f15e151ab7f5b0aed5b7a95485e26b7193e43cd8
[idea/community.git] / platform / lang-impl / src / com / intellij / execution / ui / layout / impl / RunnerContentUi.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.execution.ui.layout.impl;
18
19 import com.intellij.execution.ui.RunnerLayoutUi;
20 import com.intellij.execution.ui.layout.*;
21 import com.intellij.execution.ui.layout.actions.RestoreViewAction;
22 import com.intellij.ide.DataManager;
23 import com.intellij.openapi.Disposable;
24 import com.intellij.openapi.actionSystem.*;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.util.ActionCallback;
27 import com.intellij.openapi.util.ActiveRunnable;
28 import com.intellij.openapi.util.Disposer;
29 import com.intellij.openapi.util.Ref;
30 import com.intellij.openapi.wm.IdeFocusManager;
31 import com.intellij.openapi.wm.IdeFrame;
32 import com.intellij.openapi.wm.ToolWindow;
33 import com.intellij.ui.UIBundle;
34 import com.intellij.ui.awt.RelativePoint;
35 import com.intellij.ui.awt.RelativeRectangle;
36 import com.intellij.ui.components.panels.NonOpaquePanel;
37 import com.intellij.ui.components.panels.Wrapper;
38 import com.intellij.ui.content.*;
39 import com.intellij.ui.docking.DockContainer;
40 import com.intellij.ui.docking.DockManager;
41 import com.intellij.ui.docking.DockableContent;
42 import com.intellij.ui.docking.DragSession;
43 import com.intellij.ui.docking.impl.DockManagerImpl;
44 import com.intellij.ui.switcher.QuickActionProvider;
45 import com.intellij.ui.switcher.SwitchProvider;
46 import com.intellij.ui.switcher.SwitchTarget;
47 import com.intellij.ui.tabs.JBTabs;
48 import com.intellij.ui.tabs.TabInfo;
49 import com.intellij.ui.tabs.TabsListener;
50 import com.intellij.util.containers.ContainerUtil;
51 import com.intellij.util.ui.AbstractLayoutManager;
52 import com.intellij.util.ui.UIUtil;
53 import org.jetbrains.annotations.NonNls;
54 import org.jetbrains.annotations.NotNull;
55 import org.jetbrains.annotations.Nullable;
56
57 import javax.swing.*;
58 import javax.swing.border.EmptyBorder;
59 import java.awt.*;
60 import java.awt.event.MouseEvent;
61 import java.beans.PropertyChangeEvent;
62 import java.beans.PropertyChangeListener;
63 import java.util.*;
64 import java.util.List;
65 import java.util.concurrent.CopyOnWriteArraySet;
66
67 public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Facade, ViewContextEx, PropertyChangeListener, SwitchProvider,
68                                         QuickActionProvider, DockContainer.Dialog {
69
70   @NonNls public static final String LAYOUT = "Runner.Layout";
71   @NonNls public static final String VIEW_POPUP = "Runner.View.Popup";
72   @NonNls public static final String VIEW_TOOLBAR = "Runner.View.Toolbar";
73
74   ContentManager myManager;
75   RunnerLayout myLayoutSettings;
76
77   ActionManager myActionManager;
78   String mySessionName;
79   MyComponent myComponent = new MyComponent();
80
81   private final Wrapper myToolbar = new Wrapper();
82   final MyDragOutDelegate myDragOutDelegate = new MyDragOutDelegate();
83
84   JBRunnerTabs myTabs;
85   private final Comparator<TabInfo> myTabsComparator = new Comparator<TabInfo>() {
86     public int compare(final TabInfo o1, final TabInfo o2) {
87       //noinspection ConstantConditions
88       return getTabFor(o1).getIndex() - getTabFor(o2).getIndex();
89     }
90   };
91   Project myProject;
92
93   ActionGroup myTopActions = new DefaultActionGroup();
94
95   DefaultActionGroup myMinimizedViewActions = new DefaultActionGroup();
96
97   Map<GridImpl, Wrapper> myMinimizedButtonsPlaceholder = new HashMap<GridImpl, Wrapper>();
98   Map<GridImpl, Wrapper> myCommonActionsPlaceholder = new HashMap<GridImpl, Wrapper>();
99   Map<GridImpl, AnAction[]> myContextActions = new HashMap<GridImpl, AnAction[]>();
100
101   boolean myUiLastStateWasRestored;
102
103   private final Set<Object> myRestoreStateRequestors = new HashSet<Object>();
104   public static final DataKey<RunnerContentUi> KEY = DataKey.create("DebuggerContentUI");
105   private String myActionsPlace = ActionPlaces.UNKNOWN;
106   private final IdeFocusManager myFocusManager;
107
108   private boolean myMinimizeActionEnabled = true;
109   private boolean myMoveToGridActionEnabled = true;
110   private final RunnerLayoutUi myRunnerUi;
111
112   private final Map<String, LayoutAttractionPolicy> myAttractions = new HashMap<String, LayoutAttractionPolicy>();
113   private final Map<String, LayoutAttractionPolicy> myConditionAttractions = new HashMap<String, LayoutAttractionPolicy>();
114
115   private ActionGroup myAdditonalFocusActions;
116
117   private final ActionCallback myInitialized = new ActionCallback();
118   private boolean myToDisposeRemovedContent = true;
119
120   private int myAttractionCount;
121   private ActionGroup myLeftToolbarActions;
122
123   private JBTabs myCurrentOver;
124   private Image myCurrentOverImg;
125   private TabInfo myCurrentOverInfo;
126   private RunnerContentUi myOriginal;
127   private CopyOnWriteArraySet<Listener> myDockingListeners = new CopyOnWriteArraySet<Listener>();
128   private Set<RunnerContentUi> myChildren = new TreeSet<RunnerContentUi>(new Comparator<RunnerContentUi>() {
129     @Override
130     public int compare(RunnerContentUi o1, RunnerContentUi o2) {
131       return o1.myWindow - o2.myWindow;
132     }
133   }); 
134   private int myWindow;
135   private boolean myDisposing;
136
137   public RunnerContentUi(Project project,
138                          RunnerLayoutUi ui,
139                          ActionManager actionManager,
140                          IdeFocusManager focusManager,
141                          RunnerLayout settings,
142                          String sessionName) {
143     myProject = project;
144     myRunnerUi = ui;
145     myLayoutSettings = settings;
146     myActionManager = actionManager;
147     mySessionName = sessionName;
148     myFocusManager = focusManager;
149   }
150
151   public RunnerContentUi(RunnerContentUi ui, RunnerContentUi original, int window) {
152     this(ui.myProject, ui.myRunnerUi, ui.myActionManager, ui.myFocusManager, ui.myLayoutSettings, ui.mySessionName);
153     myOriginal = original;
154     original.myChildren.add(this);
155     myWindow = window == 0 ? original.findFreeWindow() : window;
156   }
157
158   public void setTopActions(@NotNull final ActionGroup topActions, @NotNull String place) {
159     myTopActions = topActions;
160     myActionsPlace = place;
161
162     rebuildCommonActions();
163   }
164
165   public void setAdditionalFocusActions(final ActionGroup group) {
166     myAdditonalFocusActions = group;
167     rebuildTabPopup();
168   }
169
170   public void setLeftToolbar(ActionGroup group, String place) {
171     final ActionToolbar tb = myActionManager.createActionToolbar(place, group, false);
172     tb.setTargetComponent(myComponent);
173     myToolbar.setContent(tb.getComponent());
174     myLeftToolbarActions = group;
175
176     myComponent.revalidate();
177     myComponent.repaint();
178   }
179
180   public void initUi() {
181     if (myTabs != null) return;
182
183     myTabs = (JBRunnerTabs)new JBRunnerTabs(myProject, myActionManager, myFocusManager, this).setDataProvider(new DataProvider() {
184       public Object getData(@NonNls final String dataId) {
185         if (ViewContext.CONTENT_KEY.is(dataId)) {
186           TabInfo info = myTabs.getTargetInfo();
187           if (info != null) {
188             return getGridFor(info).getData(dataId);
189           }
190         }
191         else if (ViewContext.CONTEXT_KEY.is(dataId)) {
192           return RunnerContentUi.this;
193         }
194         return null;
195       }
196     }).setTabLabelActionsAutoHide(false).setProvideSwitchTargets(false).setInnerInsets(new Insets(0, 0, 0, 0))
197       .setToDrawBorderIfTabsHidden(false).setTabDraggingEnabled(isMoveToGridActionEnabled()).setUiDecorator(null).getJBTabs();
198     rebuildTabPopup();
199
200     myTabs.getPresentation().setPaintBorder(0, 0, 0, 0).setPaintFocus(false)
201       .setRequestFocusOnLastFocusedComponent(true);
202     myTabs.getComponent().setBackground(myToolbar.getBackground());
203     myTabs.getComponent().setBorder(new EmptyBorder(0, 2, 0, 0));
204
205     final NonOpaquePanel wrappper = new NonOpaquePanel(new BorderLayout(0, 0));
206     wrappper.add(myToolbar, BorderLayout.WEST);
207     wrappper.add(myTabs.getComponent(), BorderLayout.CENTER);
208
209     myComponent.setContent(wrappper);
210
211     myTabs.addListener(new TabsListener() {
212
213       public void beforeSelectionChanged(TabInfo oldSelection, TabInfo newSelection) {
214         if (oldSelection != null && !isStateBeingRestored()) {
215           final GridImpl grid = getGridFor(oldSelection);
216           if (grid != null) {
217             grid.saveUiState();
218           }
219         }
220       }
221
222       @Override
223       public void tabsMoved() {
224         saveUiState();
225       }
226
227       public void selectionChanged(final TabInfo oldSelection, final TabInfo newSelection) {
228         if (!myTabs.getComponent().isShowing()) return;
229
230         if (newSelection != null) {
231           newSelection.stopAlerting();
232           getGridFor(newSelection).processAddToUi(false);
233         }
234
235         if (oldSelection != null) {
236           getGridFor(oldSelection).processRemoveFromUi();
237         }
238       }
239     });
240
241     if (myOriginal != null) {
242       final ContentManager manager = ContentFactory.SERVICE.getInstance().createContentManager(this, false, myProject);
243       Disposer.register((Disposable)myRunnerUi, manager);
244       manager.getComponent();
245     } else {
246       DockManager.getInstance(myProject).register(this);
247     }
248   }
249
250   private void rebuildTabPopup() {
251     myTabs.setPopupGroup(getCellPopupGroup(TAB_POPUP_PLACE), TAB_POPUP_PLACE, true);
252
253     final ArrayList<GridImpl> grids = getGrids();
254     for (GridImpl each : grids) {
255       each.rebuildTabPopup();
256     }
257   }
258
259   public ActionGroup getCellPopupGroup(final String place) {
260     final ActionGroup original = (ActionGroup)myActionManager.getAction(VIEW_POPUP);
261     final ActionGroup focusPlaceholder = (ActionGroup)myActionManager.getAction("Runner.Focus");
262
263     DefaultActionGroup group = new DefaultActionGroup(VIEW_POPUP, original.isPopup());
264
265     final AnActionEvent event = new AnActionEvent(null, DataManager.getInstance().getDataContext(), place, new Presentation(),
266                                                   ActionManager.getInstance(), 0);
267     final AnAction[] originalActions = original.getChildren(event);
268
269
270     for (final AnAction each : originalActions) {
271       if (each == focusPlaceholder) {
272         final AnAction[] focusActions = ((ActionGroup)each).getChildren(event);
273         for (AnAction eachFocus : focusActions) {
274           group.add(eachFocus);
275         }
276         if (myAdditonalFocusActions != null) {
277           final AnAction[] addins = myAdditonalFocusActions.getChildren(event);
278           for (AnAction eachAddin : addins) {
279             group.add(eachAddin);
280           }
281         }
282       }
283       else {
284         group.add(each);
285       }
286     }
287     return group;
288   }
289
290   @Override
291   public boolean isOriginal() {
292     return myOriginal == null;
293   }
294
295   @Override
296   public int getWindow() {
297     return myWindow;
298   }
299
300   public void propertyChange(final PropertyChangeEvent evt) {
301     Content content = (Content)evt.getSource();
302     final GridImpl grid = getGridFor(content, false);
303     if (grid == null) return;
304
305     final GridCellImpl cell = grid.findCell(content);
306     if (cell == null) return;
307
308     final String property = evt.getPropertyName();
309     if (Content.PROP_ALERT.equals(property)) {
310       attract(content, true);
311     }
312     else if (Content.PROP_DISPLAY_NAME.equals(property)
313              || Content.PROP_ICON.equals(property)
314              || Content.PROP_ACTIONS.equals(property)
315              || Content.PROP_DESCRIPTION.equals(property)) {
316       cell.updateTabPresentation(content);
317       updateTabsUI(false);
318     }
319   }
320
321
322   public void processBounce(Content content, final boolean activate) {
323     final GridImpl grid = getGridFor(content, false);
324     if (grid == null) return;
325
326     final GridCellImpl cell = grid.findCell(content);
327     if (cell == null) return;
328
329
330     final TabInfo tab = myTabs.findInfo(grid);
331     if (tab == null) return;
332
333
334     if (getSelectedGrid() != grid) {
335       tab.setAlertIcon(content.getAlertIcon());
336       if (activate) {
337         tab.fireAlert();
338       }
339       else {
340         tab.stopAlerting();
341       }
342     }
343     else {
344       grid.processAlert(content, activate);
345     }
346   }
347
348   @Override
349   public ActionCallback detachTo(int window, GridCell cell) {
350     if (myOriginal != null) {
351       return myOriginal.detachTo(window, cell);
352     }
353     RunnerContentUi target = null;
354     if (window > 0) {
355       for (RunnerContentUi child : myChildren) {
356         if (child.myWindow == window) {
357           target = child;
358           break;
359         }
360       }
361     }
362     final GridCellImpl gridCell = (GridCellImpl)cell;
363     final Content[] contents = gridCell.getContents();
364     storeDefaultIndices(contents);
365     for (Content content : contents) {
366       content.putUserData(RunnerLayout.DROP_INDEX, getStateFor(content).getTab().getIndex());
367     }
368     Dimension size = gridCell.getSize();
369     if (size == null) {
370       size = new Dimension(200, 200);
371     }
372     final DockableGrid content = new DockableGrid(null, null, size, Arrays.asList(contents), window);
373     if (target != null) {
374       target.add(content, null);
375     } else {
376       final Point location = gridCell.getLocation();
377       location.translate(size.width / 2, size.height / 2);
378       getDockManager().createNewDockContainerFor(content, new RelativePoint(location));
379     }
380     return new ActionCallback.Done();
381   }
382
383   private void storeDefaultIndices(Content[] contents) {
384     for (Content content : contents) {
385       content.putUserData(RunnerLayout.DEFAULT_INDEX, getStateFor(content).getTab().getDefaultIndex());
386     }
387   }
388
389   @Override
390   public RelativeRectangle getAcceptArea() {
391     return new RelativeRectangle(myTabs.getComponent());
392   }
393
394   @Override
395   public boolean canAccept(DockableContent content, RelativePoint point) {
396     if (!(content instanceof DockableGrid)) {
397       return false;
398     }
399     final RunnerContentUi ui = ((DockableGrid)content).getOriginalRunnerUi();
400     return ui.getProject() == myProject && ui.mySessionName.equals(mySessionName);
401   }
402
403   @Override
404   public JComponent getComponent() {
405     initUi();
406     return myComponent;
407   }
408
409   @Override
410   public JComponent getContainerComponent() {
411     initUi();
412     return myManager.getComponent();
413   }
414
415   @Override
416   public void add(DockableContent dockable, RelativePoint dropTarget) {
417     saveUiState();
418
419     final DockableGrid dockableGrid = (DockableGrid)dockable;
420     final List<Content> contents = dockableGrid.getContents();
421     final boolean wasRestoring = myOriginal != null && myOriginal.isStateBeingRestored();
422     setStateIsBeingRestored(true, this);
423     try {
424       final Point point = dropTarget != null ? dropTarget.getPoint(myComponent) : null;
425       boolean hadGrid = !myTabs.shouldAddToGlobal(point);
426
427       for (Content content : contents) {
428         dockableGrid.getRunnerUi().myManager.removeContent(content, false);
429         myManager.removeContent(content, false);
430         if (hadGrid && contents.size() == 1 && !wasRestoring) {
431           getStateFor(content).assignTab(getTabFor(getSelectedGrid()));
432           getStateFor(content).setPlaceInGrid(myLayoutSettings.getDefaultGridPlace(content));
433         } else if (contents.size() == 1 && !wasRestoring) {
434           getStateFor(content).assignTab(myLayoutSettings.createNewTab());
435           getStateFor(content).setPlaceInGrid(myLayoutSettings.getDefaultGridPlace(content));
436         }
437         getStateFor(content).setWindow(myWindow);
438         myManager.addContent(content);
439       }
440     } finally {
441       setStateIsBeingRestored(false, this);
442     }
443
444     saveUiState();
445
446     updateTabsUI(true);
447   }
448
449   @Override
450   public void closeAll() {
451     final Content[] contents = myManager.getContents();
452     for (Content content : contents) {
453       getStateFor(content).setWindow(0);
454     }
455     myManager.removeAllContents(false);
456     for (Content content : contents) {
457       myOriginal.myManager.addContent(content);
458       myOriginal.findCellFor(content).minimize(content);
459     }
460   }
461
462   @Override
463   public void addListener(final Listener listener, Disposable parent) {
464     myDockingListeners.add(listener);
465     Disposer.register(parent, new Disposable() {
466       @Override
467       public void dispose() {
468         myDockingListeners.remove(listener);
469       }
470     });
471   }
472
473   @Override
474   public boolean isEmpty() {
475     return myTabs.isEmptyVisible() || myDisposing;
476   }
477
478   @Override
479   public Image startDropOver(DockableContent content, RelativePoint point) {
480     return null;
481   }
482
483   @Override
484   public Image processDropOver(DockableContent content, RelativePoint point) {
485     JBTabs current = getTabsAt(content, point);
486
487     if (myCurrentOver != null && myCurrentOver != current) {
488       resetDropOver(content);
489     }
490
491     if (myCurrentOver == null && current != null) {
492       myCurrentOver = current;
493       Presentation presentation = content.getPresentation();
494       myCurrentOverInfo = new TabInfo(new JLabel("")).setText(presentation.getText()).setIcon(presentation.getIcon());
495       myCurrentOverImg = myCurrentOver.startDropOver(myCurrentOverInfo, point);
496     }
497
498     if (myCurrentOver != null) {
499       myCurrentOver.processDropOver(myCurrentOverInfo, point);
500     }
501
502     return myCurrentOverImg;
503   }
504
505   @Nullable
506   private JBTabs getTabsAt(DockableContent content, RelativePoint point) {
507     if (content instanceof DockableGrid) {
508       final Point p = point.getPoint(getComponent());
509       Component c = SwingUtilities.getDeepestComponentAt(getComponent(), p.x, p.y);
510       while (c != null) {
511         if (c instanceof JBTabs) {
512           return (JBTabs)c;
513         }
514         c = c.getParent();
515       }
516     }
517     return null;
518   }
519
520   @Override
521   public void resetDropOver(DockableContent content) {
522     if (myCurrentOver != null) {
523       myCurrentOver.resetDropOver(myCurrentOverInfo);
524       myCurrentOver = null;
525       myCurrentOverInfo = null;
526       myCurrentOverImg = null;
527     }
528   }
529
530   @Override
531   public boolean isDisposeWhenEmpty() {
532     return myOriginal != null;
533   }
534
535   public boolean isCycleRoot() {
536     return false;
537   }
538
539   public void setManager(final ContentManager manager) {
540     assert myManager == null;
541
542     myManager = manager;
543     myManager.addContentManagerListener(new ContentManagerListener() {
544       public void contentAdded(final ContentManagerEvent event) {
545         final GridImpl grid = getGridFor(event.getContent(), true);
546
547         if (grid == null) {
548           return;
549         }
550
551         grid.add(event.getContent());
552
553         if (getSelectedGrid() == grid) {
554           grid.processAddToUi(false);
555         }
556
557         if (myManager.getComponent().isShowing()) {
558           grid.restoreLastUiState();
559         }
560
561         updateTabsUI(false);
562
563
564         event.getContent().addPropertyChangeListener(RunnerContentUi.this);
565         fireContentOpened(event.getContent());
566       }
567
568       public void contentRemoved(final ContentManagerEvent event) {
569         event.getContent().removePropertyChangeListener(RunnerContentUi.this);
570
571         GridImpl grid = (GridImpl)findGridFor(event.getContent());
572         if (grid != null) {
573           grid.remove(event.getContent());
574           grid.processRemoveFromUi();
575           removeGridIfNeeded(grid);
576         }
577         updateTabsUI(false);
578         fireContentClosed(event.getContent());
579       }
580
581       public void contentRemoveQuery(final ContentManagerEvent event) {
582       }
583
584       public void selectionChanged(final ContentManagerEvent event) {
585         if (isStateBeingRestored()) return;
586
587         if (event.getOperation() == ContentManagerEvent.ContentOperation.add) {
588           select(event.getContent(), false);
589         }
590       }
591     });
592   }
593
594   @Nullable
595   private GridImpl getSelectedGrid() {
596     TabInfo selection = myTabs.getSelectedInfo();
597     return selection != null ? getGridFor(selection) : null;
598   }
599
600   private void removeGridIfNeeded(GridImpl grid) {
601     if (grid.isEmpty()) {
602       myTabs.removeTab(myTabs.findInfo(grid));
603       myMinimizedButtonsPlaceholder.remove(grid);
604       myCommonActionsPlaceholder.remove(grid);
605       Disposer.dispose(grid);
606     }
607   }
608
609   @Nullable
610   private GridImpl getGridFor(Content content, boolean createIfMissing) {
611     GridImpl grid = (GridImpl)findGridFor(content);
612     if (grid != null || !createIfMissing) return grid;
613
614     grid = new GridImpl(this, mySessionName);
615
616     if (myCurrentOver != null || myOriginal != null) {
617       Integer forcedDropIndex = content.getUserData(RunnerLayout.DROP_INDEX);
618       final int index = myTabs.getDropInfoIndex() + (myOriginal != null ? myOriginal.getTabOffsetFor(this) : 0);
619       final TabImpl tab = myLayoutSettings.getOrCreateTab(-1);
620       final Integer defaultIndex = content.getUserData(RunnerLayout.DEFAULT_INDEX);
621       tab.setDefaultIndex(defaultIndex != null ? defaultIndex : -1);
622       tab.setIndex(forcedDropIndex != null ? forcedDropIndex : index);
623       getStateFor(content).assignTab(tab);
624     }
625     
626     TabInfo tab = new TabInfo(grid).setObject(getStateFor(content).getTab()).setText("Tab");
627
628
629     Wrapper left = new Wrapper();
630     myCommonActionsPlaceholder.put(grid, left);
631
632
633     Wrapper minimizedToolbar = new Wrapper();
634     myMinimizedButtonsPlaceholder.put(grid, minimizedToolbar);
635
636
637     final Wrapper searchComponent = new Wrapper();
638     if (content.getSearchComponent() != null) {
639       searchComponent.setContent(content.getSearchComponent());
640     }
641
642     TwoSideComponent right = new TwoSideComponent(searchComponent, minimizedToolbar);
643
644
645     NonOpaquePanel sideComponent = new TwoSideComponent(left, right);
646
647     tab.setSideComponent(sideComponent);
648
649     tab.setTabLabelActions((ActionGroup)myActionManager.getAction(VIEW_TOOLBAR), TAB_TOOLBAR_PLACE);
650
651     myTabs.addTab(tab);
652     myTabs.sortTabs(myTabsComparator);
653
654     return grid;
655   }
656
657   private int getTabOffsetFor(RunnerContentUi ui) {
658     int offset = myTabs.getTabCount();
659     for (RunnerContentUi child : myChildren) {
660       if (child == ui) break;
661       offset += child.myTabs.getTabCount();
662     }
663     return offset;
664   }
665
666   @Nullable
667   public GridCell findCellFor(final Content content) {
668     GridImpl cell = getGridFor(content, false);
669     return cell != null ? cell.getCellFor(content) : null;
670   }
671
672   private boolean rebuildToolbar() {
673     boolean hasToolbarContent = rebuildCommonActions();
674     hasToolbarContent |= rebuildMinimizedActions();
675     return hasToolbarContent;
676   }
677
678   private boolean rebuildCommonActions() {
679     boolean hasToolbarContent = false;
680     for (Map.Entry<GridImpl, Wrapper> entry : myCommonActionsPlaceholder.entrySet()) {
681       Wrapper eachPlaceholder = entry.getValue();
682       List<Content> contentList = entry.getKey().getContents();
683
684       Set<Content> contents = new HashSet<Content>();
685       contents.addAll(contentList);
686
687       DefaultActionGroup groupToBuild;
688       JComponent contextComponent = null;
689       if (isHorizontalToolbar() && contents.size() == 1) {
690         Content content = contentList.get(0);
691         groupToBuild = new DefaultActionGroup();
692         if (content.getActions() != null) {
693           groupToBuild.addAll(content.getActions());
694           groupToBuild.addSeparator();
695           contextComponent = content.getActionsContextComponent();
696         }
697         groupToBuild.addAll(myTopActions);
698       }
699       else {
700         final DefaultActionGroup group = new DefaultActionGroup();
701         group.addAll(myTopActions);
702         groupToBuild = group;
703       }
704
705       final AnAction[] actions = groupToBuild.getChildren(null);
706       if (!Arrays.equals(actions, myContextActions.get(entry.getKey()))) {
707         ActionToolbar tb = myActionManager.createActionToolbar(myActionsPlace, groupToBuild, true);
708         tb.getComponent().setBorder(null);
709         tb.setTargetComponent(contextComponent);
710         eachPlaceholder.setContent(tb.getComponent());
711       }
712
713       if (groupToBuild.getChildrenCount() > 0) {
714         hasToolbarContent = true;
715       }
716
717       myContextActions.put(entry.getKey(), actions);
718     }
719
720     return hasToolbarContent;
721   }
722
723   private boolean rebuildMinimizedActions() {
724     for (Map.Entry<GridImpl, Wrapper> entry : myMinimizedButtonsPlaceholder.entrySet()) {
725       Wrapper eachPlaceholder = entry.getValue();
726       ActionToolbar tb = myActionManager.createActionToolbar(ActionPlaces.DEBUGGER_TOOLBAR, myMinimizedViewActions, true);
727       tb.getComponent().setBorder(null);
728       tb.setReservePlaceAutoPopupIcon(false);
729       JComponent minimized = tb.getComponent();
730       eachPlaceholder.setContent(minimized);
731     }
732
733     myTabs.getComponent().revalidate();
734     myTabs.getComponent().repaint();
735
736     return myMinimizedViewActions.getChildrenCount() > 0;
737   }
738
739   private void updateTabsUI(final boolean validateNow) {
740     boolean hasToolbarContent = rebuildToolbar();
741
742     List<TabInfo> tabs = myTabs.getTabs();
743     for (TabInfo each : tabs) {
744       hasToolbarContent |= updateTabUI(each);
745     }
746     int tabsCount = tabs.size();
747     for (RunnerContentUi child : myChildren) {
748       tabsCount += child.myTabs.getTabCount();
749     }
750     myTabs.getPresentation().setHideTabs(!hasToolbarContent && tabsCount <= 1 && myOriginal == null);
751     myTabs.updateTabActions(validateNow);
752
753     if (validateNow) {
754       myTabs.sortTabs(myTabsComparator);
755     }
756   }
757
758   private boolean updateTabUI(TabInfo tab) {
759     TabImpl t = getTabFor(tab);
760     if (t == null) {
761       return false;
762     }
763
764     String title = t.getDisplayName();
765     Icon icon = t.getIcon();
766
767     GridImpl grid = getGridFor(tab);
768     boolean hasToolbarContent = grid.updateGridUI();
769
770     List<Content> contents = grid.getContents();
771     if (title == null) {
772       title = "";
773       Iterator<Content> all = contents.iterator();
774       while (all.hasNext()) {
775         Content each = all.next();
776         title += each.getTabName();
777         if (all.hasNext()) {
778           title += " | ";
779         }
780       }
781     }
782
783     if (icon == null && contents.size() == 1) {
784       tab.setHidden(grid.isMinimized(contents.get(0)));
785       icon = contents.get(0).getIcon();
786     }
787
788     tab.setDragOutDelegate(myTabs.getTabs().size() > 1 || !isOriginal() ? myDragOutDelegate : null);
789
790     Tab gridTab = grid.getTab();
791     tab.setText(title).setIcon(gridTab != null && gridTab.isDefault() ? null : icon);
792
793     return hasToolbarContent;
794   }
795
796   private ActionCallback restoreLastUiState() {
797     if (isStateBeingRestored()) return new ActionCallback.Rejected();
798
799     try {
800       setStateIsBeingRestored(true, this);
801
802       List<TabInfo> tabs = new ArrayList<TabInfo>();
803       tabs.addAll(myTabs.getTabs());
804
805       final ActionCallback result = new ActionCallback(tabs.size());
806
807       for (TabInfo each : tabs) {
808         getGridFor(each).restoreLastUiState().notifyWhenDone(result);
809       }
810
811       return result;
812     }
813     finally {
814       setStateIsBeingRestored(false, this);
815     }
816   }
817
818   public void saveUiState() {
819     if (isStateBeingRestored()) return;
820
821     if (myOriginal != null) {
822       myOriginal.saveUiState();
823       return;
824     }
825     int offset = updateTabsIndices(myTabs, 0);
826     for (RunnerContentUi child : myChildren) {
827       offset = updateTabsIndices(child.myTabs, offset);
828     }
829
830     doSaveUiState();
831   }
832
833   private static int updateTabsIndices(final JBRunnerTabs tabs, int offset) {
834     for (TabInfo each : tabs.getTabs()) {
835       final int index = tabs.getIndexOf(each);
836       final TabImpl tab = getTabFor(each);
837       if (tab != null) tab.setIndex(index + offset);
838     }
839     return offset + tabs.getTabCount();
840   }
841
842   private void doSaveUiState() {
843     if (isStateBeingRestored()) return;
844
845     for (TabInfo each : myTabs.getTabs()) {
846       GridImpl eachGrid = getGridFor(each);
847       eachGrid.saveUiState();
848     }
849
850     for (RunnerContentUi child : myChildren) {
851       child.doSaveUiState();
852     }
853   }
854
855   @Nullable
856   public Tab getTabFor(final Grid grid) {
857     TabInfo info = myTabs.findInfo((Component)grid);
858     return getTabFor(info);
859   }
860
861   @Override
862   public void showNotify() {
863     final Window window = SwingUtilities.getWindowAncestor(myComponent);
864     if (window instanceof IdeFrame.Child) {
865       ((IdeFrame.Child)window).setFrameTitle(mySessionName);
866     }
867   }
868
869   @Override
870   public void hideNotify() {}
871
872   @Nullable
873   private static TabImpl getTabFor(@Nullable final TabInfo tab) {
874     if (tab == null) {
875       return null;
876     }
877     return (TabImpl)tab.getObject();
878   }
879
880   private static GridImpl getGridFor(TabInfo tab) {
881     return (GridImpl)tab.getComponent();
882   }
883
884   @Nullable
885   public Grid findGridFor(Content content) {
886     TabImpl tab = (TabImpl)getStateFor(content).getTab();
887     for (TabInfo each : myTabs.getTabs()) {
888       TabImpl t = getTabFor(each);
889       if (t != null && t.equals(tab)) return getGridFor(each);
890     }
891
892     return null;
893   }
894
895   private ArrayList<GridImpl> getGrids() {
896     ArrayList<GridImpl> result = new ArrayList<GridImpl>();
897     for (TabInfo each : myTabs.getTabs()) {
898       result.add(getGridFor(each));
899     }
900     return result;
901   }
902
903
904   public void setHorizontalToolbar(final boolean state) {
905     myLayoutSettings.setToolbarHorizontal(state);
906     for (GridImpl each : getGrids()) {
907       each.setToolbarHorizontal(state);
908     }
909
910     myContextActions.clear();
911     updateTabsUI(false);
912   }
913
914
915   public boolean isSingleSelection() {
916     return false;
917   }
918
919   public boolean isToSelectAddedContent() {
920     return false;
921   }
922
923   public boolean canBeEmptySelection() {
924     return true;
925   }
926
927   public void beforeDispose() {
928     if (myComponent.getRootPane() != null) {
929       saveUiState();
930     }
931     if (myOriginal != null) {
932       myDisposing = true;
933       fireContentClosed(null);
934     }
935   }
936
937   public boolean canChangeSelectionTo(Content content, boolean implicit) {
938     if (implicit) {
939       GridImpl grid = getGridFor(content, false);
940       if (grid != null) {
941         return !grid.isMinimized(content);
942       }
943     }
944
945     return true;
946   }
947
948   @Override
949   public String getCloseActionName() {
950     return UIBundle.message("tabbed.pane.close.tab.action.name");
951   }
952
953   @Override
954   public String getCloseAllButThisActionName() {
955     return UIBundle.message("tabbed.pane.close.all.tabs.but.this.action.name");
956   }
957
958   @Override
959   public String getPreviousContentActionName() {
960     return "Select Previous Tab";
961   }
962
963   @Override
964   public String getNextContentActionName() {
965     return "Select Next Tab";
966   }
967
968   public void dispose() {
969     if (myOriginal != null) {
970       myOriginal.myChildren.remove(this);
971     }
972   }
973
974   public void restoreLayout() {
975     final RunnerContentUi[] children = myChildren.toArray(new RunnerContentUi[myChildren.size()]);
976     final List<Content> contents = new ArrayList<Content>();
977     Collections.addAll(contents, myManager.getContents());
978     for (RunnerContentUi child : children) {
979       Collections.addAll(contents, child.myManager.getContents());
980     }
981     Content[] all = contents.toArray(new Content[contents.size()]);
982
983     setStateIsBeingRestored(true, this);
984     try {
985       for (RunnerContentUi child : children) {
986         child.myManager.removeAllContents(false);
987       }
988       myManager.removeAllContents(false);
989       myMinimizedViewActions.removeAll();
990     }
991     finally {
992       setStateIsBeingRestored(false, this);
993     }
994
995     myLayoutSettings.resetToDefault();
996     for (Content each : all) {
997       myManager.addContent(each);
998     }
999     restoreLastUiState();
1000
1001     updateTabsUI(true);
1002   }
1003
1004   public boolean isStateBeingRestored() {
1005     return !myRestoreStateRequestors.isEmpty();
1006   }
1007
1008   public void setStateIsBeingRestored(final boolean restoredNow, final Object requestor) {
1009     if (restoredNow) {
1010       myRestoreStateRequestors.add(requestor);
1011     }
1012     else {
1013       myRestoreStateRequestors.remove(requestor);
1014     }
1015   }
1016
1017   public ActionGroup getLayoutActions() {
1018     return (ActionGroup)myActionManager.getAction(LAYOUT);
1019   }
1020
1021   public void updateActionsImmediately() {
1022     if (myToolbar.getTargetComponent() instanceof ActionToolbar) {
1023       ((ActionToolbar)myToolbar.getTargetComponent()).updateActionsImmediately();
1024     }
1025   }
1026
1027   public void setMinimizeActionEnabled(final boolean enabled) {
1028     myMinimizeActionEnabled = enabled;
1029   }
1030
1031   public void setMovetoGridActionEnabled(final boolean enabled) {
1032     myMoveToGridActionEnabled = enabled;
1033   }
1034
1035   public boolean isMinimizeActionEnabled() {
1036     return myMinimizeActionEnabled && myOriginal == null;
1037   }
1038
1039   public boolean isMoveToGridActionEnabled() {
1040     return myMoveToGridActionEnabled;
1041   }
1042
1043   public void setPolicy(String contentId, final LayoutAttractionPolicy policy) {
1044     myAttractions.put(contentId, policy);
1045   }
1046
1047   public void setConditionPolicy(final String condition, final LayoutAttractionPolicy policy) {
1048     myConditionAttractions.put(condition, policy);
1049   }
1050
1051   private static LayoutAttractionPolicy getOrCreatePolicyFor(String key,
1052                                                              Map<String, LayoutAttractionPolicy> map,
1053                                                              LayoutAttractionPolicy defaultPolicy) {
1054     LayoutAttractionPolicy policy = map.get(key);
1055     if (policy == null) {
1056       policy = defaultPolicy;
1057       map.put(key, policy);
1058     }
1059     return policy;
1060   }
1061
1062   @Nullable
1063   public Content findContent(final String key) {
1064     final ContentManager manager = getContentManager();
1065     if (manager == null || key == null) return null;
1066
1067     Content[] contents = manager.getContents();
1068     for (Content content : contents) {
1069       String kind = content.getUserData(ViewImpl.ID);
1070       if (key.equals(kind)) {
1071         return content;
1072       }
1073     }
1074
1075     return null;
1076   }
1077
1078   public void setToDisposeRemovedContent(final boolean toDispose) {
1079     myToDisposeRemovedContent = toDispose;
1080   }
1081
1082   public boolean isToDisposeRemovedContent() {
1083     return myToDisposeRemovedContent;
1084   }
1085
1086   private class MyComponent extends Wrapper.FocusHolder implements DataProvider, QuickActionProvider {
1087
1088     private boolean myWasEverAdded;
1089
1090     public MyComponent() {
1091       setOpaque(true);
1092       setFocusCycleRoot(true);
1093       setBorder(new ToolWindow.Border(true, false, true, true));
1094     }
1095
1096     @Nullable
1097     public Object getData(@NonNls final String dataId) {
1098       if (KEY.is(dataId)) {
1099         return RunnerContentUi.this;
1100       }
1101       else {
1102         return null;
1103       }
1104     }
1105
1106     @Override
1107     public String getName() {
1108       return RunnerContentUi.this.getName();
1109     }
1110
1111     public List<AnAction> getActions(boolean originalProvider) {
1112       return RunnerContentUi.this.getActions(originalProvider);
1113     }
1114
1115     public JComponent getComponent() {
1116       return RunnerContentUi.this.getComponent();
1117     }
1118
1119     public boolean isCycleRoot() {
1120       return RunnerContentUi.this.isCycleRoot();
1121     }
1122
1123     public void addNotify() {
1124       super.addNotify();
1125
1126       if (!myUiLastStateWasRestored && myOriginal == null) {
1127         myUiLastStateWasRestored = true;
1128
1129         // [kirillk] this is done later since restoreUiState doesn't work properly in the addNotify call chain
1130         //todo to investigate and to fix (may cause extra flickering)
1131         //noinspection SSBasedInspection
1132         SwingUtilities.invokeLater(new Runnable() {
1133           public void run() {
1134             restoreLastUiState().doWhenDone(new Runnable() {
1135               public void run() {
1136                 if (!myWasEverAdded) {
1137                   myWasEverAdded = true;
1138                   attractOnStartup();
1139                   myInitialized.setDone();
1140                 }
1141               }
1142             });
1143           }
1144         });
1145       }
1146     }
1147
1148     public void removeNotify() {
1149       super.removeNotify();
1150
1151       if (Disposer.isDisposed(RunnerContentUi.this)) return;
1152
1153       saveUiState();
1154     }
1155   }
1156
1157   @SuppressWarnings({"SSBasedInspection"})
1158   // [kirillk] this is done later since "startup" attractions should be done gently, only if no explicit calls are done
1159   private void attractOnStartup() {
1160     final int currentCount = myAttractionCount;
1161     SwingUtilities.invokeLater(new Runnable() {
1162       public void run() {
1163         if (currentCount < myAttractionCount) return;
1164         attractByCondition(LayoutViewOptions.STARTUP, false);
1165       }
1166     });
1167   }
1168
1169   public void attract(final Content content, boolean afterInitialized) {
1170     processAttraction(content.getUserData(ViewImpl.ID), myAttractions, new LayoutAttractionPolicy.Bounce(), afterInitialized, true);
1171   }
1172
1173   public void attractByCondition(String condition, boolean afterInitialized) {
1174     processAttraction(myLayoutSettings.getToFocus(condition), myConditionAttractions, myLayoutSettings.getAttractionPolicy(condition),
1175                       afterInitialized, true);
1176   }
1177
1178   public void clearAttractionByCondition(String condition, boolean afterInitialized) {
1179     processAttraction(myLayoutSettings.getToFocus(condition), myConditionAttractions, new LayoutAttractionPolicy.FocusOnce(),
1180                       afterInitialized, false);
1181   }
1182
1183   private void processAttraction(final String contentId,
1184                                  final Map<String, LayoutAttractionPolicy> policyMap,
1185                                  final LayoutAttractionPolicy defaultPolicy,
1186                                  final boolean afterInitialized,
1187                                  final boolean activate) {
1188     IdeFocusManager.getInstance(getProject()).doWhenFocusSettlesDown(new Runnable() {
1189       public void run() {
1190         myInitialized.processOnDone(new Runnable() {
1191           public void run() {
1192             Content content = findContent(contentId);
1193             if (content == null) return;
1194
1195             final LayoutAttractionPolicy policy = getOrCreatePolicyFor(contentId, policyMap, defaultPolicy);
1196             if (activate) {
1197               myAttractionCount++;
1198               policy.attract(content, myRunnerUi);
1199             }
1200             else {
1201               policy.clearAttraction(content, myRunnerUi);
1202             }
1203           }
1204         }, afterInitialized);
1205       }
1206     });
1207   }
1208
1209
1210   public static boolean ensureValid(JComponent c) {
1211     if (c.getRootPane() == null) return false;
1212
1213     Container eachParent = c.getParent();
1214     while (eachParent != null && eachParent.isValid()) {
1215       eachParent = eachParent.getParent();
1216     }
1217
1218     if (eachParent == null) {
1219       eachParent = c.getRootPane();
1220     }
1221
1222     eachParent.validate();
1223
1224     return true;
1225   }
1226
1227   public ContentUI getContentUI() {
1228     return this;
1229   }
1230
1231   public void minimize(final Content content, final CellTransform.Restore restore) {
1232     final Ref<AnAction> restoreAction = new Ref<AnAction>();
1233     restoreAction.set(new RestoreViewAction(content, new CellTransform.Restore() {
1234       public ActionCallback restoreInGrid() {
1235         myMinimizedViewActions.remove(restoreAction.get());
1236         return restore.restoreInGrid().doWhenDone(new Runnable() {
1237           public void run() {
1238             saveUiState();
1239             select(content, true);
1240             updateTabsUI(false);
1241           }
1242         });
1243       }
1244     }));
1245
1246     myMinimizedViewActions.add(restoreAction.get());
1247
1248     saveUiState();
1249     updateTabsUI(false);
1250   }
1251
1252   public Project getProject() {
1253     return myProject;
1254   }
1255
1256   public CellTransform.Facade getCellTransform() {
1257     return this;
1258   }
1259
1260   public ContentManager getContentManager() {
1261     return myManager;
1262   }
1263
1264   public ActionManager getActionManager() {
1265     return myActionManager;
1266   }
1267
1268   public RunnerLayout getLayoutSettings() {
1269     return myLayoutSettings;
1270   }
1271
1272   public View getStateFor(final Content content) {
1273     return myLayoutSettings.getStateFor(content);
1274   }
1275
1276   public boolean isHorizontalToolbar() {
1277     return false;
1278   }
1279
1280   public ActionCallback select(final Content content, final boolean requestFocus) {
1281     final GridImpl grid = (GridImpl)findGridFor(content);
1282     if (grid == null) return new ActionCallback.Done();
1283
1284
1285     final TabInfo info = myTabs.findInfo(grid);
1286     if (info == null) return new ActionCallback.Done();
1287
1288
1289     final ActionCallback result = new ActionCallback();
1290     myTabs.select(info, false).doWhenDone(new Runnable() {
1291       public void run() {
1292         grid.select(content, requestFocus).notifyWhenDone(result);
1293       }
1294     });
1295
1296
1297     return result;
1298   }
1299
1300   public void validate(final Content content, final ActiveRunnable toRestore) {
1301     final TabInfo current = myTabs.getSelectedInfo();
1302     myTabs.getPresentation().setPaintBlocked(true, true);
1303
1304     select(content, false).doWhenDone(new Runnable() {
1305       public void run() {
1306         myTabs.getComponent().validate();
1307         toRestore.run().doWhenDone(new Runnable() {
1308           public void run() {
1309             myTabs.select(current, true);
1310             myTabs.getPresentation().setPaintBlocked(false, true);
1311           }
1312         });
1313       }
1314     });
1315   }
1316
1317   private static class TwoSideComponent extends NonOpaquePanel {
1318     private TwoSideComponent(JComponent left, JComponent right) {
1319       setLayout(new CommonToolbarLayout(left, right));
1320       add(left);
1321       add(right);
1322     }
1323   }
1324
1325   private static class CommonToolbarLayout extends AbstractLayoutManager {
1326     private final JComponent myLeft;
1327     private final JComponent myRight;
1328
1329     public CommonToolbarLayout(final JComponent left, final JComponent right) {
1330       myLeft = left;
1331       myRight = right;
1332     }
1333
1334     public Dimension preferredLayoutSize(final Container parent) {
1335
1336       Dimension size = new Dimension();
1337       Dimension leftSize = myLeft.getPreferredSize();
1338       Dimension rightSize = myRight.getPreferredSize();
1339
1340       size.width = leftSize.width + rightSize.width;
1341       size.height = Math.max(leftSize.height, rightSize.height);
1342
1343       return size;
1344     }
1345
1346     public void layoutContainer(final Container parent) {
1347       Dimension size = parent.getSize();
1348       Dimension prefSize = parent.getPreferredSize();
1349       if (prefSize.width <= size.width) {
1350         myLeft.setBounds(0, 0, myLeft.getPreferredSize().width, parent.getHeight());
1351         Dimension rightSize = myRight.getPreferredSize();
1352         myRight.setBounds(parent.getWidth() - rightSize.width, 0, rightSize.width, parent.getHeight());
1353       }
1354       else {
1355         Dimension leftMinSize = myLeft.getMinimumSize();
1356         Dimension rightMinSize = myRight.getMinimumSize();
1357
1358         int delta = (prefSize.width - size.width) / 2;
1359
1360         myLeft.setBounds(0, 0, myLeft.getPreferredSize().width - delta, parent.getHeight());
1361         int rightX = (int)myLeft.getBounds().getMaxX();
1362         int rightWidth = size.width - rightX;
1363         if (rightWidth < rightMinSize.width) {
1364           Dimension leftSize = myLeft.getSize();
1365           int diffToRightMin = rightMinSize.width - rightWidth;
1366           if (leftSize.width - diffToRightMin >= leftMinSize.width) {
1367             leftSize.width -= diffToRightMin;
1368             myLeft.setSize(leftSize);
1369           }
1370         }
1371
1372         myRight.setBounds((int)myLeft.getBounds().getMaxX(), 0, parent.getWidth() - myLeft.getWidth(), parent.getHeight());
1373       }
1374
1375       toMakeVerticallyInCenter(myLeft, parent);
1376       toMakeVerticallyInCenter(myRight, parent);
1377     }
1378
1379     private static void toMakeVerticallyInCenter(JComponent comp, Container parent) {
1380       final Rectangle compBounds = comp.getBounds();
1381       int compHeight = comp.getPreferredSize().height;
1382       final int parentHeight = parent.getHeight();
1383       if (compHeight > parentHeight) {
1384         compHeight = parentHeight;
1385       }
1386
1387       int y = (int)Math.floor(parentHeight / 2.0 - compHeight / 2.0);
1388       comp.setBounds(compBounds.x, y, compBounds.width, compHeight);
1389     }
1390   }
1391
1392   public IdeFocusManager getFocusManager() {
1393     return myFocusManager;
1394   }
1395
1396   public RunnerLayoutUi getRunnerLayoutUi() {
1397     return myRunnerUi;
1398   }
1399
1400   public String getName() {
1401     return mySessionName;
1402   }
1403
1404   public List<AnAction> getActions(boolean originalProvider) {
1405     ArrayList<AnAction> result = new ArrayList<AnAction>();
1406     if (myLeftToolbarActions != null) {
1407       AnAction[] kids = myLeftToolbarActions.getChildren(null);
1408       ContainerUtil.addAll(result, kids);
1409     }
1410     return result;
1411   }
1412
1413   public SwitchTarget getCurrentTarget() {
1414     Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
1415     if (owner == null) return myTabs.getCurrentTarget();
1416
1417     GridImpl grid = getSelectedGrid();
1418     if (grid != null && grid.getContents().size() <= 1) return myTabs.getCurrentTarget();
1419
1420     if (grid != null) {
1421       SwitchTarget cell = grid.getCellFor(owner);
1422       return cell != null ? cell : myTabs.getCurrentTarget();
1423     }
1424     else {
1425       return myTabs.getCurrentTarget();
1426     }
1427   }
1428
1429   public List<SwitchTarget> getTargets(boolean onlyVisible, boolean originalProvider) {
1430     List<SwitchTarget> result = new ArrayList<SwitchTarget>();
1431
1432     result.addAll(myTabs.getTargets(true, false));
1433     GridImpl grid = getSelectedGrid();
1434     if (grid != null) {
1435       result.addAll(grid.getTargets(onlyVisible));
1436     }
1437
1438     for (Wrapper wrapper : myMinimizedButtonsPlaceholder.values()) {
1439       if (!wrapper.isShowing()) continue;
1440       JComponent target = wrapper.getTargetComponent();
1441       if (target instanceof ActionToolbar) {
1442         ActionToolbar tb = (ActionToolbar)target;
1443         result.addAll(tb.getTargets(onlyVisible, false));
1444       }
1445     }
1446
1447     return result;
1448   }
1449
1450
1451   private int findFreeWindow() {
1452     int i;
1453     for (i = 1; i < Integer.MAX_VALUE; i++) {
1454       if (!isUsed(i)) {
1455         return i;
1456       }
1457     }
1458     return i;
1459   }
1460
1461   private boolean isUsed(int i) {
1462     for (RunnerContentUi child : myChildren) {
1463       if (child.getWindow() == i) {
1464         return true;
1465       }
1466     }
1467     return false;
1468   }
1469
1470   private DockManagerImpl getDockManager() {
1471     return (DockManagerImpl)DockManager.getInstance(myProject);
1472   }
1473   
1474   class MyDragOutDelegate implements TabInfo.DragOutDelegate {
1475     private DragSession mySession;
1476
1477     @Override
1478     public void dragOutStarted(MouseEvent mouseEvent, TabInfo info) {
1479       final JComponent component = info.getComponent();
1480       final Content[] data = CONTENT_KEY.getData((DataProvider)component);
1481       final List<Content> contents = Arrays.asList(data);
1482
1483       storeDefaultIndices(data);
1484
1485       final Dimension size = info.getComponent().getSize();
1486       final Image image = myTabs.getComponentImage(info);
1487       if (component instanceof Grid) {
1488         info.setHidden(true);
1489       }
1490
1491       Presentation presentation = new Presentation(info.getText());
1492       presentation.setIcon(info.getIcon());
1493       mySession = getDockManager().createDragSession(mouseEvent, new DockableGrid(image, presentation,
1494                                                                                   size,
1495                                                                                   contents, 0));
1496     }
1497
1498     @Override
1499     public void processDragOut(MouseEvent event, TabInfo source) {
1500       mySession.process(event);
1501     }
1502
1503     @Override
1504     public void dragOutFinished(MouseEvent event, TabInfo source) {
1505       final Component component = event.getComponent();
1506       final IdeFrame window = UIUtil.getParentOfType(IdeFrame.class, component);
1507       if (window != null) {
1508         
1509       }
1510       mySession.process(event);
1511       mySession = null;
1512     }
1513   }
1514
1515   class DockableGrid implements DockableContent<List<Content>> {
1516     final Image myImg;
1517     private Presentation myPresentation;
1518     private final Dimension myPreferredSize;
1519     private final List<Content> myContents;
1520     private final int myWindow;
1521
1522     public DockableGrid(Image img, Presentation presentation, final Dimension size, List<Content> contents, int window) {
1523       myImg = img;
1524       myPresentation = presentation;
1525       myPreferredSize = size;
1526       myContents = contents;
1527       myWindow = window;
1528     }
1529
1530     @Override
1531     public List<Content> getKey() {
1532       return myContents;
1533     }
1534
1535     @Override
1536     public Image getPreviewImage() {
1537       return myImg;
1538     }
1539
1540     @Override
1541     public Dimension getPreferredSize() {
1542       return myPreferredSize;
1543     }
1544
1545     @Override
1546     public String getDockContainerType() {
1547       return DockableGridContainerFactory.TYPE;
1548     }
1549
1550     @Override
1551     public Presentation getPresentation() {
1552       return myPresentation;
1553     }
1554
1555     public RunnerContentUi getRunnerUi() {
1556       return RunnerContentUi.this;
1557     }
1558
1559     public RunnerContentUi getOriginalRunnerUi() {
1560       return myOriginal != null ? myOriginal : RunnerContentUi.this;
1561     }
1562
1563     public List<Content> getContents() {
1564       return myContents;
1565     }
1566
1567     @Override
1568     public void close() {
1569     }
1570
1571     public int getWindow() {
1572       return myWindow;
1573     }
1574   }
1575
1576   void fireContentOpened(Content content) {
1577     for (Listener each : myDockingListeners) {
1578       each.contentAdded(content);
1579     }
1580   }
1581
1582   void fireContentClosed(Content content) {
1583     for (Listener each : myDockingListeners) {
1584       each.contentRemoved(content);
1585     }
1586   }
1587 }