blob: 5384ea06de2386e29d26f4312c0f3bd0dda57109 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.ui.layout.impl;
import com.intellij.execution.ui.RunnerLayoutUi;
import com.intellij.execution.ui.layout.*;
import com.intellij.execution.ui.layout.actions.CloseViewAction;
import com.intellij.execution.ui.layout.actions.MinimizeViewAction;
import com.intellij.execution.ui.layout.actions.RestoreViewAction;
import com.intellij.ide.DataManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.AbstractPainter;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.ActiveRunnable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.openapi.wm.IdeGlassPaneUtil;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.JBColor;
import com.intellij.ui.ScreenUtil;
import com.intellij.ui.UIBundle;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.awt.RelativeRectangle;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.ui.components.panels.Wrapper;
import com.intellij.ui.content.*;
import com.intellij.ui.docking.DockContainer;
import com.intellij.ui.docking.DockManager;
import com.intellij.ui.docking.DockableContent;
import com.intellij.ui.docking.DragSession;
import com.intellij.ui.docking.impl.DockManagerImpl;
import com.intellij.ui.switcher.QuickActionProvider;
import com.intellij.ui.switcher.SwitchProvider;
import com.intellij.ui.switcher.SwitchTarget;
import com.intellij.ui.tabs.JBTabs;
import com.intellij.ui.tabs.TabInfo;
import com.intellij.ui.tabs.TabsListener;
import com.intellij.ui.tabs.impl.JBTabsImpl;
import com.intellij.util.NotNullFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.AbstractLayoutManager;
import com.intellij.util.ui.GraphicsUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Facade, ViewContextEx, PropertyChangeListener, SwitchProvider,
QuickActionProvider, DockContainer.Dialog {
public static final DataKey<RunnerContentUi> KEY = DataKey.create("DebuggerContentUI");
@NonNls private static final String LAYOUT = "Runner.Layout";
@NonNls private static final String SETTINGS = "XDebugger.Settings";
@NonNls private static final String VIEW_POPUP = "Runner.View.Popup";
@NonNls static final String VIEW_TOOLBAR = "Runner.View.Toolbar";
private ContentManager myManager;
private final RunnerLayout myLayoutSettings;
@NotNull private final ActionManager myActionManager;
private final String mySessionName;
private final MyComponent myComponent = new MyComponent();
private final Wrapper myToolbar = new Wrapper();
final MyDragOutDelegate myDragOutDelegate = new MyDragOutDelegate();
JBRunnerTabs myTabs;
private final Comparator<TabInfo> myTabsComparator = new Comparator<TabInfo>() {
@Override
public int compare(@NotNull final TabInfo o1, @NotNull final TabInfo o2) {
//noinspection ConstantConditions
TabImpl tab1 = getTabFor(o1);
TabImpl tab2 = getTabFor(o2);
int index1 = tab1 != null ? tab1.getIndex() : -1;
int index2 = tab2 != null ? tab2.getIndex() : -1;
return index1 - index2;
}
};
private final Project myProject;
private ActionGroup myTopActions = new DefaultActionGroup();
private final DefaultActionGroup myMinimizedViewActions = new DefaultActionGroup();
private final Map<GridImpl, Wrapper> myMinimizedButtonsPlaceholder = new HashMap<GridImpl, Wrapper>();
private final Map<GridImpl, Wrapper> myCommonActionsPlaceholder = new HashMap<GridImpl, Wrapper>();
private final Map<GridImpl, AnAction[]> myContextActions = new HashMap<GridImpl, AnAction[]>();
private boolean myUiLastStateWasRestored;
private final Set<Object> myRestoreStateRequestors = new HashSet<Object>();
private String myActionsPlace = ActionPlaces.UNKNOWN;
private final IdeFocusManager myFocusManager;
private boolean myMinimizeActionEnabled = true;
private boolean myMoveToGridActionEnabled = true;
private final RunnerLayoutUi myRunnerUi;
private final Map<String, LayoutAttractionPolicy> myAttractions = new HashMap<String, LayoutAttractionPolicy>();
private final Map<String, LayoutAttractionPolicy> myConditionAttractions = new HashMap<String, LayoutAttractionPolicy>();
private ActionGroup myTabPopupActions;
private ActionGroup myAdditionalFocusActions;
private final ActionCallback myInitialized = new ActionCallback();
private boolean myToDisposeRemovedContent = true;
private int myAttractionCount;
private ActionGroup myLeftToolbarActions;
private JBTabs myCurrentOver;
private Image myCurrentOverImg;
private TabInfo myCurrentOverInfo;
private MyDropAreaPainter myCurrentPainter;
private RunnerContentUi myOriginal;
private final CopyOnWriteArraySet<Listener> myDockingListeners = new CopyOnWriteArraySet<Listener>();
private final Set<RunnerContentUi> myChildren = new TreeSet<RunnerContentUi>(new Comparator<RunnerContentUi>() {
@Override
public int compare(@NotNull RunnerContentUi o1, @NotNull RunnerContentUi o2) {
return o1.myWindow - o2.myWindow;
}
});
private int myWindow;
private boolean myDisposing;
public RunnerContentUi(@NotNull Project project,
@NotNull RunnerLayoutUi ui,
@NotNull ActionManager actionManager,
@NotNull IdeFocusManager focusManager,
@NotNull RunnerLayout settings,
@NotNull String sessionName) {
myProject = project;
myRunnerUi = ui;
myLayoutSettings = settings;
myActionManager = actionManager;
mySessionName = sessionName;
myFocusManager = focusManager;
}
public RunnerContentUi(@NotNull RunnerContentUi ui, @NotNull RunnerContentUi original, int window) {
this(ui.myProject, ui.myRunnerUi, ui.myActionManager, ui.myFocusManager, ui.myLayoutSettings, ui.mySessionName);
myOriginal = original;
original.myChildren.add(this);
myWindow = window == 0 ? original.findFreeWindow() : window;
}
public void setTopActions(@NotNull final ActionGroup topActions, @NotNull String place) {
myTopActions = topActions;
myActionsPlace = place;
rebuildCommonActions();
}
public void setTabPopupActions(ActionGroup tabPopupActions) {
myTabPopupActions = tabPopupActions;
rebuildTabPopup();
}
public void setAdditionalFocusActions(final ActionGroup group) {
myAdditionalFocusActions = group;
rebuildTabPopup();
}
public void setLeftToolbar(ActionGroup group, String place) {
final ActionToolbar tb = myActionManager.createActionToolbar(place, group, false);
tb.setTargetComponent(myComponent);
myToolbar.setContent(tb.getComponent());
myLeftToolbarActions = group;
myComponent.revalidate();
myComponent.repaint();
}
public void initUi() {
if (myTabs != null) return;
myTabs = (JBRunnerTabs)new JBRunnerTabs(myProject, myActionManager, myFocusManager, this).setDataProvider(new DataProvider() {
@Override
public Object getData(@NonNls final String dataId) {
if (ViewContext.CONTENT_KEY.is(dataId)) {
TabInfo info = myTabs.getTargetInfo();
if (info != null) {
return getGridFor(info).getData(dataId);
}
}
else if (ViewContext.CONTEXT_KEY.is(dataId)) {
return RunnerContentUi.this;
}
return null;
}
}).setTabLabelActionsAutoHide(false).setProvideSwitchTargets(false).setInnerInsets(new Insets(0, 0, 0, 0))
.setToDrawBorderIfTabsHidden(false).setTabDraggingEnabled(isMoveToGridActionEnabled()).setUiDecorator(null).getJBTabs();
rebuildTabPopup();
myTabs.getPresentation().setPaintBorder(0, 0, 0, 0).setPaintFocus(false)
.setRequestFocusOnLastFocusedComponent(true);
myTabs.getComponent().setBackground(myToolbar.getBackground());
myTabs.getComponent().setBorder(new EmptyBorder(0, 2, 0, 0));
final NonOpaquePanel wrappper = new NonOpaquePanel(new BorderLayout(0, 0));
wrappper.add(myToolbar, BorderLayout.WEST);
wrappper.add(myTabs.getComponent(), BorderLayout.CENTER);
myComponent.setContent(wrappper);
myTabs.addListener(new TabsListener.Adapter() {
@Override
public void beforeSelectionChanged(TabInfo oldSelection, TabInfo newSelection) {
if (oldSelection != null && !isStateBeingRestored()) {
final GridImpl grid = getGridFor(oldSelection);
if (grid != null && getTabFor(grid) != null) {
grid.saveUiState();
}
}
}
@Override
public void tabsMoved() {
saveUiState();
}
@Override
public void selectionChanged(final TabInfo oldSelection, final TabInfo newSelection) {
if (!myTabs.getComponent().isShowing()) return;
if (newSelection != null) {
newSelection.stopAlerting();
getGridFor(newSelection).processAddToUi(false);
}
if (oldSelection != null) {
getGridFor(oldSelection).processRemoveFromUi();
}
}
});
myTabs.addTabMouseListener(new MouseAdapter() {
@Override
public void mousePressed(@NotNull MouseEvent e) {
if (UIUtil.isCloseClick(e)) {
final TabInfo tabInfo = myTabs.findInfo(e);
final GridImpl grid = tabInfo == null? null : getGridFor(tabInfo);
final Content[] contents = grid != null ? CONTENT_KEY.getData(grid) : null;
if (contents == null) return;
// see GridCellImpl.closeOrMinimize as well
if (CloseViewAction.isEnabled(contents)) {
CloseViewAction.perform(RunnerContentUi.this, contents[0]);
}
else if (MinimizeViewAction.isEnabled(RunnerContentUi.this, contents, ViewContext.TAB_TOOLBAR_PLACE)) {
grid.getCellFor(contents[0]).minimize(contents[0]);
}
}
}
});
if (myOriginal != null) {
final ContentManager manager = ContentFactory.SERVICE.getInstance().createContentManager(this, false, myProject);
Disposer.register((Disposable)myRunnerUi, manager);
manager.getComponent();
}
else {
final DockManager dockManager = DockManager.getInstance(myProject);
if (dockManager != null) { //default project
dockManager.register(this);
}
}
}
private void rebuildTabPopup() {
initUi();
myTabs.setPopupGroup(getCellPopupGroup(TAB_POPUP_PLACE), TAB_POPUP_PLACE, true);
for (GridImpl each : getGrids()) {
each.rebuildTabPopup();
}
}
@Override
public ActionGroup getCellPopupGroup(final String place) {
final ActionGroup original = myTabPopupActions != null? myTabPopupActions : (ActionGroup)myActionManager.getAction(VIEW_POPUP);
final ActionGroup focusPlaceholder = (ActionGroup)myActionManager.getAction("Runner.Focus");
DefaultActionGroup group = new DefaultActionGroup(VIEW_POPUP, original.isPopup());
final AnActionEvent event = new AnActionEvent(null, DataManager.getInstance().getDataContext(), place, new Presentation(),
ActionManager.getInstance(), 0);
final AnAction[] originalActions = original.getChildren(event);
for (final AnAction each : originalActions) {
if (each == focusPlaceholder) {
final AnAction[] focusActions = ((ActionGroup)each).getChildren(event);
for (AnAction eachFocus : focusActions) {
group.add(eachFocus);
}
if (myAdditionalFocusActions != null) {
for (AnAction action : myAdditionalFocusActions.getChildren(event)) {
group.add(action);
}
}
}
else {
group.add(each);
}
}
return group;
}
@Override
public boolean isOriginal() {
return myOriginal == null;
}
@Override
public int getWindow() {
return myWindow;
}
@Override
public void propertyChange(@NotNull final PropertyChangeEvent evt) {
Content content = (Content)evt.getSource();
final GridImpl grid = getGridFor(content, false);
if (grid == null) return;
final GridCellImpl cell = grid.findCell(content);
if (cell == null) return;
final String property = evt.getPropertyName();
if (Content.PROP_ALERT.equals(property)) {
attract(content, true);
}
else if (Content.PROP_DISPLAY_NAME.equals(property)
|| Content.PROP_ICON.equals(property)
|| Content.PROP_ACTIONS.equals(property)
|| Content.PROP_DESCRIPTION.equals(property)) {
cell.updateTabPresentation(content);
updateTabsUI(false);
}
}
public void processBounce(Content content, final boolean activate) {
final GridImpl grid = getGridFor(content, false);
if (grid == null) return;
final GridCellImpl cell = grid.findCell(content);
if (cell == null) return;
final TabInfo tab = myTabs.findInfo(grid);
if (tab == null) return;
if (getSelectedGrid() != grid) {
tab.setAlertIcon(content.getAlertIcon());
if (activate) {
tab.fireAlert();
}
else {
tab.stopAlerting();
}
}
else {
grid.processAlert(content, activate);
}
}
@Override
public ActionCallback detachTo(int window, GridCell cell) {
if (myOriginal != null) {
return myOriginal.detachTo(window, cell);
}
RunnerContentUi target = null;
if (window > 0) {
for (RunnerContentUi child : myChildren) {
if (child.myWindow == window) {
target = child;
break;
}
}
}
final GridCellImpl gridCell = (GridCellImpl)cell;
final Content[] contents = gridCell.getContents();
storeDefaultIndices(contents);
for (Content content : contents) {
content.putUserData(RunnerLayout.DROP_INDEX, getStateFor(content).getTab().getIndex());
}
Dimension size = gridCell.getSize();
if (size == null) {
size = new Dimension(200, 200);
}
final DockableGrid content = new DockableGrid(null, null, size, Arrays.asList(contents), window);
if (target != null) {
target.add(content, null);
} else {
Point location = gridCell.getLocation();
if (location == null) {
location = getComponent().getLocationOnScreen();
}
location.translate(size.width / 2, size.height / 2);
getDockManager().createNewDockContainerFor(content, new RelativePoint(location));
}
return new ActionCallback.Done();
}
private void storeDefaultIndices(@NotNull Content[] contents) {
//int i = 0;
for (Content content : contents) {
content.putUserData(RunnerLayout.DEFAULT_INDEX, getStateFor(content).getTab().getDefaultIndex());
//content.putUserData(CONTENT_NUMBER, i++);
}
}
@Override
public RelativeRectangle getAcceptArea() {
return new RelativeRectangle(myTabs.getComponent());
}
@Override
public RelativeRectangle getAcceptAreaFallback() {
return getAcceptArea();
}
@NotNull
@Override
public ContentResponse getContentResponse(@NotNull DockableContent content, RelativePoint point) {
if (!(content instanceof DockableGrid)) {
return ContentResponse.DENY;
}
final RunnerContentUi ui = ((DockableGrid)content).getOriginalRunnerUi();
return ui.getProject() == myProject && ui.mySessionName.equals(mySessionName) ? ContentResponse.ACCEPT_MOVE : ContentResponse.DENY;
}
@Override
public JComponent getComponent() {
initUi();
return myComponent;
}
@Override
public JComponent getContainerComponent() {
initUi();
return myManager.getComponent();
}
@Override
public void add(@NotNull DockableContent dockable, RelativePoint dropTarget) {
final DockableGrid dockableGrid = (DockableGrid)dockable;
final RunnerContentUi prev = dockableGrid.getRunnerUi();
saveUiState();
final List<Content> contents = dockableGrid.getContents();
final boolean wasRestoring = myOriginal != null && myOriginal.isStateBeingRestored();
setStateIsBeingRestored(true, this);
try {
final Point point = dropTarget != null ? dropTarget.getPoint(myComponent) : null;
boolean hadGrid = !myTabs.shouldAddToGlobal(point);
for (Content content : contents) {
final View view = getStateFor(content);
if (view.isMinimizedInGrid()) continue;
prev.myManager.removeContent(content, false);
myManager.removeContent(content, false);
if (hadGrid && !wasRestoring) {
view.assignTab(getTabFor(getSelectedGrid()));
view.setPlaceInGrid(calcPlaceInGrid(point, myComponent.getSize()));
}
else if (contents.size() == 1 && !wasRestoring) {
view.assignTab(null);
view.setPlaceInGrid(myLayoutSettings.getDefaultGridPlace(content));
}
view.setWindow(myWindow);
myManager.addContent(content);
}
} finally {
setStateIsBeingRestored(false, this);
}
saveUiState();
updateTabsUI(true);
}
@Override
public void closeAll() {
final Content[] contents = myManager.getContents();
if (myOriginal != null) {
for (Content content : contents) {
getStateFor(content).setWindow(0);
myOriginal.myManager.addContent(content);
GridCell cell = myOriginal.findCellFor(content);
if (cell != null) {
myOriginal.restoreContent(content.getUserData(ViewImpl.ID));
cell.minimize(content);
}
}
}
myManager.removeAllContents(false);
}
@Override
public void addListener(final Listener listener, Disposable parent) {
myDockingListeners.add(listener);
Disposer.register(parent, new Disposable() {
@Override
public void dispose() {
myDockingListeners.remove(listener);
}
});
}
@Override
public boolean isEmpty() {
return myTabs.isEmptyVisible() || myDisposing;
}
@Override
public Image startDropOver(@NotNull DockableContent content, RelativePoint point) {
return null;
}
@Override
public Image processDropOver(@NotNull DockableContent dockable, RelativePoint dropTarget) {
JBTabs current = getTabsAt(dockable, dropTarget);
if (myCurrentOver != null && myCurrentOver != current) {
resetDropOver(dockable);
}
if (myCurrentOver == null && current != null) {
myCurrentOver = current;
Presentation presentation = dockable.getPresentation();
myCurrentOverInfo = new TabInfo(new JLabel("")).setText(presentation.getText()).setIcon(presentation.getIcon());
myCurrentOverImg = myCurrentOver.startDropOver(myCurrentOverInfo, dropTarget);
}
if (myCurrentOver != null) {
myCurrentOver.processDropOver(myCurrentOverInfo, dropTarget);
}
if (myCurrentPainter == null) {
myCurrentPainter = new MyDropAreaPainter();
IdeGlassPaneUtil.find(myComponent).addPainter(myComponent, myCurrentPainter, this);
}
myCurrentPainter.processDropOver(this, dockable, dropTarget);
return myCurrentOverImg;
}
@NotNull
private static PlaceInGrid calcPlaceInGrid(Point point, Dimension size) {
// 1/3 (left) | (center/bottom) | 1/3 (right)
if (point.x < size.width / 3) return PlaceInGrid.left;
if (point.x > size.width * 2 / 3) return PlaceInGrid.right;
// 3/4 (center with tab titles) | 1/4 (bottom)
if (point.y > size.height * 3 / 4) return PlaceInGrid.bottom;
return PlaceInGrid.center;
}
@Nullable
private JBTabs getTabsAt(DockableContent content, RelativePoint point) {
if (content instanceof DockableGrid) {
final Point p = point.getPoint(getComponent());
Component c = SwingUtilities.getDeepestComponentAt(getComponent(), p.x, p.y);
while (c != null) {
if (c instanceof JBRunnerTabs) {
return (JBTabs)c;
}
c = c.getParent();
}
}
return null;
}
@Override
public void resetDropOver(@NotNull DockableContent content) {
if (myCurrentOver != null) {
myCurrentOver.resetDropOver(myCurrentOverInfo);
myCurrentOver = null;
myCurrentOverInfo = null;
myCurrentOverImg = null;
IdeGlassPaneUtil.find(myComponent).removePainter(myCurrentPainter);
myCurrentPainter = null;
}
}
@Override
public boolean isDisposeWhenEmpty() {
return myOriginal != null;
}
@Override
public boolean isCycleRoot() {
return false;
}
@Override
public void setManager(@NotNull final ContentManager manager) {
assert myManager == null;
myManager = manager;
myManager.addContentManagerListener(new ContentManagerListener() {
@Override
public void contentAdded(final ContentManagerEvent event) {
initUi();
GridImpl grid = getGridFor(event.getContent(), true);
if (grid == null) {
return;
}
grid.add(event.getContent());
if (getSelectedGrid() == grid) {
grid.processAddToUi(false);
}
if (myManager.getComponent().isShowing() && !isStateBeingRestored()) {
grid.restoreLastUiState();
}
updateTabsUI(false);
event.getContent().addPropertyChangeListener(RunnerContentUi.this);
fireContentOpened(event.getContent());
}
@Override
public void contentRemoved(final ContentManagerEvent event) {
event.getContent().removePropertyChangeListener(RunnerContentUi.this);
GridImpl grid = (GridImpl)findGridFor(event.getContent());
if (grid != null) {
grid.remove(event.getContent());
grid.processRemoveFromUi();
removeGridIfNeeded(grid);
}
updateTabsUI(false);
fireContentClosed(event.getContent());
}
@Override
public void contentRemoveQuery(final ContentManagerEvent event) {
}
@Override
public void selectionChanged(final ContentManagerEvent event) {
if (isStateBeingRestored()) return;
if (event.getOperation() == ContentManagerEvent.ContentOperation.add) {
select(event.getContent(), false);
}
}
});
}
@Nullable
private GridImpl getSelectedGrid() {
TabInfo selection = myTabs.getSelectedInfo();
return selection != null ? getGridFor(selection) : null;
}
private void removeGridIfNeeded(GridImpl grid) {
if (grid.isEmpty()) {
myTabs.removeTab(myTabs.findInfo(grid));
myMinimizedButtonsPlaceholder.remove(grid);
myCommonActionsPlaceholder.remove(grid);
Disposer.dispose(grid);
}
}
@Nullable
private GridImpl getGridFor(@NotNull Content content, boolean createIfMissing) {
GridImpl grid = (GridImpl)findGridFor(content);
if (grid != null || !createIfMissing) return grid;
grid = new GridImpl(this, mySessionName);
if (myCurrentOver != null || myOriginal != null) {
Integer forcedDropIndex = content.getUserData(RunnerLayout.DROP_INDEX);
final int index = myTabs.getDropInfoIndex() + (myOriginal != null ? myOriginal.getTabOffsetFor(this) : 0);
final int dropIndex = forcedDropIndex != null ? forcedDropIndex : index;
if (forcedDropIndex == null) {
moveFollowingTabs(dropIndex);
}
final int defaultIndex = content.getUserData(RunnerLayout.DEFAULT_INDEX);
final TabImpl tab = myLayoutSettings.getOrCreateTab(forcedDropIndex != null ? forcedDropIndex : -1);
tab.setDefaultIndex(defaultIndex);
tab.setIndex(dropIndex);
getStateFor(content).assignTab(tab);
content.putUserData(RunnerLayout.DROP_INDEX, null);
content.putUserData(RunnerLayout.DEFAULT_INDEX, null);
}
TabInfo tab = new TabInfo(grid).setObject(getStateFor(content).getTab()).setText("Tab");
Wrapper left = new Wrapper();
myCommonActionsPlaceholder.put(grid, left);
Wrapper minimizedToolbar = new Wrapper();
myMinimizedButtonsPlaceholder.put(grid, minimizedToolbar);
final Wrapper searchComponent = new Wrapper();
if (content.getSearchComponent() != null) {
searchComponent.setContent(content.getSearchComponent());
}
TwoSideComponent right = new TwoSideComponent(searchComponent, minimizedToolbar);
NonOpaquePanel sideComponent = new TwoSideComponent(left, right);
tab.setSideComponent(sideComponent);
tab.setTabLabelActions((ActionGroup)myActionManager.getAction(VIEW_TOOLBAR), TAB_TOOLBAR_PLACE);
myTabs.addTab(tab);
myTabs.sortTabs(myTabsComparator);
return grid;
}
private void moveFollowingTabs(int index) {
if (myOriginal != null) {
myOriginal.moveFollowingTabs(index);
return;
}
moveFollowingTabs(index, myTabs);
for (RunnerContentUi child : myChildren) {
moveFollowingTabs(index, child.myTabs);
}
}
public ActionGroup getSettingsActions() {
return (ActionGroup)myActionManager.getAction(SETTINGS);
}
public ContentManager getContentManager(Content content) {
if (hasContent(myManager, content)) {
return myManager;
}
for (RunnerContentUi child : myChildren) {
if (hasContent(child.myManager, content)) {
return child.myManager;
}
}
return myManager;
}
private static boolean hasContent(ContentManager manager, Content content) {
for (Content c : manager.getContents()) {
if (c == content) {
return true;
}
}
return false;
}
private static void moveFollowingTabs(int index, final JBRunnerTabs tabs) {
for (TabInfo info : tabs.getTabs()) {
TabImpl tab = getTabFor(info);
if (tab != null) {
int tabIndex = tab.getIndex();
if (tabIndex >= index) {
tab.setIndex(tabIndex + 1);
}
}
}
}
private int getTabOffsetFor(RunnerContentUi ui) {
int offset = myTabs.getTabCount();
for (RunnerContentUi child : myChildren) {
if (child == ui) break;
offset += child.myTabs.getTabCount();
}
return offset;
}
@Override
@Nullable
public GridCell findCellFor(@NotNull final Content content) {
GridImpl cell = getGridFor(content, false);
return cell != null ? cell.getCellFor(content) : null;
}
private boolean rebuildToolbar() {
boolean hasToolbarContent = rebuildCommonActions();
hasToolbarContent |= rebuildMinimizedActions();
return hasToolbarContent;
}
private boolean rebuildCommonActions() {
boolean hasToolbarContent = false;
for (Map.Entry<GridImpl, Wrapper> entry : myCommonActionsPlaceholder.entrySet()) {
Wrapper eachPlaceholder = entry.getValue();
List<Content> contentList = entry.getKey().getContents();
Set<Content> contents = new HashSet<Content>();
contents.addAll(contentList);
DefaultActionGroup groupToBuild;
JComponent contextComponent = null;
if (isHorizontalToolbar() && contents.size() == 1) {
Content content = contentList.get(0);
groupToBuild = new DefaultActionGroup();
if (content.getActions() != null) {
groupToBuild.addAll(content.getActions());
groupToBuild.addSeparator();
contextComponent = content.getActionsContextComponent();
}
groupToBuild.addAll(myTopActions);
}
else {
final DefaultActionGroup group = new DefaultActionGroup();
group.addAll(myTopActions);
groupToBuild = group;
}
final AnAction[] actions = groupToBuild.getChildren(null);
if (!Arrays.equals(actions, myContextActions.get(entry.getKey()))) {
ActionToolbar tb = myActionManager.createActionToolbar(myActionsPlace, groupToBuild, true);
tb.getComponent().setBorder(null);
tb.setTargetComponent(contextComponent);
eachPlaceholder.setContent(tb.getComponent());
}
if (groupToBuild.getChildrenCount() > 0) {
hasToolbarContent = true;
}
myContextActions.put(entry.getKey(), actions);
}
return hasToolbarContent;
}
private boolean rebuildMinimizedActions() {
for (Map.Entry<GridImpl, Wrapper> entry : myMinimizedButtonsPlaceholder.entrySet()) {
Wrapper eachPlaceholder = entry.getValue();
ActionToolbar tb = myActionManager.createActionToolbar(ActionPlaces.DEBUGGER_TOOLBAR, myMinimizedViewActions, true);
tb.getComponent().setBorder(null);
tb.setReservePlaceAutoPopupIcon(false);
JComponent minimized = tb.getComponent();
eachPlaceholder.setContent(minimized);
}
myTabs.getComponent().revalidate();
myTabs.getComponent().repaint();
return myMinimizedViewActions.getChildrenCount() > 0;
}
private void updateTabsUI(final boolean validateNow) {
boolean hasToolbarContent = rebuildToolbar();
Set<String> usedNames = new HashSet<String>();
List<TabInfo> tabs = myTabs.getTabs();
for (TabInfo each : tabs) {
hasToolbarContent |= updateTabUI(each, usedNames);
}
int tabsCount = tabs.size();
for (RunnerContentUi child : myChildren) {
tabsCount += child.myTabs.getTabCount();
}
myTabs.getPresentation().setHideTabs(!hasToolbarContent && tabsCount <= 1 && myOriginal == null);
myTabs.updateTabActions(validateNow);
if (validateNow) {
myTabs.sortTabs(myTabsComparator);
}
}
private boolean updateTabUI(TabInfo tab, Set<String> usedNames) {
TabImpl t = getTabFor(tab);
if (t == null) {
return false;
}
Icon icon = t.getIcon();
GridImpl grid = getGridFor(tab);
boolean hasToolbarContent = grid.updateGridUI();
List<Content> contents = grid.getContents();
String title = contents.size() > 1 ? t.getDisplayName() : null;
if (title == null) {
final String name = myLayoutSettings.getDefaultDisplayName(t.getDefaultIndex());
if (name != null && contents.size() > 1 && !usedNames.contains(name)) {
title = name;
} else {
title = StringUtil.join(contents, new NotNullFunction<Content, String>() {
@NotNull
@Override
public String fun(Content dom) {
return dom.getTabName();
}
}, " | ");
}
}
usedNames.add(title);
boolean hidden = true;
for (Content content : contents) {
if (!grid.isMinimized(content)) {
hidden = false;
break;
}
}
tab.setHidden(hidden);
if (icon == null && contents.size() == 1) {
icon = contents.get(0).getIcon();
}
tab.setDragOutDelegate(myTabs.getTabs().size() > 1 || !isOriginal() ? myDragOutDelegate : null);
Tab gridTab = grid.getTab();
tab.setText(title).setIcon(gridTab != null && gridTab.isDefault() && contents.size() > 1 ? null : icon);
return hasToolbarContent;
}
private ActionCallback restoreLastUiState() {
if (isStateBeingRestored()) return new ActionCallback.Rejected();
try {
setStateIsBeingRestored(true, this);
List<TabInfo> tabs = new ArrayList<TabInfo>();
tabs.addAll(myTabs.getTabs());
final ActionCallback result = new ActionCallback(tabs.size());
for (TabInfo each : tabs) {
getGridFor(each).restoreLastUiState().notifyWhenDone(result);
}
return result;
}
finally {
setStateIsBeingRestored(false, this);
}
}
@Override
public void saveUiState() {
if (isStateBeingRestored()) return;
if (myOriginal != null) {
myOriginal.saveUiState();
return;
}
int offset = updateTabsIndices(myTabs, 0);
for (RunnerContentUi child : myChildren) {
offset = updateTabsIndices(child.myTabs, offset);
}
doSaveUiState();
}
private static int updateTabsIndices(final JBRunnerTabs tabs, int offset) {
for (TabInfo each : tabs.getTabs()) {
final int index = tabs.getIndexOf(each);
final TabImpl tab = getTabFor(each);
if (tab != null) tab.setIndex(index >= 0 ? index + offset : index);
}
return offset + tabs.getTabCount();
}
private void doSaveUiState() {
if (isStateBeingRestored()) return;
for (TabInfo each : myTabs.getTabs()) {
GridImpl eachGrid = getGridFor(each);
eachGrid.saveUiState();
}
for (RunnerContentUi child : myChildren) {
child.doSaveUiState();
}
}
@Override
@Nullable
public Tab getTabFor(final Grid grid) {
TabInfo info = myTabs.findInfo((Component)grid);
return getTabFor(info);
}
@Override
public void showNotify() {
final Window window = SwingUtilities.getWindowAncestor(myComponent);
if (window instanceof IdeFrame.Child) {
((IdeFrame.Child)window).setFrameTitle(mySessionName);
}
}
@Override
public void hideNotify() {}
@Nullable
private static TabImpl getTabFor(@Nullable final TabInfo tab) {
if (tab == null) {
return null;
}
return (TabImpl)tab.getObject();
}
private static GridImpl getGridFor(TabInfo tab) {
return (GridImpl)tab.getComponent();
}
@Override
@Nullable
public Grid findGridFor(@NotNull Content content) {
TabImpl tab = (TabImpl)getStateFor(content).getTab();
for (TabInfo each : myTabs.getTabs()) {
TabImpl t = getTabFor(each);
if (t != null && t.equals(tab)) return getGridFor(each);
}
return null;
}
private ArrayList<GridImpl> getGrids() {
ArrayList<GridImpl> result = new ArrayList<GridImpl>();
for (TabInfo each : myTabs.getTabs()) {
result.add(getGridFor(each));
}
return result;
}
public void setHorizontalToolbar(final boolean state) {
myLayoutSettings.setToolbarHorizontal(state);
for (GridImpl each : getGrids()) {
each.setToolbarHorizontal(state);
}
myContextActions.clear();
updateTabsUI(false);
}
@Override
public boolean isSingleSelection() {
return false;
}
@Override
public boolean isToSelectAddedContent() {
return false;
}
@Override
public boolean canBeEmptySelection() {
return true;
}
@Override
public void beforeDispose() {
if (myOriginal != null) {
myDisposing = true;
fireContentClosed(null);
}
}
@Override
public boolean canChangeSelectionTo(@NotNull Content content, boolean implicit) {
if (implicit) {
GridImpl grid = getGridFor(content, false);
if (grid != null) {
return !grid.isMinimized(content);
}
}
return true;
}
@NotNull
@Override
public String getCloseActionName() {
return UIBundle.message("tabbed.pane.close.tab.action.name");
}
@NotNull
@Override
public String getCloseAllButThisActionName() {
return UIBundle.message("tabbed.pane.close.all.tabs.but.this.action.name");
}
@NotNull
@Override
public String getPreviousContentActionName() {
return "Select Previous Tab";
}
@NotNull
@Override
public String getNextContentActionName() {
return "Select Next Tab";
}
@Override
public void dispose() {
if (myOriginal != null) {
myOriginal.myChildren.remove(this);
}
myMinimizedButtonsPlaceholder.clear();
myCommonActionsPlaceholder.clear();
myContextActions.clear();
myOriginal = null;
myTopActions = null;
myAdditionalFocusActions = null;
myLeftToolbarActions = null;
}
@Override
public void restoreLayout() {
final RunnerContentUi[] children = myChildren.toArray(new RunnerContentUi[myChildren.size()]);
final List<Content> contents = new ArrayList<Content>();
Collections.addAll(contents, myManager.getContents());
for (RunnerContentUi child : children) {
Collections.addAll(contents, child.myManager.getContents());
}
for (AnAction action : myMinimizedViewActions.getChildren(null)) {
final Content content = ((RestoreViewAction)action).getContent();
contents.add(content);
}
Content[] all = contents.toArray(new Content[contents.size()]);
Arrays.sort(all, new Comparator<Content>() {
@Override
public int compare(@NotNull Content content, @NotNull Content content1) {
final int i = getStateFor(content).getTab().getDefaultIndex();
final int i1 = getStateFor(content1).getTab().getDefaultIndex();
return i - i1;
}
});
setStateIsBeingRestored(true, this);
try {
for (RunnerContentUi child : children) {
child.myManager.removeAllContents(false);
}
myManager.removeAllContents(false);
myMinimizedViewActions.removeAll();
}
finally {
setStateIsBeingRestored(false, this);
}
myLayoutSettings.resetToDefault();
for (Content each : all) {
myManager.addContent(each);
}
updateTabsUI(true);
}
@Override
public boolean isStateBeingRestored() {
return !myRestoreStateRequestors.isEmpty();
}
@Override
public void setStateIsBeingRestored(final boolean restoredNow, final Object requestor) {
if (restoredNow) {
myRestoreStateRequestors.add(requestor);
}
else {
myRestoreStateRequestors.remove(requestor);
}
}
public ActionGroup getLayoutActions() {
return (ActionGroup)myActionManager.getAction(LAYOUT);
}
public void updateActionsImmediately() {
if (myToolbar.getTargetComponent() instanceof ActionToolbar) {
((ActionToolbar)myToolbar.getTargetComponent()).updateActionsImmediately();
}
}
public void setMinimizeActionEnabled(final boolean enabled) {
myMinimizeActionEnabled = enabled;
}
@SuppressWarnings("SpellCheckingInspection")
public void setMovetoGridActionEnabled(final boolean enabled) {
myMoveToGridActionEnabled = enabled;
}
@Override
public boolean isMinimizeActionEnabled() {
return myMinimizeActionEnabled && myOriginal == null;
}
@Override
public boolean isMoveToGridActionEnabled() {
return myMoveToGridActionEnabled;
}
public void setPolicy(String contentId, final LayoutAttractionPolicy policy) {
myAttractions.put(contentId, policy);
}
public void setConditionPolicy(final String condition, final LayoutAttractionPolicy policy) {
myConditionAttractions.put(condition, policy);
}
private static LayoutAttractionPolicy getOrCreatePolicyFor(String key,
Map<String, LayoutAttractionPolicy> map,
LayoutAttractionPolicy defaultPolicy) {
LayoutAttractionPolicy policy = map.get(key);
if (policy == null) {
policy = defaultPolicy;
map.put(key, policy);
}
return policy;
}
@Nullable
public Content findContent(final String key) {
final ContentManager manager = getContentManager();
if (manager == null || key == null) return null;
Content[] contents = manager.getContents();
for (Content content : contents) {
String kind = content.getUserData(ViewImpl.ID);
if (key.equals(kind)) {
return content;
}
}
return null;
}
public void restoreContent(final String key) {
for (AnAction action : myMinimizedViewActions.getChildren(null)) {
Content content = ((RestoreViewAction)action).getContent();
if (key.equals(content.getUserData(ViewImpl.ID))) {
action.actionPerformed(null);
return;
}
}
}
public void setToDisposeRemovedContent(final boolean toDispose) {
myToDisposeRemovedContent = toDispose;
}
@Override
public boolean isToDisposeRemovedContent() {
return myToDisposeRemovedContent;
}
private static class MyDropAreaPainter extends AbstractPainter {
private Shape myBoundingBox;
private final Color myColor = ColorUtil.mix(JBColor.BLUE, JBColor.WHITE, .3);
@Override
public boolean needsRepaint() {
return myBoundingBox != null;
}
@Override
public void executePaint(Component component, Graphics2D g) {
if (myBoundingBox == null) return;
GraphicsUtil.setupAAPainting(g);
g.setColor(ColorUtil.toAlpha(myColor, 200));
g.setStroke(new BasicStroke(2));
g.draw(myBoundingBox);
g.setColor(ColorUtil.toAlpha(myColor, 40));
g.fill(myBoundingBox);
}
private void processDropOver(RunnerContentUi ui, DockableContent dockable, RelativePoint dropTarget) {
myBoundingBox = null;
setNeedsRepaint(true);
if (!(dockable instanceof DockableGrid)) return;
JComponent component = ui.myComponent;
Point point = dropTarget != null ? dropTarget.getPoint(component) : null;
// do not paint anything if adding to the top
if (ui.myTabs.shouldAddToGlobal(point)) return;
// calc target place-in-grid
PlaceInGrid targetPlaceInGrid = null;
for (Content c : ((DockableGrid)dockable).getContents()) {
View view = ui.getStateFor(c);
if (view.isMinimizedInGrid()) continue;
PlaceInGrid defaultGridPlace = ui.getLayoutSettings().getDefaultGridPlace(c);
targetPlaceInGrid = point == null ? defaultGridPlace : calcPlaceInGrid(point, component.getSize());
break;
}
if (targetPlaceInGrid == null) return;
// calc the default rectangle for the targetPlaceInGrid "area"
Dimension size = component.getSize();
Rectangle r = new Rectangle(size);
switch (targetPlaceInGrid) {
case left:
r.width /= 3;
break;
case center:
r.width /= 3;
r.x += r.width;
break;
case right:
r.width /= 3;
r.x += 2 * r.width;
break;
case bottom:
r.height /= 4;
r.y += 3 * r.height;
break;
}
// adjust the rectangle if the target grid cell is already present and showing
for (Content c : ui.getContentManager().getContents()) {
GridCell cellFor = ui.findCellFor(c);
PlaceInGrid placeInGrid = cellFor == null? null : ((GridCellImpl)cellFor).getPlaceInGrid();
if (placeInGrid != targetPlaceInGrid) continue;
Wrapper wrapper = UIUtil.getParentOfType(Wrapper.class, c.getComponent());
JComponent cellWrapper = wrapper == null ? null : (JComponent)wrapper.getParent();
if (cellWrapper == null || !cellWrapper.isShowing()) continue;
r = new RelativeRectangle(cellWrapper).getRectangleOn(component);
break;
}
myBoundingBox = new RoundRectangle2D.Double(r.x, r.y, r.width, r.height, 16, 16);
}
}
private class MyComponent extends Wrapper.FocusHolder implements DataProvider, QuickActionProvider {
private boolean myWasEverAdded;
public MyComponent() {
setOpaque(true);
setFocusCycleRoot(true);
setBorder(new ToolWindow.Border(true, false, true, true));
}
@Override
@Nullable
public Object getData(@NonNls final String dataId) {
if (KEY.is(dataId)) {
return RunnerContentUi.this;
}
ContentManager originalContentManager = myOriginal == null ? null : myOriginal.getContentManager();
JComponent originalContentComponent = originalContentManager == null ? null : originalContentManager.getComponent();
if (originalContentComponent instanceof DataProvider) {
return ((DataProvider)originalContentComponent).getData(dataId);
}
return null;
}
@SuppressWarnings("NullableProblems")
@Override
public String getName() {
return RunnerContentUi.this.getName();
}
@Override
public List<AnAction> getActions(boolean originalProvider) {
return RunnerContentUi.this.getActions(originalProvider);
}
@Override
public JComponent getComponent() {
return RunnerContentUi.this.getComponent();
}
@Override
public boolean isCycleRoot() {
return RunnerContentUi.this.isCycleRoot();
}
@Override
public void addNotify() {
super.addNotify();
if (!myUiLastStateWasRestored && myOriginal == null) {
myUiLastStateWasRestored = true;
// [kirillk] this is done later since restoreUiState doesn't work properly in the addNotify call chain
//todo to investigate and to fix (may cause extra flickering)
//noinspection SSBasedInspection
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
restoreLastUiState().doWhenDone(new Runnable() {
@Override
public void run() {
if (!myWasEverAdded) {
myWasEverAdded = true;
attractOnStartup();
myInitialized.setDone();
}
}
});
}
});
}
}
@Override
public void removeNotify() {
super.removeNotify();
if (!ScreenUtil.isStandardAddRemoveNotify(this))
return;
if (Disposer.isDisposed(RunnerContentUi.this)) return;
saveUiState();
}
}
@SuppressWarnings({"SSBasedInspection"})
// [kirillk] this is done later since "startup" attractions should be done gently, only if no explicit calls are done
private void attractOnStartup() {
final int currentCount = myAttractionCount;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (currentCount < myAttractionCount) return;
attractByCondition(LayoutViewOptions.STARTUP, false);
}
});
}
public void attract(final Content content, boolean afterInitialized) {
processAttraction(content.getUserData(ViewImpl.ID), myAttractions, new LayoutAttractionPolicy.Bounce(), afterInitialized, true);
}
public void attractByCondition(@NotNull String condition, boolean afterInitialized) {
processAttraction(myLayoutSettings.getToFocus(condition), myConditionAttractions, myLayoutSettings.getAttractionPolicy(condition),
afterInitialized, true);
}
public void clearAttractionByCondition(String condition, boolean afterInitialized) {
processAttraction(myLayoutSettings.getToFocus(condition), myConditionAttractions, new LayoutAttractionPolicy.FocusOnce(),
afterInitialized, false);
}
private void processAttraction(final String contentId,
final Map<String, LayoutAttractionPolicy> policyMap,
final LayoutAttractionPolicy defaultPolicy,
final boolean afterInitialized,
final boolean activate) {
IdeFocusManager.getInstance(getProject()).doWhenFocusSettlesDown(new Runnable() {
@Override
public void run() {
myInitialized.processOnDone(new Runnable() {
@Override
public void run() {
Content content = findContent(contentId);
if (content == null) return;
final LayoutAttractionPolicy policy = getOrCreatePolicyFor(contentId, policyMap, defaultPolicy);
if (activate) {
// See IDEA-93683, bounce attraction should not disable further focus attraction
if (!(policy instanceof LayoutAttractionPolicy.Bounce)) {
myAttractionCount++;
}
policy.attract(content, myRunnerUi);
}
else {
policy.clearAttraction(content, myRunnerUi);
}
}
}, afterInitialized);
}
});
}
public static boolean ensureValid(JComponent c) {
if (c.getRootPane() == null) return false;
Container eachParent = c.getParent();
while (eachParent != null && eachParent.isValid()) {
eachParent = eachParent.getParent();
}
if (eachParent == null) {
eachParent = c.getRootPane();
}
eachParent.validate();
return true;
}
public ContentUI getContentUI() {
return this;
}
@Override
public void minimize(final Content content, final CellTransform.Restore restore) {
final Ref<AnAction> restoreAction = new Ref<AnAction>();
myManager.removeContent(content, false);
restoreAction.set(new RestoreViewAction(content, new CellTransform.Restore() {
@Override
public ActionCallback restoreInGrid() {
myMinimizedViewActions.remove(restoreAction.get());
final GridImpl grid = getGridFor(content, false);
if (grid == null) {
getStateFor(content).assignTab(myLayoutSettings.getOrCreateTab(-1));
} else {
//noinspection ConstantConditions
((GridCellImpl)findCellFor(content)).restore(content);
}
getStateFor(content).setMinimizedInGrid(false);
myManager.addContent(content);
saveUiState();
select(content, true);
updateTabsUI(false);
return new ActionCallback.Done();
}
}));
myMinimizedViewActions.add(restoreAction.get());
saveUiState();
updateTabsUI(false);
}
@Override
public Project getProject() {
return myProject;
}
@Override
public CellTransform.Facade getCellTransform() {
return this;
}
@Override
public ContentManager getContentManager() {
return myManager;
}
@NotNull
@Override
public ActionManager getActionManager() {
return myActionManager;
}
@Override
public RunnerLayout getLayoutSettings() {
return myLayoutSettings;
}
@Override
public View getStateFor(@NotNull final Content content) {
return myLayoutSettings.getStateFor(content);
}
public boolean isHorizontalToolbar() {
return myLayoutSettings.isToolbarHorizontal();
}
@Override
public ActionCallback select(final Content content, final boolean requestFocus) {
final GridImpl grid = (GridImpl)findGridFor(content);
if (grid == null) return new ActionCallback.Done();
final TabInfo info = myTabs.findInfo(grid);
if (info == null) return new ActionCallback.Done();
final ActionCallback result = new ActionCallback();
myTabs.select(info, false).doWhenDone(new Runnable() {
@Override
public void run() {
grid.select(content, requestFocus).notifyWhenDone(result);
}
});
return result;
}
@Override
public void validate(Content content, final ActiveRunnable toRestore) {
final TabInfo current = myTabs.getSelectedInfo();
myTabs.getPresentation().setPaintBlocked(true, true);
select(content, false).doWhenDone(new Runnable() {
@Override
public void run() {
myTabs.getComponent().validate();
toRestore.run().doWhenDone(new Runnable() {
@Override
public void run() {
assert current != null;
myTabs.select(current, true);
myTabs.getPresentation().setPaintBlocked(false, true);
}
});
}
});
}
private static class TwoSideComponent extends NonOpaquePanel {
private TwoSideComponent(JComponent left, JComponent right) {
setLayout(new CommonToolbarLayout(left, right));
add(left);
add(right);
}
}
private static class CommonToolbarLayout extends AbstractLayoutManager {
private final JComponent myLeft;
private final JComponent myRight;
public CommonToolbarLayout(final JComponent left, final JComponent right) {
myLeft = left;
myRight = right;
}
@Override
public Dimension preferredLayoutSize(@NotNull final Container parent) {
Dimension size = new Dimension();
Dimension leftSize = myLeft.getPreferredSize();
Dimension rightSize = myRight.getPreferredSize();
size.width = leftSize.width + rightSize.width;
size.height = Math.max(leftSize.height, rightSize.height);
return size;
}
@Override
public void layoutContainer(@NotNull final Container parent) {
Dimension size = parent.getSize();
Dimension prefSize = parent.getPreferredSize();
if (prefSize.width <= size.width) {
myLeft.setBounds(0, 0, myLeft.getPreferredSize().width, parent.getHeight());
Dimension rightSize = myRight.getPreferredSize();
myRight.setBounds(parent.getWidth() - rightSize.width, 0, rightSize.width, parent.getHeight());
}
else {
Dimension leftMinSize = myLeft.getMinimumSize();
Dimension rightMinSize = myRight.getMinimumSize();
int delta = (prefSize.width - size.width) / 2;
myLeft.setBounds(0, 0, myLeft.getPreferredSize().width - delta, parent.getHeight());
int rightX = (int)myLeft.getBounds().getMaxX();
int rightWidth = size.width - rightX;
if (rightWidth < rightMinSize.width) {
Dimension leftSize = myLeft.getSize();
int diffToRightMin = rightMinSize.width - rightWidth;
if (leftSize.width - diffToRightMin >= leftMinSize.width) {
leftSize.width -= diffToRightMin;
myLeft.setSize(leftSize);
}
}
myRight.setBounds((int)myLeft.getBounds().getMaxX(), 0, parent.getWidth() - myLeft.getWidth(), parent.getHeight());
}
toMakeVerticallyInCenter(myLeft, parent);
toMakeVerticallyInCenter(myRight, parent);
}
private static void toMakeVerticallyInCenter(JComponent comp, Container parent) {
final Rectangle compBounds = comp.getBounds();
int compHeight = comp.getPreferredSize().height;
final int parentHeight = parent.getHeight();
if (compHeight > parentHeight) {
compHeight = parentHeight;
}
int y = (int)Math.floor(parentHeight / 2.0 - compHeight / 2.0);
comp.setBounds(compBounds.x, y, compBounds.width, compHeight);
}
}
@Override
public IdeFocusManager getFocusManager() {
return myFocusManager;
}
@Override
public RunnerLayoutUi getRunnerLayoutUi() {
return myRunnerUi;
}
@Override
public String getName() {
return mySessionName;
}
@Override
public List<AnAction> getActions(boolean originalProvider) {
ArrayList<AnAction> result = new ArrayList<AnAction>();
if (myLeftToolbarActions != null) {
AnAction[] kids = myLeftToolbarActions.getChildren(null);
ContainerUtil.addAll(result, kids);
}
return result;
}
@Override
public SwitchTarget getCurrentTarget() {
Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
if (owner == null) return myTabs.getCurrentTarget();
GridImpl grid = getSelectedGrid();
if (grid != null && grid.getContents().size() <= 1) return myTabs.getCurrentTarget();
if (grid != null) {
SwitchTarget cell = grid.getCellFor(owner);
return cell != null ? cell : myTabs.getCurrentTarget();
}
else {
return myTabs.getCurrentTarget();
}
}
@Override
public List<SwitchTarget> getTargets(boolean onlyVisible, boolean originalProvider) {
List<SwitchTarget> result = new ArrayList<SwitchTarget>();
result.addAll(myTabs.getTargets(true, false));
GridImpl grid = getSelectedGrid();
if (grid != null) {
result.addAll(grid.getTargets(onlyVisible));
}
for (Wrapper wrapper : myMinimizedButtonsPlaceholder.values()) {
if (!wrapper.isShowing()) continue;
JComponent target = wrapper.getTargetComponent();
if (target instanceof ActionToolbar) {
ActionToolbar tb = (ActionToolbar)target;
result.addAll(tb.getTargets(onlyVisible, false));
}
}
return result;
}
private int findFreeWindow() {
int i;
for (i = 1; i < Integer.MAX_VALUE; i++) {
if (!isUsed(i)) {
return i;
}
}
return i;
}
private boolean isUsed(int i) {
for (RunnerContentUi child : myChildren) {
if (child.getWindow() == i) {
return true;
}
}
return false;
}
private DockManagerImpl getDockManager() {
return (DockManagerImpl)DockManager.getInstance(myProject);
}
class MyDragOutDelegate implements TabInfo.DragOutDelegate {
private DragSession mySession;
@Override
public void dragOutStarted(MouseEvent mouseEvent, TabInfo info) {
JComponent component = info.getComponent();
Content[] data = CONTENT_KEY.getData((DataProvider)component);
assert data != null;
storeDefaultIndices(data);
final Dimension size = info.getComponent().getSize();
final Image image = JBTabsImpl.getComponentImage(info);
if (component instanceof Grid) {
info.setHidden(true);
}
Presentation presentation = new Presentation(info.getText());
presentation.setIcon(info.getIcon());
mySession = getDockManager().createDragSession(mouseEvent, new DockableGrid(image, presentation,
size,
Arrays.asList(data), 0));
}
@Override
public void processDragOut(MouseEvent event, TabInfo source) {
mySession.process(event);
}
@Override
public void dragOutFinished(MouseEvent event, TabInfo source) {
final Component component = event.getComponent();
final IdeFrame window = UIUtil.getParentOfType(IdeFrame.class, component);
mySession.process(event);
mySession = null;
}
@Override
public void dragOutCancelled(TabInfo source) {
source.setHidden(false);
mySession.cancel();
mySession = null;
}
}
class DockableGrid implements DockableContent<List<Content>> {
private final Image myImg;
private final Presentation myPresentation;
private final Dimension myPreferredSize;
private final List<Content> myContents;
private final int myWindow;
public DockableGrid(Image img, Presentation presentation, final Dimension size, List<Content> contents, int window) {
myImg = img;
myPresentation = presentation;
myPreferredSize = size;
myContents = contents;
myWindow = window;
}
@NotNull
@Override
public List<Content> getKey() {
return myContents;
}
@Override
public Image getPreviewImage() {
return myImg;
}
@Override
public Dimension getPreferredSize() {
return myPreferredSize;
}
@Override
public String getDockContainerType() {
return DockableGridContainerFactory.TYPE;
}
@Override
public Presentation getPresentation() {
return myPresentation;
}
public RunnerContentUi getRunnerUi() {
return RunnerContentUi.this;
}
public RunnerContentUi getOriginalRunnerUi() {
return myOriginal != null ? myOriginal : RunnerContentUi.this;
}
@NotNull
public List<Content> getContents() {
return myContents;
}
@Override
public void close() {
}
public int getWindow() {
return myWindow;
}
}
void fireContentOpened(Content content) {
for (Listener each : myDockingListeners) {
each.contentAdded(content);
}
}
void fireContentClosed(Content content) {
for (Listener each : myDockingListeners) {
each.contentRemoved(content);
}
}
}