2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.intellij.execution.ui.layout.impl;
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;
58 import javax.swing.border.EmptyBorder;
60 import java.awt.event.MouseEvent;
61 import java.beans.PropertyChangeEvent;
62 import java.beans.PropertyChangeListener;
64 import java.util.List;
65 import java.util.concurrent.CopyOnWriteArraySet;
67 public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Facade, ViewContextEx, PropertyChangeListener, SwitchProvider,
68 QuickActionProvider, DockContainer.Dialog {
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";
74 ContentManager myManager;
75 RunnerLayout myLayoutSettings;
77 ActionManager myActionManager;
79 MyComponent myComponent = new MyComponent();
81 private final Wrapper myToolbar = new Wrapper();
82 final MyDragOutDelegate myDragOutDelegate = new MyDragOutDelegate();
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();
93 ActionGroup myTopActions = new DefaultActionGroup();
95 DefaultActionGroup myMinimizedViewActions = new DefaultActionGroup();
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[]>();
101 boolean myUiLastStateWasRestored;
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;
108 private boolean myMinimizeActionEnabled = true;
109 private boolean myMoveToGridActionEnabled = true;
110 private final RunnerLayoutUi myRunnerUi;
112 private final Map<String, LayoutAttractionPolicy> myAttractions = new HashMap<String, LayoutAttractionPolicy>();
113 private final Map<String, LayoutAttractionPolicy> myConditionAttractions = new HashMap<String, LayoutAttractionPolicy>();
115 private ActionGroup myAdditonalFocusActions;
117 private final ActionCallback myInitialized = new ActionCallback();
118 private boolean myToDisposeRemovedContent = true;
120 private int myAttractionCount;
121 private ActionGroup myLeftToolbarActions;
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>() {
130 public int compare(RunnerContentUi o1, RunnerContentUi o2) {
131 return o1.myWindow - o2.myWindow;
134 private int myWindow;
135 private boolean myDisposing;
137 public RunnerContentUi(Project project,
139 ActionManager actionManager,
140 IdeFocusManager focusManager,
141 RunnerLayout settings,
142 String sessionName) {
145 myLayoutSettings = settings;
146 myActionManager = actionManager;
147 mySessionName = sessionName;
148 myFocusManager = focusManager;
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;
158 public void setTopActions(@NotNull final ActionGroup topActions, @NotNull String place) {
159 myTopActions = topActions;
160 myActionsPlace = place;
162 rebuildCommonActions();
165 public void setAdditionalFocusActions(final ActionGroup group) {
166 myAdditonalFocusActions = group;
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;
176 myComponent.revalidate();
177 myComponent.repaint();
180 public void initUi() {
181 if (myTabs != null) return;
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();
188 return getGridFor(info).getData(dataId);
191 else if (ViewContext.CONTEXT_KEY.is(dataId)) {
192 return RunnerContentUi.this;
196 }).setTabLabelActionsAutoHide(false).setProvideSwitchTargets(false).setInnerInsets(new Insets(0, 0, 0, 0))
197 .setToDrawBorderIfTabsHidden(false).setTabDraggingEnabled(isMoveToGridActionEnabled()).setUiDecorator(null).getJBTabs();
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));
205 final NonOpaquePanel wrappper = new NonOpaquePanel(new BorderLayout(0, 0));
206 wrappper.add(myToolbar, BorderLayout.WEST);
207 wrappper.add(myTabs.getComponent(), BorderLayout.CENTER);
209 myComponent.setContent(wrappper);
211 myTabs.addListener(new TabsListener() {
213 public void beforeSelectionChanged(TabInfo oldSelection, TabInfo newSelection) {
214 if (oldSelection != null && !isStateBeingRestored()) {
215 final GridImpl grid = getGridFor(oldSelection);
223 public void tabsMoved() {
227 public void selectionChanged(final TabInfo oldSelection, final TabInfo newSelection) {
228 if (!myTabs.getComponent().isShowing()) return;
230 if (newSelection != null) {
231 newSelection.stopAlerting();
232 getGridFor(newSelection).processAddToUi(false);
235 if (oldSelection != null) {
236 getGridFor(oldSelection).processRemoveFromUi();
241 if (myOriginal != null) {
242 final ContentManager manager = ContentFactory.SERVICE.getInstance().createContentManager(this, false, myProject);
243 Disposer.register((Disposable)myRunnerUi, manager);
244 manager.getComponent();
246 DockManager.getInstance(myProject).register(this);
250 private void rebuildTabPopup() {
251 myTabs.setPopupGroup(getCellPopupGroup(TAB_POPUP_PLACE), TAB_POPUP_PLACE, true);
253 final ArrayList<GridImpl> grids = getGrids();
254 for (GridImpl each : grids) {
255 each.rebuildTabPopup();
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");
263 DefaultActionGroup group = new DefaultActionGroup(VIEW_POPUP, original.isPopup());
265 final AnActionEvent event = new AnActionEvent(null, DataManager.getInstance().getDataContext(), place, new Presentation(),
266 ActionManager.getInstance(), 0);
267 final AnAction[] originalActions = original.getChildren(event);
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);
276 if (myAdditonalFocusActions != null) {
277 final AnAction[] addins = myAdditonalFocusActions.getChildren(event);
278 for (AnAction eachAddin : addins) {
279 group.add(eachAddin);
291 public boolean isOriginal() {
292 return myOriginal == null;
296 public int getWindow() {
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;
305 final GridCellImpl cell = grid.findCell(content);
306 if (cell == null) return;
308 final String property = evt.getPropertyName();
309 if (Content.PROP_ALERT.equals(property)) {
310 attract(content, true);
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);
322 public void processBounce(Content content, final boolean activate) {
323 final GridImpl grid = getGridFor(content, false);
324 if (grid == null) return;
326 final GridCellImpl cell = grid.findCell(content);
327 if (cell == null) return;
330 final TabInfo tab = myTabs.findInfo(grid);
331 if (tab == null) return;
334 if (getSelectedGrid() != grid) {
335 tab.setAlertIcon(content.getAlertIcon());
344 grid.processAlert(content, activate);
349 public ActionCallback detachTo(int window, GridCell cell) {
350 if (myOriginal != null) {
351 return myOriginal.detachTo(window, cell);
353 RunnerContentUi target = null;
355 for (RunnerContentUi child : myChildren) {
356 if (child.myWindow == window) {
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());
368 Dimension size = gridCell.getSize();
370 size = new Dimension(200, 200);
372 final DockableGrid content = new DockableGrid(null, null, size, Arrays.asList(contents), window);
373 if (target != null) {
374 target.add(content, null);
376 final Point location = gridCell.getLocation();
377 location.translate(size.width / 2, size.height / 2);
378 getDockManager().createNewDockContainerFor(content, new RelativePoint(location));
380 return new ActionCallback.Done();
383 private void storeDefaultIndices(Content[] contents) {
384 for (Content content : contents) {
385 content.putUserData(RunnerLayout.DEFAULT_INDEX, getStateFor(content).getTab().getDefaultIndex());
390 public RelativeRectangle getAcceptArea() {
391 return new RelativeRectangle(myTabs.getComponent());
395 public boolean canAccept(DockableContent content, RelativePoint point) {
396 if (!(content instanceof DockableGrid)) {
399 final RunnerContentUi ui = ((DockableGrid)content).getOriginalRunnerUi();
400 return ui.getProject() == myProject && ui.mySessionName.equals(mySessionName);
404 public JComponent getComponent() {
410 public JComponent getContainerComponent() {
412 return myManager.getComponent();
416 public void add(DockableContent dockable, RelativePoint dropTarget) {
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);
424 final Point point = dropTarget != null ? dropTarget.getPoint(myComponent) : null;
425 boolean hadGrid = !myTabs.shouldAddToGlobal(point);
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));
437 getStateFor(content).setWindow(myWindow);
438 myManager.addContent(content);
441 setStateIsBeingRestored(false, this);
450 public void closeAll() {
451 final Content[] contents = myManager.getContents();
452 for (Content content : contents) {
453 getStateFor(content).setWindow(0);
455 myManager.removeAllContents(false);
456 for (Content content : contents) {
457 myOriginal.myManager.addContent(content);
458 myOriginal.findCellFor(content).minimize(content);
463 public void addListener(final Listener listener, Disposable parent) {
464 myDockingListeners.add(listener);
465 Disposer.register(parent, new Disposable() {
467 public void dispose() {
468 myDockingListeners.remove(listener);
474 public boolean isEmpty() {
475 return myTabs.isEmptyVisible() || myDisposing;
479 public Image startDropOver(DockableContent content, RelativePoint point) {
484 public Image processDropOver(DockableContent content, RelativePoint point) {
485 JBTabs current = getTabsAt(content, point);
487 if (myCurrentOver != null && myCurrentOver != current) {
488 resetDropOver(content);
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);
498 if (myCurrentOver != null) {
499 myCurrentOver.processDropOver(myCurrentOverInfo, point);
502 return myCurrentOverImg;
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);
511 if (c instanceof JBTabs) {
521 public void resetDropOver(DockableContent content) {
522 if (myCurrentOver != null) {
523 myCurrentOver.resetDropOver(myCurrentOverInfo);
524 myCurrentOver = null;
525 myCurrentOverInfo = null;
526 myCurrentOverImg = null;
531 public boolean isDisposeWhenEmpty() {
532 return myOriginal != null;
535 public boolean isCycleRoot() {
539 public void setManager(final ContentManager manager) {
540 assert myManager == null;
543 myManager.addContentManagerListener(new ContentManagerListener() {
544 public void contentAdded(final ContentManagerEvent event) {
545 final GridImpl grid = getGridFor(event.getContent(), true);
551 grid.add(event.getContent());
553 if (getSelectedGrid() == grid) {
554 grid.processAddToUi(false);
557 if (myManager.getComponent().isShowing()) {
558 grid.restoreLastUiState();
564 event.getContent().addPropertyChangeListener(RunnerContentUi.this);
565 fireContentOpened(event.getContent());
568 public void contentRemoved(final ContentManagerEvent event) {
569 event.getContent().removePropertyChangeListener(RunnerContentUi.this);
571 GridImpl grid = (GridImpl)findGridFor(event.getContent());
573 grid.remove(event.getContent());
574 grid.processRemoveFromUi();
575 removeGridIfNeeded(grid);
578 fireContentClosed(event.getContent());
581 public void contentRemoveQuery(final ContentManagerEvent event) {
584 public void selectionChanged(final ContentManagerEvent event) {
585 if (isStateBeingRestored()) return;
587 if (event.getOperation() == ContentManagerEvent.ContentOperation.add) {
588 select(event.getContent(), false);
595 private GridImpl getSelectedGrid() {
596 TabInfo selection = myTabs.getSelectedInfo();
597 return selection != null ? getGridFor(selection) : null;
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);
610 private GridImpl getGridFor(Content content, boolean createIfMissing) {
611 GridImpl grid = (GridImpl)findGridFor(content);
612 if (grid != null || !createIfMissing) return grid;
614 grid = new GridImpl(this, mySessionName);
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);
626 TabInfo tab = new TabInfo(grid).setObject(getStateFor(content).getTab()).setText("Tab");
629 Wrapper left = new Wrapper();
630 myCommonActionsPlaceholder.put(grid, left);
633 Wrapper minimizedToolbar = new Wrapper();
634 myMinimizedButtonsPlaceholder.put(grid, minimizedToolbar);
637 final Wrapper searchComponent = new Wrapper();
638 if (content.getSearchComponent() != null) {
639 searchComponent.setContent(content.getSearchComponent());
642 TwoSideComponent right = new TwoSideComponent(searchComponent, minimizedToolbar);
645 NonOpaquePanel sideComponent = new TwoSideComponent(left, right);
647 tab.setSideComponent(sideComponent);
649 tab.setTabLabelActions((ActionGroup)myActionManager.getAction(VIEW_TOOLBAR), TAB_TOOLBAR_PLACE);
652 myTabs.sortTabs(myTabsComparator);
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();
667 public GridCell findCellFor(final Content content) {
668 GridImpl cell = getGridFor(content, false);
669 return cell != null ? cell.getCellFor(content) : null;
672 private boolean rebuildToolbar() {
673 boolean hasToolbarContent = rebuildCommonActions();
674 hasToolbarContent |= rebuildMinimizedActions();
675 return hasToolbarContent;
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();
684 Set<Content> contents = new HashSet<Content>();
685 contents.addAll(contentList);
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();
697 groupToBuild.addAll(myTopActions);
700 final DefaultActionGroup group = new DefaultActionGroup();
701 group.addAll(myTopActions);
702 groupToBuild = group;
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());
713 if (groupToBuild.getChildrenCount() > 0) {
714 hasToolbarContent = true;
717 myContextActions.put(entry.getKey(), actions);
720 return hasToolbarContent;
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);
733 myTabs.getComponent().revalidate();
734 myTabs.getComponent().repaint();
736 return myMinimizedViewActions.getChildrenCount() > 0;
739 private void updateTabsUI(final boolean validateNow) {
740 boolean hasToolbarContent = rebuildToolbar();
742 List<TabInfo> tabs = myTabs.getTabs();
743 for (TabInfo each : tabs) {
744 hasToolbarContent |= updateTabUI(each);
746 int tabsCount = tabs.size();
747 for (RunnerContentUi child : myChildren) {
748 tabsCount += child.myTabs.getTabCount();
750 myTabs.getPresentation().setHideTabs(!hasToolbarContent && tabsCount <= 1 && myOriginal == null);
751 myTabs.updateTabActions(validateNow);
754 myTabs.sortTabs(myTabsComparator);
758 private boolean updateTabUI(TabInfo tab) {
759 TabImpl t = getTabFor(tab);
764 String title = t.getDisplayName();
765 Icon icon = t.getIcon();
767 GridImpl grid = getGridFor(tab);
768 boolean hasToolbarContent = grid.updateGridUI();
770 List<Content> contents = grid.getContents();
773 Iterator<Content> all = contents.iterator();
774 while (all.hasNext()) {
775 Content each = all.next();
776 title += each.getTabName();
783 if (icon == null && contents.size() == 1) {
784 tab.setHidden(grid.isMinimized(contents.get(0)));
785 icon = contents.get(0).getIcon();
788 tab.setDragOutDelegate(myTabs.getTabs().size() > 1 || !isOriginal() ? myDragOutDelegate : null);
790 Tab gridTab = grid.getTab();
791 tab.setText(title).setIcon(gridTab != null && gridTab.isDefault() ? null : icon);
793 return hasToolbarContent;
796 private ActionCallback restoreLastUiState() {
797 if (isStateBeingRestored()) return new ActionCallback.Rejected();
800 setStateIsBeingRestored(true, this);
802 List<TabInfo> tabs = new ArrayList<TabInfo>();
803 tabs.addAll(myTabs.getTabs());
805 final ActionCallback result = new ActionCallback(tabs.size());
807 for (TabInfo each : tabs) {
808 getGridFor(each).restoreLastUiState().notifyWhenDone(result);
814 setStateIsBeingRestored(false, this);
818 public void saveUiState() {
819 if (isStateBeingRestored()) return;
821 if (myOriginal != null) {
822 myOriginal.saveUiState();
825 int offset = updateTabsIndices(myTabs, 0);
826 for (RunnerContentUi child : myChildren) {
827 offset = updateTabsIndices(child.myTabs, offset);
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);
839 return offset + tabs.getTabCount();
842 private void doSaveUiState() {
843 if (isStateBeingRestored()) return;
845 for (TabInfo each : myTabs.getTabs()) {
846 GridImpl eachGrid = getGridFor(each);
847 eachGrid.saveUiState();
850 for (RunnerContentUi child : myChildren) {
851 child.doSaveUiState();
856 public Tab getTabFor(final Grid grid) {
857 TabInfo info = myTabs.findInfo((Component)grid);
858 return getTabFor(info);
862 public void showNotify() {
863 final Window window = SwingUtilities.getWindowAncestor(myComponent);
864 if (window instanceof IdeFrame.Child) {
865 ((IdeFrame.Child)window).setFrameTitle(mySessionName);
870 public void hideNotify() {}
873 private static TabImpl getTabFor(@Nullable final TabInfo tab) {
877 return (TabImpl)tab.getObject();
880 private static GridImpl getGridFor(TabInfo tab) {
881 return (GridImpl)tab.getComponent();
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);
895 private ArrayList<GridImpl> getGrids() {
896 ArrayList<GridImpl> result = new ArrayList<GridImpl>();
897 for (TabInfo each : myTabs.getTabs()) {
898 result.add(getGridFor(each));
904 public void setHorizontalToolbar(final boolean state) {
905 myLayoutSettings.setToolbarHorizontal(state);
906 for (GridImpl each : getGrids()) {
907 each.setToolbarHorizontal(state);
910 myContextActions.clear();
915 public boolean isSingleSelection() {
919 public boolean isToSelectAddedContent() {
923 public boolean canBeEmptySelection() {
927 public void beforeDispose() {
928 if (myComponent.getRootPane() != null) {
931 if (myOriginal != null) {
933 fireContentClosed(null);
937 public boolean canChangeSelectionTo(Content content, boolean implicit) {
939 GridImpl grid = getGridFor(content, false);
941 return !grid.isMinimized(content);
949 public String getCloseActionName() {
950 return UIBundle.message("tabbed.pane.close.tab.action.name");
954 public String getCloseAllButThisActionName() {
955 return UIBundle.message("tabbed.pane.close.all.tabs.but.this.action.name");
959 public String getPreviousContentActionName() {
960 return "Select Previous Tab";
964 public String getNextContentActionName() {
965 return "Select Next Tab";
968 public void dispose() {
969 if (myOriginal != null) {
970 myOriginal.myChildren.remove(this);
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());
981 Content[] all = contents.toArray(new Content[contents.size()]);
983 setStateIsBeingRestored(true, this);
985 for (RunnerContentUi child : children) {
986 child.myManager.removeAllContents(false);
988 myManager.removeAllContents(false);
989 myMinimizedViewActions.removeAll();
992 setStateIsBeingRestored(false, this);
995 myLayoutSettings.resetToDefault();
996 for (Content each : all) {
997 myManager.addContent(each);
999 restoreLastUiState();
1004 public boolean isStateBeingRestored() {
1005 return !myRestoreStateRequestors.isEmpty();
1008 public void setStateIsBeingRestored(final boolean restoredNow, final Object requestor) {
1010 myRestoreStateRequestors.add(requestor);
1013 myRestoreStateRequestors.remove(requestor);
1017 public ActionGroup getLayoutActions() {
1018 return (ActionGroup)myActionManager.getAction(LAYOUT);
1021 public void updateActionsImmediately() {
1022 if (myToolbar.getTargetComponent() instanceof ActionToolbar) {
1023 ((ActionToolbar)myToolbar.getTargetComponent()).updateActionsImmediately();
1027 public void setMinimizeActionEnabled(final boolean enabled) {
1028 myMinimizeActionEnabled = enabled;
1031 public void setMovetoGridActionEnabled(final boolean enabled) {
1032 myMoveToGridActionEnabled = enabled;
1035 public boolean isMinimizeActionEnabled() {
1036 return myMinimizeActionEnabled && myOriginal == null;
1039 public boolean isMoveToGridActionEnabled() {
1040 return myMoveToGridActionEnabled;
1043 public void setPolicy(String contentId, final LayoutAttractionPolicy policy) {
1044 myAttractions.put(contentId, policy);
1047 public void setConditionPolicy(final String condition, final LayoutAttractionPolicy policy) {
1048 myConditionAttractions.put(condition, policy);
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);
1063 public Content findContent(final String key) {
1064 final ContentManager manager = getContentManager();
1065 if (manager == null || key == null) return null;
1067 Content[] contents = manager.getContents();
1068 for (Content content : contents) {
1069 String kind = content.getUserData(ViewImpl.ID);
1070 if (key.equals(kind)) {
1078 public void setToDisposeRemovedContent(final boolean toDispose) {
1079 myToDisposeRemovedContent = toDispose;
1082 public boolean isToDisposeRemovedContent() {
1083 return myToDisposeRemovedContent;
1086 private class MyComponent extends Wrapper.FocusHolder implements DataProvider, QuickActionProvider {
1088 private boolean myWasEverAdded;
1090 public MyComponent() {
1092 setFocusCycleRoot(true);
1093 setBorder(new ToolWindow.Border(true, false, true, true));
1097 public Object getData(@NonNls final String dataId) {
1098 if (KEY.is(dataId)) {
1099 return RunnerContentUi.this;
1107 public String getName() {
1108 return RunnerContentUi.this.getName();
1111 public List<AnAction> getActions(boolean originalProvider) {
1112 return RunnerContentUi.this.getActions(originalProvider);
1115 public JComponent getComponent() {
1116 return RunnerContentUi.this.getComponent();
1119 public boolean isCycleRoot() {
1120 return RunnerContentUi.this.isCycleRoot();
1123 public void addNotify() {
1126 if (!myUiLastStateWasRestored && myOriginal == null) {
1127 myUiLastStateWasRestored = true;
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() {
1134 restoreLastUiState().doWhenDone(new Runnable() {
1136 if (!myWasEverAdded) {
1137 myWasEverAdded = true;
1139 myInitialized.setDone();
1148 public void removeNotify() {
1149 super.removeNotify();
1151 if (Disposer.isDisposed(RunnerContentUi.this)) return;
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() {
1163 if (currentCount < myAttractionCount) return;
1164 attractByCondition(LayoutViewOptions.STARTUP, false);
1169 public void attract(final Content content, boolean afterInitialized) {
1170 processAttraction(content.getUserData(ViewImpl.ID), myAttractions, new LayoutAttractionPolicy.Bounce(), afterInitialized, true);
1173 public void attractByCondition(String condition, boolean afterInitialized) {
1174 processAttraction(myLayoutSettings.getToFocus(condition), myConditionAttractions, myLayoutSettings.getAttractionPolicy(condition),
1175 afterInitialized, true);
1178 public void clearAttractionByCondition(String condition, boolean afterInitialized) {
1179 processAttraction(myLayoutSettings.getToFocus(condition), myConditionAttractions, new LayoutAttractionPolicy.FocusOnce(),
1180 afterInitialized, false);
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() {
1190 myInitialized.processOnDone(new Runnable() {
1192 Content content = findContent(contentId);
1193 if (content == null) return;
1195 final LayoutAttractionPolicy policy = getOrCreatePolicyFor(contentId, policyMap, defaultPolicy);
1197 myAttractionCount++;
1198 policy.attract(content, myRunnerUi);
1201 policy.clearAttraction(content, myRunnerUi);
1204 }, afterInitialized);
1210 public static boolean ensureValid(JComponent c) {
1211 if (c.getRootPane() == null) return false;
1213 Container eachParent = c.getParent();
1214 while (eachParent != null && eachParent.isValid()) {
1215 eachParent = eachParent.getParent();
1218 if (eachParent == null) {
1219 eachParent = c.getRootPane();
1222 eachParent.validate();
1227 public ContentUI getContentUI() {
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() {
1239 select(content, true);
1240 updateTabsUI(false);
1246 myMinimizedViewActions.add(restoreAction.get());
1249 updateTabsUI(false);
1252 public Project getProject() {
1256 public CellTransform.Facade getCellTransform() {
1260 public ContentManager getContentManager() {
1264 public ActionManager getActionManager() {
1265 return myActionManager;
1268 public RunnerLayout getLayoutSettings() {
1269 return myLayoutSettings;
1272 public View getStateFor(final Content content) {
1273 return myLayoutSettings.getStateFor(content);
1276 public boolean isHorizontalToolbar() {
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();
1285 final TabInfo info = myTabs.findInfo(grid);
1286 if (info == null) return new ActionCallback.Done();
1289 final ActionCallback result = new ActionCallback();
1290 myTabs.select(info, false).doWhenDone(new Runnable() {
1292 grid.select(content, requestFocus).notifyWhenDone(result);
1300 public void validate(final Content content, final ActiveRunnable toRestore) {
1301 final TabInfo current = myTabs.getSelectedInfo();
1302 myTabs.getPresentation().setPaintBlocked(true, true);
1304 select(content, false).doWhenDone(new Runnable() {
1306 myTabs.getComponent().validate();
1307 toRestore.run().doWhenDone(new Runnable() {
1309 myTabs.select(current, true);
1310 myTabs.getPresentation().setPaintBlocked(false, true);
1317 private static class TwoSideComponent extends NonOpaquePanel {
1318 private TwoSideComponent(JComponent left, JComponent right) {
1319 setLayout(new CommonToolbarLayout(left, right));
1325 private static class CommonToolbarLayout extends AbstractLayoutManager {
1326 private final JComponent myLeft;
1327 private final JComponent myRight;
1329 public CommonToolbarLayout(final JComponent left, final JComponent right) {
1334 public Dimension preferredLayoutSize(final Container parent) {
1336 Dimension size = new Dimension();
1337 Dimension leftSize = myLeft.getPreferredSize();
1338 Dimension rightSize = myRight.getPreferredSize();
1340 size.width = leftSize.width + rightSize.width;
1341 size.height = Math.max(leftSize.height, rightSize.height);
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());
1355 Dimension leftMinSize = myLeft.getMinimumSize();
1356 Dimension rightMinSize = myRight.getMinimumSize();
1358 int delta = (prefSize.width - size.width) / 2;
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);
1372 myRight.setBounds((int)myLeft.getBounds().getMaxX(), 0, parent.getWidth() - myLeft.getWidth(), parent.getHeight());
1375 toMakeVerticallyInCenter(myLeft, parent);
1376 toMakeVerticallyInCenter(myRight, parent);
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;
1387 int y = (int)Math.floor(parentHeight / 2.0 - compHeight / 2.0);
1388 comp.setBounds(compBounds.x, y, compBounds.width, compHeight);
1392 public IdeFocusManager getFocusManager() {
1393 return myFocusManager;
1396 public RunnerLayoutUi getRunnerLayoutUi() {
1400 public String getName() {
1401 return mySessionName;
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);
1413 public SwitchTarget getCurrentTarget() {
1414 Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
1415 if (owner == null) return myTabs.getCurrentTarget();
1417 GridImpl grid = getSelectedGrid();
1418 if (grid != null && grid.getContents().size() <= 1) return myTabs.getCurrentTarget();
1421 SwitchTarget cell = grid.getCellFor(owner);
1422 return cell != null ? cell : myTabs.getCurrentTarget();
1425 return myTabs.getCurrentTarget();
1429 public List<SwitchTarget> getTargets(boolean onlyVisible, boolean originalProvider) {
1430 List<SwitchTarget> result = new ArrayList<SwitchTarget>();
1432 result.addAll(myTabs.getTargets(true, false));
1433 GridImpl grid = getSelectedGrid();
1435 result.addAll(grid.getTargets(onlyVisible));
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));
1451 private int findFreeWindow() {
1453 for (i = 1; i < Integer.MAX_VALUE; i++) {
1461 private boolean isUsed(int i) {
1462 for (RunnerContentUi child : myChildren) {
1463 if (child.getWindow() == i) {
1470 private DockManagerImpl getDockManager() {
1471 return (DockManagerImpl)DockManager.getInstance(myProject);
1474 class MyDragOutDelegate implements TabInfo.DragOutDelegate {
1475 private DragSession mySession;
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);
1483 storeDefaultIndices(data);
1485 final Dimension size = info.getComponent().getSize();
1486 final Image image = myTabs.getComponentImage(info);
1487 if (component instanceof Grid) {
1488 info.setHidden(true);
1491 Presentation presentation = new Presentation(info.getText());
1492 presentation.setIcon(info.getIcon());
1493 mySession = getDockManager().createDragSession(mouseEvent, new DockableGrid(image, presentation,
1499 public void processDragOut(MouseEvent event, TabInfo source) {
1500 mySession.process(event);
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) {
1510 mySession.process(event);
1515 class DockableGrid implements DockableContent<List<Content>> {
1517 private Presentation myPresentation;
1518 private final Dimension myPreferredSize;
1519 private final List<Content> myContents;
1520 private final int myWindow;
1522 public DockableGrid(Image img, Presentation presentation, final Dimension size, List<Content> contents, int window) {
1524 myPresentation = presentation;
1525 myPreferredSize = size;
1526 myContents = contents;
1531 public List<Content> getKey() {
1536 public Image getPreviewImage() {
1541 public Dimension getPreferredSize() {
1542 return myPreferredSize;
1546 public String getDockContainerType() {
1547 return DockableGridContainerFactory.TYPE;
1551 public Presentation getPresentation() {
1552 return myPresentation;
1555 public RunnerContentUi getRunnerUi() {
1556 return RunnerContentUi.this;
1559 public RunnerContentUi getOriginalRunnerUi() {
1560 return myOriginal != null ? myOriginal : RunnerContentUi.this;
1563 public List<Content> getContents() {
1568 public void close() {
1571 public int getWindow() {
1576 void fireContentOpened(Content content) {
1577 for (Listener each : myDockingListeners) {
1578 each.contentAdded(content);
1582 void fireContentClosed(Content content) {
1583 for (Listener each : myDockingListeners) {
1584 each.contentRemoved(content);