53df6f2e8ea8b7a8c4066b87924dbf04ecef6259
[idea/community.git] / platform / lang-impl / src / com / intellij / execution / ui / layout / impl / GridCellImpl.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.execution.ui.layout.impl;
18
19 import com.intellij.execution.ui.layout.*;
20 import com.intellij.execution.ui.layout.actions.MinimizeViewAction;
21 import com.intellij.openapi.actionSystem.ActionGroup;
22 import com.intellij.openapi.actionSystem.DataProvider;
23 import com.intellij.openapi.ui.popup.JBPopup;
24 import com.intellij.openapi.util.ActionCallback;
25 import com.intellij.openapi.util.DimensionService;
26 import com.intellij.openapi.util.MutualMap;
27 import com.intellij.ui.components.panels.NonOpaquePanel;
28 import com.intellij.ui.content.Content;
29 import com.intellij.ui.docking.DockContainer;
30 import com.intellij.ui.docking.DockManager;
31 import com.intellij.ui.switcher.SwitchTarget;
32 import com.intellij.ui.tabs.JBTabs;
33 import com.intellij.ui.tabs.TabInfo;
34 import com.intellij.ui.tabs.TabsListener;
35 import com.intellij.ui.tabs.UiDecorator;
36 import com.intellij.ui.tabs.impl.JBTabsImpl;
37 import com.intellij.util.containers.HashSet;
38 import com.intellij.util.ui.UIUtil;
39 import org.jetbrains.annotations.NonNls;
40 import org.jetbrains.annotations.NotNull;
41 import org.jetbrains.annotations.Nullable;
42
43 import javax.swing.*;
44 import java.awt.*;
45 import java.awt.event.MouseAdapter;
46 import java.awt.event.MouseEvent;
47 import java.util.ArrayList;
48 import java.util.List;
49 import java.util.Set;
50
51 public class GridCellImpl implements GridCell {
52   private final GridImpl myContainer;
53
54   private final MutualMap<Content, TabInfo> myContents = new MutualMap<Content, TabInfo>(true);
55   private final Set<Content> myMinimizedContents = new HashSet<Content>();
56
57   private final JBTabs myTabs;
58   private final GridImpl.Placeholder myPlaceholder;
59   private final PlaceInGrid myPlaceInGrid;
60
61   private final ViewContextEx myContext;
62   private JBPopup myPopup;
63
64   public GridCellImpl(ViewContextEx context, @NotNull GridImpl container, GridImpl.Placeholder placeholder, PlaceInGrid placeInGrid) {
65     myContext = context;
66     myContainer = container;
67
68     myPlaceInGrid = placeInGrid;
69     myPlaceholder = placeholder;
70     myTabs = new JBTabsImpl(myContext.getProject(), myContext.getActionManager(), myContext.getFocusManager(), container).setDataProvider(new DataProvider() {
71       @Nullable
72       public Object getData(@NonNls final String dataId) {
73         if (ViewContext.CONTENT_KEY.is(dataId)) {
74           TabInfo target = myTabs.getTargetInfo();
75           if (target != null) {
76             return new Content[]{getContentFor(target)};
77           }
78         }
79         else if (ViewContext.CONTEXT_KEY.is(dataId)) {
80           return myContext;
81         }
82
83         return null;
84       }
85     });
86     myTabs.getPresentation().setUiDecorator(new UiDecorator() {
87       @NotNull
88       public UiDecoration getDecoration() {
89         return new UiDecoration(null, new Insets(1, -1, 1, -1));
90       }
91     }).setSideComponentVertical(!context.getLayoutSettings().isToolbarHorizontal())
92       .setStealthTabMode(true)
93       .setFocusCycle(false).setPaintFocus(true).setProvideSwitchTargets(false).setTabDraggingEnabled(true);
94
95     myTabs.addTabMouseListener(new MouseAdapter() {
96       public void mousePressed(final MouseEvent e) {
97         if (UIUtil.isCloseClick(e)) {
98             minimize(e);
99         }
100       }
101     });
102     rebuildPopupGroup();
103     myTabs.addListener(new TabsListener.Adapter() {
104
105       public void beforeSelectionChanged(TabInfo oldSelection, TabInfo newSelection) {
106         if (oldSelection != null && myContext.isStateBeingRestored()) {
107           saveUiState();
108         }
109       }
110
111       public void selectionChanged(final TabInfo oldSelection, final TabInfo newSelection) {
112         updateSelection(myTabs.getComponent().isShowing());
113
114         if (!myTabs.getComponent().isShowing()) return;
115
116         if (newSelection != null) {
117           newSelection.stopAlerting();
118         }
119       }
120     });
121   }
122
123   public void rebuildPopupGroup() {
124     myTabs.setPopupGroup(myContext.getCellPopupGroup(ViewContext.CELL_POPUP_PLACE),
125                          ViewContext.CELL_POPUP_PLACE, true);
126   }
127
128   public PlaceInGrid getPlaceInGrid() {
129     return myPlaceInGrid;
130   }
131
132   void add(final Content content) {
133     if (myContents.containsKey(content)) return;
134     myContents.put(content, null);
135
136     revalidateCell(new Runnable() {
137       public void run() {
138         myTabs.addTab(createTabInfoFor(content));
139       }
140     });
141
142     updateSelection(myTabs.getComponent().getRootPane() != null);
143   }
144
145   void remove(Content content) {
146     if (!myContents.containsKey(content)) return;
147
148     final TabInfo info = getTabFor(content);
149     myContents.remove(content);
150
151     revalidateCell(new Runnable() {
152       public void run() {
153         myTabs.removeTab(info);
154       }
155     });
156
157     updateSelection(myTabs.getComponent().getRootPane() != null);
158   }
159
160   private void revalidateCell(Runnable contentAction) {
161     if (myContents.size() == 0) {
162       myPlaceholder.removeAll();
163       myTabs.removeAllTabs();
164
165       if (myPopup != null) {
166         myPopup.cancel();
167         myPopup = null;
168       }
169     }
170     else {
171       if (myPlaceholder.isNull()) {
172         myPlaceholder.setContent(myTabs.getComponent());
173       }
174
175       contentAction.run();
176     }
177
178     restoreProportions();
179
180     myTabs.getComponent().revalidate();
181     myTabs.getComponent().repaint();
182   }
183
184   void setHideTabs(boolean hide) {
185     myTabs.getPresentation().setHideTabs(hide);
186   }
187
188   private TabInfo createTabInfoFor(Content content) {
189     final JComponent c = content.getComponent();
190
191     final TabInfo tabInfo = updatePresentation(new TabInfo(new ProviderWrapper(content, myContext)), content)
192       .setObject(content)
193       .setPreferredFocusableComponent(content.getPreferredFocusableComponent())
194       .setActionsContextComponent(content.getActionsContextComponent());
195
196     myContents.remove(content);
197     myContents.put(content, tabInfo);
198
199     ActionGroup group = (ActionGroup)myContext.getActionManager().getAction(RunnerContentUi.VIEW_TOOLBAR);
200     tabInfo.setTabLabelActions(group, ViewContext.CELL_TOOLBAR_PLACE);
201     tabInfo.setDragOutDelegate(((RunnerContentUi)myContext).myDragOutDelegate);
202     return tabInfo;
203   }
204
205   @Nullable
206   private static TabInfo updatePresentation(TabInfo info, Content content) {
207     if (info == null) return info;
208     return info.setIcon(content.getIcon()).setText(content.getDisplayName()).setActions(content.getActions(), content.getPlace());
209   }
210
211   public ActionCallback select(final Content content, final boolean requestFocus) {
212     final TabInfo tabInfo = myContents.getValue(content);
213     return tabInfo != null ? myTabs.select(tabInfo, requestFocus) : new ActionCallback.Done();
214   }
215
216   public void processAlert(final Content content, final boolean activate) {
217     if (myMinimizedContents.contains(content)) return;
218
219     TabInfo tab = getTabFor(content);
220     if (tab == null) return;
221     if (myTabs.getSelectedInfo() != tab) {
222       if (activate) {
223         tab.fireAlert();
224       } else {
225         tab.stopAlerting();
226       }
227     }
228   }
229
230   public void updateTabPresentation(Content content) {
231     updatePresentation(myTabs.findInfo(content), content);
232   }
233
234   public boolean isMinimized(Content content) {
235     return myMinimizedContents.contains(content);
236   }
237
238   public List<SwitchTarget> getTargets(boolean onlyVisible) {
239     if (myTabs.getPresentation().isHideTabs()) return new ArrayList<SwitchTarget>();
240
241     return myTabs.getTargets(onlyVisible, false);
242   }
243
244   public SwitchTarget getTargetForSelection() {
245     return myTabs.getCurrentTarget();
246   }
247
248   public boolean contains(Component c) {
249     return myTabs.getComponent().isAncestorOf(c);
250   }
251
252   private static class ProviderWrapper extends NonOpaquePanel implements DataProvider {
253
254     Content myContent;
255     ViewContext myContext;
256
257     private ProviderWrapper(final Content content, final ViewContext context) {
258       myContent = content;
259       myContext = context;
260       setLayout(new BorderLayout());
261       add(content.getComponent(), BorderLayout.CENTER);
262     }
263
264     @Nullable
265     public Object getData(@NonNls final String dataId) {
266       if (ViewContext.CONTENT_KEY.is(dataId)) {
267         return new Content[]{myContent};
268       }
269       else if (ViewContext.CONTEXT_KEY.is(dataId)) {
270         return myContext;
271       }
272       return null;
273     }
274   }
275
276   @Nullable
277   TabInfo getTabFor(Content content) {
278     return myContents.getValue(content);
279   }
280
281   @NotNull
282   private Content getContentFor(TabInfo tab) {
283     return myContents.getKey(tab);
284   }
285
286   public void setToolbarHorizontal(final boolean horizontal) {
287     myTabs.getPresentation().setSideComponentVertical(!horizontal);
288   }
289
290   public ActionCallback restoreLastUiState() {
291     final ActionCallback result = new ActionCallback();
292
293     restoreProportions();
294
295     Content[] contents = getContents();
296     int window = 0;
297     for (Content each : contents) {
298       final View view = myContainer.getStateFor(each);
299       if (view.isMinimizedInGrid()) {
300         minimize(each);
301       }
302       window = view.getWindow();
303     }
304     final Tab tab = myContainer.getTab();
305     final boolean detached = (tab != null && tab.isDetached(myPlaceInGrid)) || window != myContext.getWindow();
306     if (detached && contents.length > 0) {
307       if (tab != null) {
308         tab.setDetached(myPlaceInGrid, false);
309       }
310       myContext.detachTo(window, this).notifyWhenDone(result);
311     } else {
312       result.setDone();
313     }
314
315     return result;
316   }
317
318   Content[] getContents() {
319     return myContents.getKeys().toArray(new Content[myContents.size()]);
320   }
321
322   public int getContentCount() {
323     return myContents.size();
324   }
325
326   public void saveUiState() {
327     saveProportions();
328
329     for (Content each : myContents.getKeys()) {
330       saveState(each, false);
331     }
332
333     for (Content each : myMinimizedContents) {
334       saveState(each, true);
335     }
336
337     final DimensionService service = DimensionService.getInstance();
338     final Dimension size = myContext.getContentManager().getComponent().getSize();
339     service.setSize(getDimensionKey(), size, myContext.getProject());
340     if (myContext.getWindow() != 0) {
341       final JFrame frame = (JFrame)DockManager.getInstance(myContext.getProject()).getIdeFrame((DockContainer)myContext);
342       service.setLocation(getDimensionKey(), frame.getLocationOnScreen());
343     }
344   }
345
346   public void saveProportions() {
347     myContainer.saveSplitterProportions(myPlaceInGrid);
348   }
349
350   private void saveState(Content content, boolean minimized) {
351     View state = myContext.getStateFor(content);
352     state.setMinimizedInGrid(minimized);
353     state.setPlaceInGrid(myPlaceInGrid);
354     state.assignTab(myContainer.getTabIndex());
355     state.setWindow(myContext.getWindow());
356   }
357
358   public void restoreProportions() {
359     myContainer.restoreLastSplitterProportions(myPlaceInGrid);
360   }
361
362   public void updateSelection(final boolean isShowing) {
363     for (Content each : myContents.getKeys()) {
364       final TabInfo eachTab = getTabFor(each);
365       boolean isSelected = eachTab != null && myTabs.getSelectedInfo() == eachTab;
366       if (isSelected && isShowing) {
367         myContext.getContentManager().addSelectedContent(each);
368       }
369       else {
370         myContext.getContentManager().removeFromSelection(each);
371       }
372     }
373
374     for (Content each : myMinimizedContents) {
375       myContext.getContentManager().removeFromSelection(each);
376     }
377   }
378
379   public void minimize(Content[] contents) {
380     myContext.saveUiState();
381
382     for (final Content each : contents) {
383       myMinimizedContents.add(each);
384       remove(each);
385       boolean isShowing = myTabs.getComponent().getRootPane() != null;
386       updateSelection(isShowing);
387       myContainer.minimize(each, new CellTransform.Restore() {
388         public ActionCallback restoreInGrid() {
389           return restore(each);
390         }
391       });
392     }
393   }
394
395   @Nullable
396   public Point getLocation() {
397     return DimensionService.getInstance().getLocation(getDimensionKey(), myContext.getProject());
398   }
399
400   @Nullable
401   public Dimension getSize() {
402     return DimensionService.getInstance().getSize(getDimensionKey(), myContext.getProject());
403   }
404   
405   private String getDimensionKey() {
406     return "GridCell.Tab." + myContainer.getTab().getIndex() + "." + myPlaceInGrid.name();
407   }
408
409   public boolean isValidForCalculateProportions() {
410     return getContentCount() > 0;
411   }
412
413   public void minimize(Content content) {
414     minimize(new Content[]{content});
415   }
416
417   public void minimize(MouseEvent e) {
418     if (!MinimizeViewAction.isEnabled(myContext, getContents(), ViewContext.CELL_TOOLBAR_PLACE)) return;
419
420     TabInfo tabInfo = myTabs.findInfo(e);
421     if (tabInfo != null) {
422       minimize(getContentFor(tabInfo));
423     }
424   }
425
426   private ActionCallback restore(Content content) {
427     myMinimizedContents.remove(content);
428     add(content);
429     updateSelection(myTabs.getComponent().getRootPane() != null);
430     return new ActionCallback.Done();
431   }
432 }