IDEA-80492 Run tool window: generic UI: Restore Layout places logs view into not...
[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).setPlaceInGrid(myLayoutSettings.getDefaultGridPlace(content));
435         }
436         getStateFor(content).setWindow(myWindow);
437         myManager.addContent(content);
438       }
439     } finally {
440       setStateIsBeingRestored(false, this);
441     }
442
443     saveUiState();
444
445     updateTabsUI(true);
446   }
447
448   @Override
449   public void closeAll() {
450     final Content[] contents = myManager.getContents();
451     for (Content content : contents) {
452       getStateFor(content).setWindow(0);
453     }
454     myManager.removeAllContents(false);
455     for (Content content : contents) {
456       myOriginal.myManager.addContent(content);
457       myOriginal.findCellFor(content).minimize(content);
458     }
459   }
460
461   @Override
462   public void addListener(final Listener listener, Disposable parent) {
463     myDockingListeners.add(listener);
464     Disposer.register(parent, new Disposable() {
465       @Override
466       public void dispose() {
467         myDockingListeners.remove(listener);
468       }
469     });
470   }
471
472   @Override
473   public boolean isEmpty() {
474     return myTabs.isEmptyVisible() || myDisposing;
475   }
476
477   @Override
478   public Image startDropOver(DockableContent content, RelativePoint point) {
479     return null;
480   }
481
482   @Override
483   public Image processDropOver(DockableContent content, RelativePoint point) {
484     JBTabs current = getTabsAt(content, point);
485
486     if (myCurrentOver != null && myCurrentOver != current) {
487       resetDropOver(content);
488     }
489
490     if (myCurrentOver == null && current != null) {
491       myCurrentOver = current;
492       Presentation presentation = content.getPresentation();
493       myCurrentOverInfo = new TabInfo(new JLabel("")).setText(presentation.getText()).setIcon(presentation.getIcon());
494       myCurrentOverImg = myCurrentOver.startDropOver(myCurrentOverInfo, point);
495     }
496
497     if (myCurrentOver != null) {
498       myCurrentOver.processDropOver(myCurrentOverInfo, point);
499     }
500
501     return myCurrentOverImg;
502   }
503
504   @Nullable
505   private JBTabs getTabsAt(DockableContent content, RelativePoint point) {
506     if (content instanceof DockableGrid) {
507       final Point p = point.getPoint(getComponent());
508       Component c = SwingUtilities.getDeepestComponentAt(getComponent(), p.x, p.y);
509       while (c != null) {
510         if (c instanceof JBTabs) {
511           return (JBTabs)c;
512         }
513         c = c.getParent();
514       }
515     }
516     return null;
517   }
518
519   @Override
520   public void resetDropOver(DockableContent content) {
521     if (myCurrentOver != null) {
522       myCurrentOver.resetDropOver(myCurrentOverInfo);
523       myCurrentOver = null;
524       myCurrentOverInfo = null;
525       myCurrentOverImg = null;
526     }
527   }
528
529   @Override
530   public boolean isDisposeWhenEmpty() {
531     return myOriginal != null;
532   }
533
534   public boolean isCycleRoot() {
535     return false;
536   }
537
538   public void setManager(final ContentManager manager) {
539     assert myManager == null;
540
541     myManager = manager;
542     myManager.addContentManagerListener(new ContentManagerListener() {
543       public void contentAdded(final ContentManagerEvent event) {
544         final GridImpl grid = getGridFor(event.getContent(), true);
545
546         if (grid == null) {
547           return;
548         }
549
550         grid.add(event.getContent());
551
552         if (getSelectedGrid() == grid) {
553           grid.processAddToUi(false);
554         }
555
556         if (myManager.getComponent().isShowing()) {
557           grid.restoreLastUiState();
558         }
559
560         updateTabsUI(false);
561
562
563         event.getContent().addPropertyChangeListener(RunnerContentUi.this);
564         fireContentOpened(event.getContent());
565       }
566
567       public void contentRemoved(final ContentManagerEvent event) {
568         event.getContent().removePropertyChangeListener(RunnerContentUi.this);
569
570         GridImpl grid = (GridImpl)findGridFor(event.getContent());
571         if (grid != null) {
572           grid.remove(event.getContent());
573           grid.processRemoveFromUi();
574           removeGridIfNeeded(grid);
575         }
576         updateTabsUI(false);
577         fireContentClosed(event.getContent());
578       }
579
580       public void contentRemoveQuery(final ContentManagerEvent event) {
581       }
582
583       public void selectionChanged(final ContentManagerEvent event) {
584         if (isStateBeingRestored()) return;
585
586         if (event.getOperation() == ContentManagerEvent.ContentOperation.add) {
587           select(event.getContent(), false);
588         }
589       }
590     });
591   }
592
593   @Nullable
594   private GridImpl getSelectedGrid() {
595     TabInfo selection = myTabs.getSelectedInfo();
596     return selection != null ? getGridFor(selection) : null;
597   }
598
599   private void removeGridIfNeeded(GridImpl grid) {
600     if (grid.isEmpty()) {
601       myTabs.removeTab(myTabs.findInfo(grid));
602       myMinimizedButtonsPlaceholder.remove(grid);
603       myCommonActionsPlaceholder.remove(grid);
604       Disposer.dispose(grid);
605     }
606   }
607
608   @Nullable
609   private GridImpl getGridFor(Content content, boolean createIfMissing) {
610     GridImpl grid = (GridImpl)findGridFor(content);
611     if (grid != null || !createIfMissing) return grid;
612
613     grid = new GridImpl(this, mySessionName);
614
615     if (myCurrentOver != null || myOriginal != null) {
616       Integer forcedDropIndex = content.getUserData(RunnerLayout.DROP_INDEX);
617       final int index = myTabs.getDropInfoIndex() + (myOriginal != null ? myOriginal.getTabOffsetFor(this) : 0);
618       final TabImpl tab = myLayoutSettings.getOrCreateTab(forcedDropIndex != null ? forcedDropIndex : -1);
619       final Integer defaultIndex = content.getUserData(RunnerLayout.DEFAULT_INDEX);
620       tab.setDefaultIndex(defaultIndex != null ? defaultIndex : -1);
621       tab.setIndex(forcedDropIndex != null ? forcedDropIndex : index);
622       getStateFor(content).assignTab(tab);
623     }
624     
625     TabInfo tab = new TabInfo(grid).setObject(getStateFor(content).getTab()).setText("Tab");
626
627
628     Wrapper left = new Wrapper();
629     myCommonActionsPlaceholder.put(grid, left);
630
631
632     Wrapper minimizedToolbar = new Wrapper();
633     myMinimizedButtonsPlaceholder.put(grid, minimizedToolbar);
634
635
636     final Wrapper searchComponent = new Wrapper();
637     if (content.getSearchComponent() != null) {
638       searchComponent.setContent(content.getSearchComponent());
639     }
640
641     TwoSideComponent right = new TwoSideComponent(searchComponent, minimizedToolbar);
642
643
644     NonOpaquePanel sideComponent = new TwoSideComponent(left, right);
645
646     tab.setSideComponent(sideComponent);
647
648     tab.setTabLabelActions((ActionGroup)myActionManager.getAction(VIEW_TOOLBAR), TAB_TOOLBAR_PLACE);
649
650     myTabs.addTab(tab);
651     myTabs.sortTabs(myTabsComparator);
652
653     return grid;
654   }
655
656   private int getTabOffsetFor(RunnerContentUi ui) {
657     int offset = myTabs.getTabCount();
658     for (RunnerContentUi child : myChildren) {
659       if (child == ui) break;
660       offset += child.myTabs.getTabCount();
661     }
662     return offset;
663   }
664
665   @Nullable
666   public GridCell findCellFor(final Content content) {
667     GridImpl cell = getGridFor(content, false);
668     return cell != null ? cell.getCellFor(content) : null;
669   }
670
671   private boolean rebuildToolbar() {
672     boolean hasToolbarContent = rebuildCommonActions();
673     hasToolbarContent |= rebuildMinimizedActions();
674     return hasToolbarContent;
675   }
676
677   private boolean rebuildCommonActions() {
678     boolean hasToolbarContent = false;
679     for (Map.Entry<GridImpl, Wrapper> entry : myCommonActionsPlaceholder.entrySet()) {
680       Wrapper eachPlaceholder = entry.getValue();
681       List<Content> contentList = entry.getKey().getContents();
682
683       Set<Content> contents = new HashSet<Content>();
684       contents.addAll(contentList);
685
686       DefaultActionGroup groupToBuild;
687       JComponent contextComponent = null;
688       if (isHorizontalToolbar() && contents.size() == 1) {
689         Content content = contentList.get(0);
690         groupToBuild = new DefaultActionGroup();
691         if (content.getActions() != null) {
692           groupToBuild.addAll(content.getActions());
693           groupToBuild.addSeparator();
694           contextComponent = content.getActionsContextComponent();
695         }
696         groupToBuild.addAll(myTopActions);
697       }
698       else {
699         final DefaultActionGroup group = new DefaultActionGroup();
700         group.addAll(myTopActions);
701         groupToBuild = group;
702       }
703
704       final AnAction[] actions = groupToBuild.getChildren(null);
705       if (!Arrays.equals(actions, myContextActions.get(entry.getKey()))) {
706         ActionToolbar tb = myActionManager.createActionToolbar(myActionsPlace, groupToBuild, true);
707         tb.getComponent().setBorder(null);
708         tb.setTargetComponent(contextComponent);
709         eachPlaceholder.setContent(tb.getComponent());
710       }
711
712       if (groupToBuild.getChildrenCount() > 0) {
713         hasToolbarContent = true;
714       }
715
716       myContextActions.put(entry.getKey(), actions);
717     }
718
719     return hasToolbarContent;
720   }
721
722   private boolean rebuildMinimizedActions() {
723     for (Map.Entry<GridImpl, Wrapper> entry : myMinimizedButtonsPlaceholder.entrySet()) {
724       Wrapper eachPlaceholder = entry.getValue();
725       ActionToolbar tb = myActionManager.createActionToolbar(ActionPlaces.DEBUGGER_TOOLBAR, myMinimizedViewActions, true);
726       tb.getComponent().setBorder(null);
727       tb.setReservePlaceAutoPopupIcon(false);
728       JComponent minimized = tb.getComponent();
729       eachPlaceholder.setContent(minimized);
730     }
731
732     myTabs.getComponent().revalidate();
733     myTabs.getComponent().repaint();
734
735     return myMinimizedViewActions.getChildrenCount() > 0;
736   }
737
738   private void updateTabsUI(final boolean validateNow) {
739     boolean hasToolbarContent = rebuildToolbar();
740
741     List<TabInfo> tabs = myTabs.getTabs();
742     for (TabInfo each : tabs) {
743       hasToolbarContent |= updateTabUI(each);
744     }
745     int tabsCount = tabs.size();
746     for (RunnerContentUi child : myChildren) {
747       tabsCount += child.myTabs.getTabCount();
748     }
749     myTabs.getPresentation().setHideTabs(!hasToolbarContent && tabsCount <= 1 && myOriginal == null);
750     myTabs.updateTabActions(validateNow);
751
752     if (validateNow) {
753       myTabs.sortTabs(myTabsComparator);
754     }
755   }
756
757   private boolean updateTabUI(TabInfo tab) {
758     TabImpl t = getTabFor(tab);
759     if (t == null) {
760       return false;
761     }
762
763     String title = t.getDisplayName();
764     Icon icon = t.getIcon();
765
766     GridImpl grid = getGridFor(tab);
767     boolean hasToolbarContent = grid.updateGridUI();
768
769     List<Content> contents = grid.getContents();
770     if (title == null) {
771       title = "";
772       Iterator<Content> all = contents.iterator();
773       while (all.hasNext()) {
774         Content each = all.next();
775         title += each.getTabName();
776         if (all.hasNext()) {
777           title += " | ";
778         }
779       }
780     }
781
782     if (icon == null && contents.size() == 1) {
783       tab.setHidden(grid.isMinimized(contents.get(0)));
784       icon = contents.get(0).getIcon();
785     }
786
787     tab.setDragOutDelegate(myTabs.getTabs().size() > 1 || !isOriginal() ? myDragOutDelegate : null);
788
789     Tab gridTab = grid.getTab();
790     tab.setText(title).setIcon(gridTab != null && gridTab.isDefault() ? null : icon);
791
792     return hasToolbarContent;
793   }
794
795   private ActionCallback restoreLastUiState() {
796     if (isStateBeingRestored()) return new ActionCallback.Rejected();
797
798     try {
799       setStateIsBeingRestored(true, this);
800
801       List<TabInfo> tabs = new ArrayList<TabInfo>();
802       tabs.addAll(myTabs.getTabs());
803
804       final ActionCallback result = new ActionCallback(tabs.size());
805
806       for (TabInfo each : tabs) {
807         getGridFor(each).restoreLastUiState().notifyWhenDone(result);
808       }
809
810       return result;
811     }
812     finally {
813       setStateIsBeingRestored(false, this);
814     }
815   }
816
817   public void saveUiState() {
818     if (isStateBeingRestored()) return;
819
820     if (myOriginal != null) {
821       myOriginal.saveUiState();
822       return;
823     }
824     int offset = updateTabsIndices(myTabs, 0);
825     for (RunnerContentUi child : myChildren) {
826       offset = updateTabsIndices(child.myTabs, offset);
827     }
828
829     doSaveUiState();
830   }
831
832   private static int updateTabsIndices(final JBRunnerTabs tabs, int offset) {
833     for (TabInfo each : tabs.getTabs()) {
834       final int index = tabs.getIndexOf(each);
835       final TabImpl tab = getTabFor(each);
836       if (tab != null) tab.setIndex(index >= 0 ? index + offset : index);
837     }
838     return offset + tabs.getTabCount();
839   }
840
841   private void doSaveUiState() {
842     if (isStateBeingRestored()) return;
843
844     for (TabInfo each : myTabs.getTabs()) {
845       GridImpl eachGrid = getGridFor(each);
846       eachGrid.saveUiState();
847     }
848
849     for (RunnerContentUi child : myChildren) {
850       child.doSaveUiState();
851     }
852   }
853
854   @Nullable
855   public Tab getTabFor(final Grid grid) {
856     TabInfo info = myTabs.findInfo((Component)grid);
857     return getTabFor(info);
858   }
859
860   @Override
861   public void showNotify() {
862     final Window window = SwingUtilities.getWindowAncestor(myComponent);
863     if (window instanceof IdeFrame.Child) {
864       ((IdeFrame.Child)window).setFrameTitle(mySessionName);
865     }
866   }
867
868   @Override
869   public void hideNotify() {}
870
871   @Nullable
872   private static TabImpl getTabFor(@Nullable final TabInfo tab) {
873     if (tab == null) {
874       return null;
875     }
876     return (TabImpl)tab.getObject();
877   }
878
879   private static GridImpl getGridFor(TabInfo tab) {
880     return (GridImpl)tab.getComponent();
881   }
882
883   @Nullable
884   public Grid findGridFor(Content content) {
885     TabImpl tab = (TabImpl)getStateFor(content).getTab();
886     for (TabInfo each : myTabs.getTabs()) {
887       TabImpl t = getTabFor(each);
888       if (t != null && t.equals(tab)) return getGridFor(each);
889     }
890
891     return null;
892   }
893
894   private ArrayList<GridImpl> getGrids() {
895     ArrayList<GridImpl> result = new ArrayList<GridImpl>();
896     for (TabInfo each : myTabs.getTabs()) {
897       result.add(getGridFor(each));
898     }
899     return result;
900   }
901
902
903   public void setHorizontalToolbar(final boolean state) {
904     myLayoutSettings.setToolbarHorizontal(state);
905     for (GridImpl each : getGrids()) {
906       each.setToolbarHorizontal(state);
907     }
908
909     myContextActions.clear();
910     updateTabsUI(false);
911   }
912
913
914   public boolean isSingleSelection() {
915     return false;
916   }
917
918   public boolean isToSelectAddedContent() {
919     return false;
920   }
921
922   public boolean canBeEmptySelection() {
923     return true;
924   }
925
926   public void beforeDispose() {
927     if (myComponent.getRootPane() != null) {
928       saveUiState();
929     }
930     if (myOriginal != null) {
931       myDisposing = true;
932       fireContentClosed(null);
933     }
934   }
935
936   public boolean canChangeSelectionTo(Content content, boolean implicit) {
937     if (implicit) {
938       GridImpl grid = getGridFor(content, false);
939       if (grid != null) {
940         return !grid.isMinimized(content);
941       }
942     }
943
944     return true;
945   }
946
947   @Override
948   public String getCloseActionName() {
949     return UIBundle.message("tabbed.pane.close.tab.action.name");
950   }
951
952   @Override
953   public String getCloseAllButThisActionName() {
954     return UIBundle.message("tabbed.pane.close.all.tabs.but.this.action.name");
955   }
956
957   @Override
958   public String getPreviousContentActionName() {
959     return "Select Previous Tab";
960   }
961
962   @Override
963   public String getNextContentActionName() {
964     return "Select Next Tab";
965   }
966
967   public void dispose() {
968     if (myOriginal != null) {
969       myOriginal.myChildren.remove(this);
970     }
971   }
972
973   public void restoreLayout() {
974     final RunnerContentUi[] children = myChildren.toArray(new RunnerContentUi[myChildren.size()]);
975     final List<Content> contents = new ArrayList<Content>();
976     Collections.addAll(contents, myManager.getContents());
977     for (RunnerContentUi child : children) {
978       Collections.addAll(contents, child.myManager.getContents());
979     }
980     Content[] all = contents.toArray(new Content[contents.size()]);
981     Arrays.sort(all, new Comparator<Content>() {
982       @Override
983       public int compare(Content content, Content content1) {
984         final int i = getStateFor(content).getTab().getDefaultIndex();
985         final int i1 = getStateFor(content1).getTab().getDefaultIndex();
986         return i - i1;
987       }
988     });
989     
990     setStateIsBeingRestored(true, this);
991     try {
992       for (RunnerContentUi child : children) {
993         child.myManager.removeAllContents(false);
994       }
995       myManager.removeAllContents(false);
996       myMinimizedViewActions.removeAll();
997     }
998     finally {
999       setStateIsBeingRestored(false, this);
1000     }
1001
1002     myLayoutSettings.resetToDefault();
1003     for (Content each : all) {
1004       myManager.addContent(each);
1005     }
1006
1007     updateTabsUI(true);
1008   }
1009
1010   public boolean isStateBeingRestored() {
1011     return !myRestoreStateRequestors.isEmpty();
1012   }
1013
1014   public void setStateIsBeingRestored(final boolean restoredNow, final Object requestor) {
1015     if (restoredNow) {
1016       myRestoreStateRequestors.add(requestor);
1017     }
1018     else {
1019       myRestoreStateRequestors.remove(requestor);
1020     }
1021   }
1022
1023   public ActionGroup getLayoutActions() {
1024     return (ActionGroup)myActionManager.getAction(LAYOUT);
1025   }
1026
1027   public void updateActionsImmediately() {
1028     if (myToolbar.getTargetComponent() instanceof ActionToolbar) {
1029       ((ActionToolbar)myToolbar.getTargetComponent()).updateActionsImmediately();
1030     }
1031   }
1032
1033   public void setMinimizeActionEnabled(final boolean enabled) {
1034     myMinimizeActionEnabled = enabled;
1035   }
1036
1037   public void setMovetoGridActionEnabled(final boolean enabled) {
1038     myMoveToGridActionEnabled = enabled;
1039   }
1040
1041   public boolean isMinimizeActionEnabled() {
1042     return myMinimizeActionEnabled && myOriginal == null;
1043   }
1044
1045   public boolean isMoveToGridActionEnabled() {
1046     return myMoveToGridActionEnabled;
1047   }
1048
1049   public void setPolicy(String contentId, final LayoutAttractionPolicy policy) {
1050     myAttractions.put(contentId, policy);
1051   }
1052
1053   public void setConditionPolicy(final String condition, final LayoutAttractionPolicy policy) {
1054     myConditionAttractions.put(condition, policy);
1055   }
1056
1057   private static LayoutAttractionPolicy getOrCreatePolicyFor(String key,
1058                                                              Map<String, LayoutAttractionPolicy> map,
1059                                                              LayoutAttractionPolicy defaultPolicy) {
1060     LayoutAttractionPolicy policy = map.get(key);
1061     if (policy == null) {
1062       policy = defaultPolicy;
1063       map.put(key, policy);
1064     }
1065     return policy;
1066   }
1067
1068   @Nullable
1069   public Content findContent(final String key) {
1070     final ContentManager manager = getContentManager();
1071     if (manager == null || key == null) return null;
1072
1073     Content[] contents = manager.getContents();
1074     for (Content content : contents) {
1075       String kind = content.getUserData(ViewImpl.ID);
1076       if (key.equals(kind)) {
1077         return content;
1078       }
1079     }
1080
1081     return null;
1082   }
1083
1084   public void setToDisposeRemovedContent(final boolean toDispose) {
1085     myToDisposeRemovedContent = toDispose;
1086   }
1087
1088   public boolean isToDisposeRemovedContent() {
1089     return myToDisposeRemovedContent;
1090   }
1091
1092   private class MyComponent extends Wrapper.FocusHolder implements DataProvider, QuickActionProvider {
1093
1094     private boolean myWasEverAdded;
1095
1096     public MyComponent() {
1097       setOpaque(true);
1098       setFocusCycleRoot(true);
1099       setBorder(new ToolWindow.Border(true, false, true, true));
1100     }
1101
1102     @Nullable
1103     public Object getData(@NonNls final String dataId) {
1104       if (KEY.is(dataId)) {
1105         return RunnerContentUi.this;
1106       }
1107       else {
1108         return null;
1109       }
1110     }
1111
1112     @Override
1113     public String getName() {
1114       return RunnerContentUi.this.getName();
1115     }
1116
1117     public List<AnAction> getActions(boolean originalProvider) {
1118       return RunnerContentUi.this.getActions(originalProvider);
1119     }
1120
1121     public JComponent getComponent() {
1122       return RunnerContentUi.this.getComponent();
1123     }
1124
1125     public boolean isCycleRoot() {
1126       return RunnerContentUi.this.isCycleRoot();
1127     }
1128
1129     public void addNotify() {
1130       super.addNotify();
1131
1132       if (!myUiLastStateWasRestored && myOriginal == null) {
1133         myUiLastStateWasRestored = true;
1134
1135         // [kirillk] this is done later since restoreUiState doesn't work properly in the addNotify call chain
1136         //todo to investigate and to fix (may cause extra flickering)
1137         //noinspection SSBasedInspection
1138         SwingUtilities.invokeLater(new Runnable() {
1139           public void run() {
1140             restoreLastUiState().doWhenDone(new Runnable() {
1141               public void run() {
1142                 if (!myWasEverAdded) {
1143                   myWasEverAdded = true;
1144                   attractOnStartup();
1145                   myInitialized.setDone();
1146                 }
1147               }
1148             });
1149           }
1150         });
1151       }
1152     }
1153
1154     public void removeNotify() {
1155       super.removeNotify();
1156
1157       if (Disposer.isDisposed(RunnerContentUi.this)) return;
1158
1159       saveUiState();
1160     }
1161   }
1162
1163   @SuppressWarnings({"SSBasedInspection"})
1164   // [kirillk] this is done later since "startup" attractions should be done gently, only if no explicit calls are done
1165   private void attractOnStartup() {
1166     final int currentCount = myAttractionCount;
1167     SwingUtilities.invokeLater(new Runnable() {
1168       public void run() {
1169         if (currentCount < myAttractionCount) return;
1170         attractByCondition(LayoutViewOptions.STARTUP, false);
1171       }
1172     });
1173   }
1174
1175   public void attract(final Content content, boolean afterInitialized) {
1176     processAttraction(content.getUserData(ViewImpl.ID), myAttractions, new LayoutAttractionPolicy.Bounce(), afterInitialized, true);
1177   }
1178
1179   public void attractByCondition(String condition, boolean afterInitialized) {
1180     processAttraction(myLayoutSettings.getToFocus(condition), myConditionAttractions, myLayoutSettings.getAttractionPolicy(condition),
1181                       afterInitialized, true);
1182   }
1183
1184   public void clearAttractionByCondition(String condition, boolean afterInitialized) {
1185     processAttraction(myLayoutSettings.getToFocus(condition), myConditionAttractions, new LayoutAttractionPolicy.FocusOnce(),
1186                       afterInitialized, false);
1187   }
1188
1189   private void processAttraction(final String contentId,
1190                                  final Map<String, LayoutAttractionPolicy> policyMap,
1191                                  final LayoutAttractionPolicy defaultPolicy,
1192                                  final boolean afterInitialized,
1193                                  final boolean activate) {
1194     IdeFocusManager.getInstance(getProject()).doWhenFocusSettlesDown(new Runnable() {
1195       public void run() {
1196         myInitialized.processOnDone(new Runnable() {
1197           public void run() {
1198             Content content = findContent(contentId);
1199             if (content == null) return;
1200
1201             final LayoutAttractionPolicy policy = getOrCreatePolicyFor(contentId, policyMap, defaultPolicy);
1202             if (activate) {
1203               myAttractionCount++;
1204               policy.attract(content, myRunnerUi);
1205             }
1206             else {
1207               policy.clearAttraction(content, myRunnerUi);
1208             }
1209           }
1210         }, afterInitialized);
1211       }
1212     });
1213   }
1214
1215
1216   public static boolean ensureValid(JComponent c) {
1217     if (c.getRootPane() == null) return false;
1218
1219     Container eachParent = c.getParent();
1220     while (eachParent != null && eachParent.isValid()) {
1221       eachParent = eachParent.getParent();
1222     }
1223
1224     if (eachParent == null) {
1225       eachParent = c.getRootPane();
1226     }
1227
1228     eachParent.validate();
1229
1230     return true;
1231   }
1232
1233   public ContentUI getContentUI() {
1234     return this;
1235   }
1236
1237   public void minimize(final Content content, final CellTransform.Restore restore) {
1238     final Ref<AnAction> restoreAction = new Ref<AnAction>();
1239     restoreAction.set(new RestoreViewAction(content, new CellTransform.Restore() {
1240       public ActionCallback restoreInGrid() {
1241         myMinimizedViewActions.remove(restoreAction.get());
1242         return restore.restoreInGrid().doWhenDone(new Runnable() {
1243           public void run() {
1244             saveUiState();
1245             select(content, true);
1246             updateTabsUI(false);
1247           }
1248         });
1249       }
1250     }));
1251
1252     myMinimizedViewActions.add(restoreAction.get());
1253
1254     saveUiState();
1255     updateTabsUI(false);
1256   }
1257
1258   public Project getProject() {
1259     return myProject;
1260   }
1261
1262   public CellTransform.Facade getCellTransform() {
1263     return this;
1264   }
1265
1266   public ContentManager getContentManager() {
1267     return myManager;
1268   }
1269
1270   public ActionManager getActionManager() {
1271     return myActionManager;
1272   }
1273
1274   public RunnerLayout getLayoutSettings() {
1275     return myLayoutSettings;
1276   }
1277
1278   public View getStateFor(final Content content) {
1279     return myLayoutSettings.getStateFor(content);
1280   }
1281
1282   public boolean isHorizontalToolbar() {
1283     return false;
1284   }
1285
1286   public ActionCallback select(final Content content, final boolean requestFocus) {
1287     final GridImpl grid = (GridImpl)findGridFor(content);
1288     if (grid == null) return new ActionCallback.Done();
1289
1290
1291     final TabInfo info = myTabs.findInfo(grid);
1292     if (info == null) return new ActionCallback.Done();
1293
1294
1295     final ActionCallback result = new ActionCallback();
1296     myTabs.select(info, false).doWhenDone(new Runnable() {
1297       public void run() {
1298         grid.select(content, requestFocus).notifyWhenDone(result);
1299       }
1300     });
1301
1302
1303     return result;
1304   }
1305
1306   public void validate(final Content content, final ActiveRunnable toRestore) {
1307     final TabInfo current = myTabs.getSelectedInfo();
1308     myTabs.getPresentation().setPaintBlocked(true, true);
1309
1310     select(content, false).doWhenDone(new Runnable() {
1311       public void run() {
1312         myTabs.getComponent().validate();
1313         toRestore.run().doWhenDone(new Runnable() {
1314           public void run() {
1315             myTabs.select(current, true);
1316             myTabs.getPresentation().setPaintBlocked(false, true);
1317           }
1318         });
1319       }
1320     });
1321   }
1322
1323   private static class TwoSideComponent extends NonOpaquePanel {
1324     private TwoSideComponent(JComponent left, JComponent right) {
1325       setLayout(new CommonToolbarLayout(left, right));
1326       add(left);
1327       add(right);
1328     }
1329   }
1330
1331   private static class CommonToolbarLayout extends AbstractLayoutManager {
1332     private final JComponent myLeft;
1333     private final JComponent myRight;
1334
1335     public CommonToolbarLayout(final JComponent left, final JComponent right) {
1336       myLeft = left;
1337       myRight = right;
1338     }
1339
1340     public Dimension preferredLayoutSize(final Container parent) {
1341
1342       Dimension size = new Dimension();
1343       Dimension leftSize = myLeft.getPreferredSize();
1344       Dimension rightSize = myRight.getPreferredSize();
1345
1346       size.width = leftSize.width + rightSize.width;
1347       size.height = Math.max(leftSize.height, rightSize.height);
1348
1349       return size;
1350     }
1351
1352     public void layoutContainer(final Container parent) {
1353       Dimension size = parent.getSize();
1354       Dimension prefSize = parent.getPreferredSize();
1355       if (prefSize.width <= size.width) {
1356         myLeft.setBounds(0, 0, myLeft.getPreferredSize().width, parent.getHeight());
1357         Dimension rightSize = myRight.getPreferredSize();
1358         myRight.setBounds(parent.getWidth() - rightSize.width, 0, rightSize.width, parent.getHeight());
1359       }
1360       else {
1361         Dimension leftMinSize = myLeft.getMinimumSize();
1362         Dimension rightMinSize = myRight.getMinimumSize();
1363
1364         int delta = (prefSize.width - size.width) / 2;
1365
1366         myLeft.setBounds(0, 0, myLeft.getPreferredSize().width - delta, parent.getHeight());
1367         int rightX = (int)myLeft.getBounds().getMaxX();
1368         int rightWidth = size.width - rightX;
1369         if (rightWidth < rightMinSize.width) {
1370           Dimension leftSize = myLeft.getSize();
1371           int diffToRightMin = rightMinSize.width - rightWidth;
1372           if (leftSize.width - diffToRightMin >= leftMinSize.width) {
1373             leftSize.width -= diffToRightMin;
1374             myLeft.setSize(leftSize);
1375           }
1376         }
1377
1378         myRight.setBounds((int)myLeft.getBounds().getMaxX(), 0, parent.getWidth() - myLeft.getWidth(), parent.getHeight());
1379       }
1380
1381       toMakeVerticallyInCenter(myLeft, parent);
1382       toMakeVerticallyInCenter(myRight, parent);
1383     }
1384
1385     private static void toMakeVerticallyInCenter(JComponent comp, Container parent) {
1386       final Rectangle compBounds = comp.getBounds();
1387       int compHeight = comp.getPreferredSize().height;
1388       final int parentHeight = parent.getHeight();
1389       if (compHeight > parentHeight) {
1390         compHeight = parentHeight;
1391       }
1392
1393       int y = (int)Math.floor(parentHeight / 2.0 - compHeight / 2.0);
1394       comp.setBounds(compBounds.x, y, compBounds.width, compHeight);
1395     }
1396   }
1397
1398   public IdeFocusManager getFocusManager() {
1399     return myFocusManager;
1400   }
1401
1402   public RunnerLayoutUi getRunnerLayoutUi() {
1403     return myRunnerUi;
1404   }
1405
1406   public String getName() {
1407     return mySessionName;
1408   }
1409
1410   public List<AnAction> getActions(boolean originalProvider) {
1411     ArrayList<AnAction> result = new ArrayList<AnAction>();
1412     if (myLeftToolbarActions != null) {
1413       AnAction[] kids = myLeftToolbarActions.getChildren(null);
1414       ContainerUtil.addAll(result, kids);
1415     }
1416     return result;
1417   }
1418
1419   public SwitchTarget getCurrentTarget() {
1420     Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
1421     if (owner == null) return myTabs.getCurrentTarget();
1422
1423     GridImpl grid = getSelectedGrid();
1424     if (grid != null && grid.getContents().size() <= 1) return myTabs.getCurrentTarget();
1425
1426     if (grid != null) {
1427       SwitchTarget cell = grid.getCellFor(owner);
1428       return cell != null ? cell : myTabs.getCurrentTarget();
1429     }
1430     else {
1431       return myTabs.getCurrentTarget();
1432     }
1433   }
1434
1435   public List<SwitchTarget> getTargets(boolean onlyVisible, boolean originalProvider) {
1436     List<SwitchTarget> result = new ArrayList<SwitchTarget>();
1437
1438     result.addAll(myTabs.getTargets(true, false));
1439     GridImpl grid = getSelectedGrid();
1440     if (grid != null) {
1441       result.addAll(grid.getTargets(onlyVisible));
1442     }
1443
1444     for (Wrapper wrapper : myMinimizedButtonsPlaceholder.values()) {
1445       if (!wrapper.isShowing()) continue;
1446       JComponent target = wrapper.getTargetComponent();
1447       if (target instanceof ActionToolbar) {
1448         ActionToolbar tb = (ActionToolbar)target;
1449         result.addAll(tb.getTargets(onlyVisible, false));
1450       }
1451     }
1452
1453     return result;
1454   }
1455
1456
1457   private int findFreeWindow() {
1458     int i;
1459     for (i = 1; i < Integer.MAX_VALUE; i++) {
1460       if (!isUsed(i)) {
1461         return i;
1462       }
1463     }
1464     return i;
1465   }
1466
1467   private boolean isUsed(int i) {
1468     for (RunnerContentUi child : myChildren) {
1469       if (child.getWindow() == i) {
1470         return true;
1471       }
1472     }
1473     return false;
1474   }
1475
1476   private DockManagerImpl getDockManager() {
1477     return (DockManagerImpl)DockManager.getInstance(myProject);
1478   }
1479   
1480   class MyDragOutDelegate implements TabInfo.DragOutDelegate {
1481     private DragSession mySession;
1482
1483     @Override
1484     public void dragOutStarted(MouseEvent mouseEvent, TabInfo info) {
1485       final JComponent component = info.getComponent();
1486       final Content[] data = CONTENT_KEY.getData((DataProvider)component);
1487       final List<Content> contents = Arrays.asList(data);
1488
1489       storeDefaultIndices(data);
1490
1491       final Dimension size = info.getComponent().getSize();
1492       final Image image = myTabs.getComponentImage(info);
1493       if (component instanceof Grid) {
1494         info.setHidden(true);
1495       }
1496
1497       Presentation presentation = new Presentation(info.getText());
1498       presentation.setIcon(info.getIcon());
1499       mySession = getDockManager().createDragSession(mouseEvent, new DockableGrid(image, presentation,
1500                                                                                   size,
1501                                                                                   contents, 0));
1502     }
1503
1504     @Override
1505     public void processDragOut(MouseEvent event, TabInfo source) {
1506       mySession.process(event);
1507     }
1508
1509     @Override
1510     public void dragOutFinished(MouseEvent event, TabInfo source) {
1511       final Component component = event.getComponent();
1512       final IdeFrame window = UIUtil.getParentOfType(IdeFrame.class, component);
1513       if (window != null) {
1514         
1515       }
1516       mySession.process(event);
1517       mySession = null;
1518     }
1519   }
1520
1521   class DockableGrid implements DockableContent<List<Content>> {
1522     final Image myImg;
1523     private Presentation myPresentation;
1524     private final Dimension myPreferredSize;
1525     private final List<Content> myContents;
1526     private final int myWindow;
1527
1528     public DockableGrid(Image img, Presentation presentation, final Dimension size, List<Content> contents, int window) {
1529       myImg = img;
1530       myPresentation = presentation;
1531       myPreferredSize = size;
1532       myContents = contents;
1533       myWindow = window;
1534     }
1535
1536     @Override
1537     public List<Content> getKey() {
1538       return myContents;
1539     }
1540
1541     @Override
1542     public Image getPreviewImage() {
1543       return myImg;
1544     }
1545
1546     @Override
1547     public Dimension getPreferredSize() {
1548       return myPreferredSize;
1549     }
1550
1551     @Override
1552     public String getDockContainerType() {
1553       return DockableGridContainerFactory.TYPE;
1554     }
1555
1556     @Override
1557     public Presentation getPresentation() {
1558       return myPresentation;
1559     }
1560
1561     public RunnerContentUi getRunnerUi() {
1562       return RunnerContentUi.this;
1563     }
1564
1565     public RunnerContentUi getOriginalRunnerUi() {
1566       return myOriginal != null ? myOriginal : RunnerContentUi.this;
1567     }
1568
1569     public List<Content> getContents() {
1570       return myContents;
1571     }
1572
1573     @Override
1574     public void close() {
1575     }
1576
1577     public int getWindow() {
1578       return myWindow;
1579     }
1580   }
1581
1582   void fireContentOpened(Content content) {
1583     for (Listener each : myDockingListeners) {
1584       each.contentAdded(content);
1585     }
1586   }
1587
1588   void fireContentClosed(Content content) {
1589     for (Listener each : myDockingListeners) {
1590       each.contentRemoved(content);
1591     }
1592   }
1593 }