NotNull, dispose
[fedora-idea.git] / platform / lang-impl / src / com / intellij / execution / ui / layout / impl / GridCellImpl.java
blob77089d104736b9db1e71f44c93ee2250da0c044e
1 /*
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.layout.*;
20 import com.intellij.execution.ui.layout.actions.MinimizeViewAction;
21 import com.intellij.openapi.Disposable;
22 import com.intellij.openapi.actionSystem.ActionGroup;
23 import com.intellij.openapi.actionSystem.DataProvider;
24 import com.intellij.openapi.ui.popup.ComponentPopupBuilder;
25 import com.intellij.openapi.ui.popup.JBPopup;
26 import com.intellij.openapi.ui.popup.JBPopupFactory;
27 import com.intellij.openapi.util.*;
28 import com.intellij.openapi.wm.IdeFrame;
29 import com.intellij.openapi.wm.WindowManager;
30 import com.intellij.ui.components.panels.NonOpaquePanel;
31 import com.intellij.ui.components.panels.Wrapper;
32 import com.intellij.ui.content.Content;
33 import com.intellij.ui.tabs.JBTabs;
34 import com.intellij.ui.tabs.TabInfo;
35 import com.intellij.ui.tabs.TabsListener;
36 import com.intellij.ui.tabs.UiDecorator;
37 import com.intellij.ui.tabs.impl.JBTabsImpl;
38 import com.intellij.util.containers.HashSet;
39 import com.intellij.util.ui.UIUtil;
40 import org.jetbrains.annotations.NonNls;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
44 import javax.swing.*;
45 import javax.swing.border.EmptyBorder;
46 import java.awt.*;
47 import java.awt.event.MouseAdapter;
48 import java.awt.event.MouseEvent;
49 import java.util.Set;
51 public class GridCellImpl implements GridCell, Disposable {
53 private final GridImpl myContainer;
55 private final MutualMap<Content, TabInfo> myContents = new MutualMap<Content, TabInfo>(true);
56 private final Set<Content> myMinimizedContents = new HashSet<Content>();
58 private final JBTabs myTabs;
59 private final GridImpl.Placeholder myPlaceholder;
60 private final PlaceInGrid myPlaceInGrid;
62 private final ViewContextEx myContext;
63 private CellTransform.Restore.List myRestoreFromDetach;
64 private JBPopup myPopup;
65 private boolean myDisposed;
67 public GridCellImpl(ViewContextEx context, @NotNull GridImpl container, GridImpl.Placeholder placeholder, PlaceInGrid placeInGrid) {
68 myContext = context;
69 myContainer = container;
71 Disposer.register(container, this);
73 myPlaceInGrid = placeInGrid;
74 myPlaceholder = placeholder;
75 myTabs = new JBTabsImpl(myContext.getProject(), myContext.getActionManager(), myContext.getFocusManager(), container).setDataProvider(new DataProvider() {
76 @Nullable
77 public Object getData(@NonNls final String dataId) {
78 if (ViewContext.CONTENT_KEY.getName().equals(dataId)) {
79 TabInfo target = myTabs.getTargetInfo();
80 if (target != null) {
81 return new Content[]{getContentFor(target)};
84 else if (ViewContext.CONTEXT_KEY.getName().equals(dataId)) {
85 return myContext;
88 return null;
90 });
91 myTabs.getPresentation().setUiDecorator(new UiDecorator() {
92 @NotNull
93 public UiDecoration getDecoration() {
94 return new UiDecoration(null, new Insets(1, -1, 1, -1));
96 }).setSideComponentVertical(!context.getLayoutSettings().isToolbarHorizontal())
97 .setStealthTabMode(true)
98 .setFocusCycle(false).setPaintFocus(true);
100 myTabs.addTabMouseListener(new MouseAdapter() {
101 public void mousePressed(final MouseEvent e) {
102 if (UIUtil.isCloseClick(e)) {
103 if (isDetached()) {
104 myPopup.cancel();
106 else {
107 minimize(e);
112 rebuildPopupGroup();
113 myTabs.addListener(new TabsListener() {
115 public void beforeSelectionChanged(TabInfo oldSelection, TabInfo newSelection) {
116 if (oldSelection != null && myContext.isStateBeingRestored()) {
117 saveUiState();
121 public void selectionChanged(final TabInfo oldSelection, final TabInfo newSelection) {
122 updateSelection(myTabs.getComponent().isShowing());
124 if (!myTabs.getComponent().isShowing()) return;
126 if (newSelection != null) {
127 newSelection.stopAlerting();
133 public void rebuildPopupGroup() {
134 myTabs.setPopupGroup(myContext.getCellPopupGroup(ViewContext.CELL_POPUP_PLACE),
135 ViewContext.CELL_POPUP_PLACE, true);
138 public PlaceInGrid getPlaceInGrid() {
139 return myPlaceInGrid;
142 void add(final Content content) {
143 if (myContents.containsKey(content)) return;
144 myContents.put(content, null);
146 revalidateCell(new Runnable() {
147 public void run() {
148 myTabs.addTab(createTabInfoFor(content));
152 updateSelection(myTabs.getComponent().getRootPane() != null);
155 void remove(Content content) {
156 if (!myContents.containsKey(content)) return;
158 final TabInfo info = getTabFor(content);
159 myContents.remove(content);
161 revalidateCell(new Runnable() {
162 public void run() {
163 myTabs.removeTab(info);
167 updateSelection(myTabs.getComponent().getRootPane() != null);
170 private void revalidateCell(Runnable contentAction) {
171 if (myContents.size() == 0) {
172 myPlaceholder.removeAll();
173 myTabs.removeAllTabs();
175 else {
176 if (myPlaceholder.isNull() && !isDetached()) {
177 myPlaceholder.setContent(myTabs.getComponent());
180 contentAction.run();
183 restoreProportions();
185 myTabs.getComponent().revalidate();
186 myTabs.getComponent().repaint();
189 void setHideTabs(boolean hide) {
190 myTabs.getPresentation().setHideTabs(hide);
193 private TabInfo createTabInfoFor(Content content) {
194 final JComponent c = content.getComponent();
196 final TabInfo tabInfo = updatePresentation(new TabInfo(new ProviderWrapper(content, myContext)), content)
197 .setObject(content)
198 .setPreferredFocusableComponent(content.getPreferredFocusableComponent())
199 .setActionsContextComponent(content.getActionsContextComponent());
201 myContents.remove(content);
202 myContents.put(content, tabInfo);
204 ActionGroup group = (ActionGroup)myContext.getActionManager().getAction(RunnerContentUi.VIEW_TOOLBAR);
205 tabInfo.setTabLabelActions(group, ViewContext.CELL_TOOLBAR_PLACE);
207 return tabInfo;
210 @Nullable
211 private static TabInfo updatePresentation(TabInfo info, Content content) {
212 if (info == null) return info;
213 return info.setIcon(content.getIcon()).setText(content.getDisplayName()).setActions(content.getActions(), content.getPlace());
216 public ActionCallback select(final Content content, final boolean requestFocus) {
217 final TabInfo tabInfo = myContents.getValue(content);
218 return tabInfo != null ? myTabs.select(tabInfo, requestFocus) : new ActionCallback.Done();
221 public void processAlert(final Content content, final boolean activate) {
222 if (myMinimizedContents.contains(content)) return;
224 TabInfo tab = getTabFor(content);
225 if (tab == null) return;
226 if (myTabs.getSelectedInfo() != tab) {
227 if (activate) {
228 tab.fireAlert();
229 } else {
230 tab.stopAlerting();
235 public void updateTabPresentation(Content content) {
236 updatePresentation(myTabs.findInfo(content), content);
239 private static class ProviderWrapper extends NonOpaquePanel implements DataProvider {
241 Content myContent;
242 ViewContext myContext;
244 private ProviderWrapper(final Content content, final ViewContext context) {
245 myContent = content;
246 myContext = context;
247 setLayout(new BorderLayout());
248 add(content.getComponent(), BorderLayout.CENTER);
251 @Nullable
252 public Object getData(@NonNls final String dataId) {
253 if (ViewContext.CONTENT_KEY.getName().equals(dataId)) {
254 return new Content[]{myContent};
256 else if (ViewContext.CONTEXT_KEY.getName().equals(dataId)) {
257 return myContext;
259 return null;
263 @Nullable
264 private TabInfo getTabFor(Content content) {
265 return myContents.getValue(content);
268 @NotNull
269 private Content getContentFor(TabInfo tab) {
270 return myContents.getKey(tab);
273 public void setToolbarHorizontal(final boolean horizontal) {
274 myTabs.getPresentation().setSideComponentVertical(!horizontal);
277 public ActionCallback restoreLastUiState() {
278 final ActionCallback result = new ActionCallback();
280 restoreProportions();
282 Content[] contents = getContents();
283 for (Content each : contents) {
284 if (myContainer.getStateFor(each).isMinimizedInGrid()) {
285 minimize(each);
289 if (!isRestoringFromDetach() && myContainer.getTab().isDetached(myPlaceInGrid)) {
290 _detach(!myContext.isStateBeingRestored()).notifyWhenDone(result);
291 } else {
292 result.setDone();
295 return result;
298 private Content[] getContents() {
299 return myContents.getKeys().toArray(new Content[myContents.size()]);
302 public int getContentCount() {
303 return myContents.size();
306 public void saveUiState() {
307 saveProportions();
309 for (Content each : myContents.getKeys()) {
310 saveState(each, false);
313 for (Content each : myMinimizedContents) {
314 saveState(each, true);
318 public void saveProportions() {
319 myContainer.saveSplitterProportions(myPlaceInGrid);
322 private void saveState(Content content, boolean minimized) {
323 View state = myContext.getStateFor(content);
324 state.setMinimizedInGrid(minimized);
325 state.setPlaceInGrid(myPlaceInGrid);
326 state.assignTab(myContainer.getTabIndex());
328 state.getTab().setDetached(myPlaceInGrid, isDetached());
331 public void restoreProportions() {
332 myContainer.restoreLastSplitterProportions(myPlaceInGrid);
335 public void updateSelection(final boolean isShowing) {
336 for (Content each : myContents.getKeys()) {
337 final TabInfo eachTab = getTabFor(each);
338 boolean isSelected = eachTab != null && myTabs.getSelectedInfo() == eachTab;
339 if (isSelected && (isShowing || isDetached())) {
340 myContext.getContentManager().addSelectedContent(each);
342 else {
343 myContext.getContentManager().removeFromSelection(each);
347 for (Content each : myMinimizedContents) {
348 myContext.getContentManager().removeFromSelection(each);
352 public void minimize(Content[] contents) {
353 myContext.saveUiState();
355 for (final Content each : contents) {
356 myMinimizedContents.add(each);
357 remove(each);
358 boolean isShowing = myTabs.getComponent().getRootPane() != null;
359 updateSelection(isShowing);
360 myContainer.minimize(each, new CellTransform.Restore() {
361 public ActionCallback restoreInGrid() {
362 return restore(each);
368 public ActionCallback detach() {
369 return _detach(true);
372 private ActionCallback _detach(final boolean requestFocus) {
373 myContext.saveUiState();
375 final DimensionService dimService = DimensionService.getInstance();
376 Point storedLocation = dimService.getLocation(getDimensionKey(), myContext.getProject());
377 Dimension storedSize = dimService.getSize(getDimensionKey(), myContext.getProject());
379 final IdeFrame frame = WindowManager.getInstance().getIdeFrame(myContext.getProject());
380 final Rectangle targetBounds = frame.suggestChildFrameBounds();
383 if (storedLocation != null && storedSize != null) {
384 targetBounds.setLocation(storedLocation);
385 targetBounds.setSize(storedSize);
388 final ActionCallback result = new ActionCallback();
390 if (storedLocation == null || storedSize == null) {
391 if (myContents.size() > 0) {
392 myContext.validate(myContents.getKeys().iterator().next(), new ActiveRunnable() {
393 public ActionCallback run() {
394 if (!myTabs.getComponent().isShowing()) {
395 detachTo(targetBounds.getLocation(), targetBounds.getSize(), false, requestFocus).notifyWhenDone(result);
396 } else {
397 detachForShowingTabs(requestFocus).notifyWhenDone(result);
400 return new ActionCallback.Done();
404 return result;
408 detachTo(targetBounds.getLocation(), targetBounds.getSize(), false, requestFocus).notifyWhenDone(result);
410 return result;
413 private ActionCallback detachForShowingTabs(boolean requestFocus) {
414 return detachTo(myTabs.getComponent().getLocationOnScreen(), myTabs.getComponent().getSize(), false, requestFocus);
417 private ActionCallback detachTo(Point screenPoint, Dimension size, boolean dragging, final boolean requestFocus) {
418 if (isDetached()) return new ActionCallback.Done();
420 final Content[] contents = getContents();
422 myRestoreFromDetach = new CellTransform.Restore.List();
424 myRestoreFromDetach.add(myPlaceholder.detach());
425 myRestoreFromDetach.add(myContainer.detach(contents));
426 myRestoreFromDetach.add(new CellTransform.Restore() {
427 public ActionCallback restoreInGrid() {
428 ensureVisible();
429 return new ActionCallback.Done();
433 myPopup = createPopup(dragging, requestFocus);
434 myPopup.setSize(size);
435 myPopup.setLocation(screenPoint);
436 myPopup.show(myContext.getContentManager().getComponent());
438 myContext.saveUiState();
440 myTabs.updateTabActions(true);
442 return new ActionCallback.Done();
445 private void ensureVisible() {
446 if (myTabs.getSelectedInfo() != null) {
447 myContext.select(getContentFor(myTabs.getSelectedInfo()), true);
451 private JBPopup createPopup(boolean dragging, final boolean requestFocus) {
452 Wrapper wrapper = new Wrapper(myTabs.getComponent());
453 wrapper.setBorder(new EmptyBorder(1, 0, 0, 0));
454 final ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(wrapper, myTabs.getComponent())
455 .setTitle(myContainer.getSessionName())
456 .setMovable(true)
457 .setRequestFocus(requestFocus)
458 .setFocusable(true)
459 .setResizable(true)
460 .setDimensionServiceKey(myContext.getProject(), getDimensionKey(), true)
461 .setCancelOnOtherWindowOpen(false)
462 .setCancelOnClickOutside(false)
463 .setCancelKeyEnabled(true)
464 .setLocateByContent(dragging)
465 .setLocateWithinScreenBounds(!dragging)
466 .setCancelKeyEnabled(false)
467 .setBelongsToGlobalPopupStack(false)
468 .setModalContext(false)
469 .setCancelCallback(new Computable<Boolean>() {
470 public Boolean compute() {
471 if (myDisposed) return Boolean.TRUE;
472 myRestoreFromDetach.restoreInGrid();
473 myRestoreFromDetach = null;
474 myContext.saveUiState();
475 myTabs.updateTabActions(true);
476 return Boolean.TRUE;
480 return builder.createPopup();
483 public void attach() {
484 if (isDetached()) {
485 myPopup.cancel();
490 public boolean isDetached() {
491 return myRestoreFromDetach != null && !myRestoreFromDetach.isRestoringNow();
494 public boolean isRestoringFromDetach() {
495 return myRestoreFromDetach != null && myRestoreFromDetach.isRestoringNow();
498 private String getDimensionKey() {
499 return "GridCell.Tab." + myContainer.getTab().getIndex() + "." + myPlaceInGrid.name();
502 public boolean isValidForCalculatePropertions() {
503 return !isDetached() && getContentCount() > 0;
506 public void minimize(Content content) {
507 minimize(new Content[]{content});
510 public void minimize(MouseEvent e) {
511 if (!MinimizeViewAction.isEnabled(myContext, getContents(), ViewContext.CELL_TOOLBAR_PLACE)) return;
513 TabInfo tabInfo = myTabs.findInfo(e);
514 if (tabInfo != null) {
515 minimize(getContentFor(tabInfo));
519 private ActionCallback restore(Content content) {
520 myMinimizedContents.remove(content);
521 add(content);
522 updateSelection(myTabs.getComponent().getRootPane() != null);
523 return new ActionCallback.Done();
526 public void dispose() {
527 myDisposed = true;
529 if (myPopup != null) {
530 myPopup.cancel();
531 myPopup = null;