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
;
45 import javax
.swing
.border
.EmptyBorder
;
47 import java
.awt
.event
.MouseAdapter
;
48 import java
.awt
.event
.MouseEvent
;
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
) {
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() {
77 public Object
getData(@NonNls final String dataId
) {
78 if (ViewContext
.CONTENT_KEY
.getName().equals(dataId
)) {
79 TabInfo target
= myTabs
.getTargetInfo();
81 return new Content
[]{getContentFor(target
)};
84 else if (ViewContext
.CONTEXT_KEY
.getName().equals(dataId
)) {
91 myTabs
.getPresentation().setUiDecorator(new UiDecorator() {
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
)) {
113 myTabs
.addListener(new TabsListener() {
115 public void beforeSelectionChanged(TabInfo oldSelection
, TabInfo newSelection
) {
116 if (oldSelection
!= null && myContext
.isStateBeingRestored()) {
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() {
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() {
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();
176 if (myPlaceholder
.isNull() && !isDetached()) {
177 myPlaceholder
.setContent(myTabs
.getComponent());
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
)
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
);
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
) {
235 public void updateTabPresentation(Content content
) {
236 updatePresentation(myTabs
.findInfo(content
), content
);
239 private static class ProviderWrapper
extends NonOpaquePanel
implements DataProvider
{
242 ViewContext myContext
;
244 private ProviderWrapper(final Content content
, final ViewContext context
) {
247 setLayout(new BorderLayout());
248 add(content
.getComponent(), BorderLayout
.CENTER
);
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
)) {
264 private TabInfo
getTabFor(Content content
) {
265 return myContents
.getValue(content
);
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()) {
289 if (!isRestoringFromDetach() && myContainer
.getTab().isDetached(myPlaceInGrid
)) {
290 _detach(!myContext
.isStateBeingRestored()).notifyWhenDone(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() {
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
);
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
);
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
);
397 detachForShowingTabs(requestFocus
).notifyWhenDone(result
);
400 return new ActionCallback
.Done();
408 detachTo(targetBounds
.getLocation(), targetBounds
.getSize(), false, requestFocus
).notifyWhenDone(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() {
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())
457 .setRequestFocus(requestFocus
)
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);
480 return builder
.createPopup();
483 public void attach() {
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
);
522 updateSelection(myTabs
.getComponent().getRootPane() != null);
523 return new ActionCallback
.Done();
526 public void dispose() {
529 if (myPopup
!= null) {