e6c84f5e77683cbce459bbcd9d6f30c8dbc5b364
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / wm / impl / InternalDecorator.java
1 /*
2  * Copyright 2000-2014 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 package com.intellij.openapi.wm.impl;
17
18 import com.intellij.ide.actions.ResizeToolWindowAction;
19 import com.intellij.idea.ActionsBundle;
20 import com.intellij.openapi.actionSystem.*;
21 import com.intellij.openapi.keymap.Keymap;
22 import com.intellij.openapi.keymap.KeymapManagerListener;
23 import com.intellij.openapi.keymap.ex.KeymapManagerEx;
24 import com.intellij.openapi.project.DumbAware;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.ui.Queryable;
27 import com.intellij.openapi.ui.Splitter;
28 import com.intellij.openapi.util.Comparing;
29 import com.intellij.openapi.util.Disposer;
30 import com.intellij.openapi.util.SystemInfo;
31 import com.intellij.openapi.wm.*;
32 import com.intellij.ui.Gray;
33 import com.intellij.ui.JBColor;
34 import com.intellij.ui.UIBundle;
35 import com.intellij.ui.components.panels.NonOpaquePanel;
36 import com.intellij.ui.content.Content;
37 import com.intellij.util.EventDispatcher;
38 import com.intellij.util.Producer;
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;
43
44 import javax.swing.*;
45 import javax.swing.border.Border;
46 import javax.swing.border.EmptyBorder;
47 import java.awt.*;
48 import java.awt.event.*;
49 import java.util.Map;
50
51 /**
52  * @author Eugene Belyaev
53  * @author Vladimir Kondratyev
54  */
55 public final class InternalDecorator extends JPanel implements Queryable, TypeSafeDataProvider {
56
57   private static final int DIVIDER_WIDTH = UIUtil.isUnderDarcula() ? 2 : 5;
58
59   private Project myProject;
60   private WindowInfoImpl myInfo;
61   private final ToolWindowImpl myToolWindow;
62   private final MyDivider myDivider;
63   private final EventDispatcher<InternalDecoratorListener> myDispatcher = EventDispatcher.create(InternalDecoratorListener.class);
64   /*
65    * Actions
66    */
67   private final TogglePinnedModeAction myToggleAutoHideModeAction;
68   private final ToggleDockModeAction myToggleDockModeAction;
69   private final ToggleFloatingModeAction myToggleFloatingModeAction;
70   private final ToggleSideModeAction myToggleSideModeAction;
71   private final ToggleContentUiTypeAction myToggleContentUiTypeAction;
72
73   private ActionGroup myAdditionalGearActions;
74   /**
75    * Catches all event from tool window and modifies decorator's appearance.
76    */
77   private final MyKeymapManagerListener myWeakKeymapManagerListener;
78   @NonNls private static final String HIDE_ACTIVE_WINDOW_ACTION_ID = "HideActiveWindow";
79   @NonNls public static final String TOGGLE_PINNED_MODE_ACTION_ID = "TogglePinnedMode";
80   @NonNls public static final String TOGGLE_DOCK_MODE_ACTION_ID = "ToggleDockMode";
81   @NonNls public static final String TOGGLE_FLOATING_MODE_ACTION_ID = "ToggleFloatingMode";
82   @NonNls public static final String TOGGLE_SIDE_MODE_ACTION_ID = "ToggleSideMode";
83   @NonNls private static final String TOGGLE_CONTENT_UI_TYPE_ACTION_ID = "ToggleContentUiTypeMode";
84
85   private ToolWindowHeader myHeader;
86
87   InternalDecorator(final Project project, @NotNull WindowInfoImpl info, final ToolWindowImpl toolWindow) {
88     super(new BorderLayout());
89     myProject = project;
90     myToolWindow = toolWindow;
91     myToolWindow.setDecorator(this);
92     myDivider = new MyDivider();
93
94     myToggleFloatingModeAction = new ToggleFloatingModeAction();
95     myToggleSideModeAction = new ToggleSideModeAction();
96     myToggleDockModeAction = new ToggleDockModeAction();
97     myToggleAutoHideModeAction = new TogglePinnedModeAction();
98     myToggleContentUiTypeAction = new ToggleContentUiTypeAction();
99
100     myHeader = new ToolWindowHeader(toolWindow, info, new Producer<ActionGroup>() {
101       @Override
102       public ActionGroup produce() {
103         return createGearPopupGroup();
104       }
105     }) {
106       @Override
107       protected boolean isActive() {
108         return isFocused();
109       }
110
111       @Override
112       protected void hideToolWindow() {
113         fireHidden();
114       }
115
116       @Override
117       protected void toolWindowTypeChanged(ToolWindowType type) {
118         fireTypeChanged(type);
119       }
120
121       @Override
122       protected void sideHidden() {
123         fireHiddenSide();
124       }
125     };
126
127     MyKeymapManagerListener keymapManagerListener = new MyKeymapManagerListener();
128     final KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
129     myWeakKeymapManagerListener = keymapManagerListener;
130     keymapManager.addWeakListener(keymapManagerListener);
131
132     init();
133
134     apply(info);
135   }
136
137   public boolean isFocused() {
138     IdeFocusManager fm = IdeFocusManager.getInstance(myProject);
139     Component component = fm.getFocusedDescendantFor(myToolWindow.getComponent());
140     if (component != null) return true;
141
142     Component owner = fm.getLastFocusedFor(WindowManager.getInstance().getIdeFrame(myProject));
143
144     return owner != null && SwingUtilities.isDescendingFrom(owner, myToolWindow.getComponent());
145   }
146
147   /**
148    * Applies specified decoration.
149    */
150   public final void apply(@NotNull WindowInfoImpl info) {
151     if (Comparing.equal(myInfo, info) || myProject == null || myProject.isDisposed()) {
152       return;
153     }
154     myInfo = info;
155
156     // Anchor
157     final ToolWindowAnchor anchor = myInfo.getAnchor();
158     if (info.isSliding()) {
159       myDivider.invalidate();
160       if (ToolWindowAnchor.TOP == anchor) {
161         add(myDivider, BorderLayout.SOUTH);
162       }
163       else if (ToolWindowAnchor.LEFT == anchor) {
164         add(myDivider, BorderLayout.EAST);
165       }
166       else if (ToolWindowAnchor.BOTTOM == anchor) {
167         add(myDivider, BorderLayout.NORTH);
168       }
169       else if (ToolWindowAnchor.RIGHT == anchor) {
170         add(myDivider, BorderLayout.WEST);
171       }
172       myDivider.setPreferredSize(new Dimension(DIVIDER_WIDTH, DIVIDER_WIDTH));
173     }
174     else { // docked and floating windows don't have divider
175       remove(myDivider);
176     }
177
178     validate();
179     repaint();
180
181
182     // Push "apply" request forward
183
184     if (myInfo.isFloating() && myInfo.isVisible()) {
185       final FloatingDecorator floatingDecorator = (FloatingDecorator)SwingUtilities.getAncestorOfClass(FloatingDecorator.class, this);
186       if (floatingDecorator != null) {
187         floatingDecorator.apply(myInfo);
188       }
189     }
190
191     myToolWindow.getContentUI().setType(myInfo.getContentUiType());
192     setBorder(new InnerPanelBorder(myToolWindow));
193   }
194
195   @Override
196   public void calcData(DataKey key, DataSink sink) {
197     if (PlatformDataKeys.TOOL_WINDOW.equals(key)) {
198       sink.put(PlatformDataKeys.TOOL_WINDOW, myToolWindow);
199     }
200   }
201
202   final void addInternalDecoratorListener(InternalDecoratorListener l) {
203     myDispatcher.addListener(l);
204   }
205
206   final void removeInternalDecoratorListener(InternalDecoratorListener l) {
207     myDispatcher.removeListener(l);
208   }
209
210   final void dispose() {
211     removeAll();
212     KeymapManagerEx.getInstanceEx().removeWeakListener(myWeakKeymapManagerListener);
213
214     Disposer.dispose(myHeader);
215     myHeader = null;
216     myProject = null;
217   }
218
219   private void fireAnchorChanged(ToolWindowAnchor anchor) {
220     myDispatcher.getMulticaster().anchorChanged(this, anchor);
221   }
222
223   private void fireAutoHideChanged(boolean autoHide) {
224     myDispatcher.getMulticaster().autoHideChanged(this, autoHide);
225   }
226
227   /**
228    * Fires event that "hide" button has been pressed.
229    */
230   final void fireHidden() {
231     myDispatcher.getMulticaster().hidden(this);
232   }
233
234   /**
235    * Fires event that "hide" button has been pressed.
236    */
237   final void fireHiddenSide() {
238     myDispatcher.getMulticaster().hiddenSide(this);
239   }
240
241   /**
242    * Fires event that user performed click into the title bar area.
243    */
244   final void fireActivated() {
245     myDispatcher.getMulticaster().activated(this);
246   }
247
248   private void fireTypeChanged(ToolWindowType type) {
249     myDispatcher.getMulticaster().typeChanged(this, type);
250   }
251
252   final void fireResized() {
253     myDispatcher.getMulticaster().resized(this);
254   }
255
256   private void fireSideStatusChanged(boolean isSide) {
257     myDispatcher.getMulticaster().sideStatusChanged(this, isSide);
258   }
259
260   private void fireContentUiTypeChanges(ToolWindowContentUiType type) {
261     myDispatcher.getMulticaster().contentUiTypeChanges(this, type);
262   }
263
264   private void init() {
265     enableEvents(AWTEvent.COMPONENT_EVENT_MASK);
266
267     final JPanel contentPane = new JPanel(new BorderLayout());
268     contentPane.add(myHeader, BorderLayout.NORTH);
269
270     JPanel innerPanel = new JPanel(new BorderLayout());
271     JComponent toolWindowComponent = myToolWindow.getComponent();
272     innerPanel.add(toolWindowComponent, BorderLayout.CENTER);
273
274     final NonOpaquePanel inner = new NonOpaquePanel(innerPanel);
275     inner.setBorder(new EmptyBorder(0, 0, 0, 0));
276
277     contentPane.add(inner, BorderLayout.CENTER);
278     add(contentPane, BorderLayout.CENTER);
279     if (SystemInfo.isMac) {
280       setBackground(new JBColor(Gray._200, Gray._90));
281     }
282
283     // Add listeners
284     registerKeyboardAction(new ActionListener() {
285       @Override
286       public void actionPerformed(final ActionEvent e) {
287         ToolWindowManager.getInstance(myProject).activateEditorComponent();
288       }
289     }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
290   }
291
292   public void setTitleActions(AnAction[] actions) {
293     myHeader.setAdditionalTitleActions(actions);
294   }
295
296   private class InnerPanelBorder implements Border {
297
298     private final ToolWindow myWindow;
299
300     private InnerPanelBorder(ToolWindow window) {
301       myWindow = window;
302     }
303
304     @Override
305     public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int width, final int height) {
306       g.setColor(UIUtil.getPanelBackground());
307       doPaintBorder(c, g, x, y, width, height);
308       g.setColor(new Color(0, 0, 0, 90));
309       doPaintBorder(c, g, x, y, width, height);
310     }
311
312     private void doPaintBorder(Component c, Graphics g, int x, int y, int width, int height) {
313       Insets insets = getBorderInsets(c);
314
315       if (insets.top > 0) {
316         UIUtil.drawLine(g, x, y + insets.top - 1, x + width - 1, y + insets.top - 1);
317       }
318
319       if (insets.left > 0) {
320         UIUtil.drawLine(g, x, y + insets.top, x, y + height - 1);
321       }
322
323       if (insets.right > 0) {
324         UIUtil.drawLine(g, x + width - 1, y + insets.top, x + width - 1, y + height - 1);
325       }
326
327       if (insets.bottom > 0) {
328         UIUtil.drawLine(g, x, y + height - 1, x + width - 1, y + height - 1);
329        }
330     }
331
332     @Override
333     public Insets getBorderInsets(final Component c) {
334       if (myProject == null) return new Insets(0, 0, 0, 0);
335       ToolWindowManager toolWindowManager =  ToolWindowManager.getInstance(myProject);
336       if (!(toolWindowManager instanceof ToolWindowManagerImpl)
337           || !((ToolWindowManagerImpl)toolWindowManager).isToolWindowRegistered(myInfo.getId())
338           || myWindow.getType() == ToolWindowType.FLOATING) {
339         return new Insets(0, 0, 0, 0);
340       }
341       ToolWindowAnchor anchor = myWindow.getAnchor();
342       Component component = myWindow.getComponent();
343       Container parent = component.getParent();
344       while(parent != null) {
345         if (parent instanceof Splitter) {
346           Splitter splitter = (Splitter)parent;
347           boolean isFirst = splitter.getFirstComponent() == component;
348           boolean isVertical = splitter.isVertical();
349           return new Insets(0,
350                             anchor == ToolWindowAnchor.RIGHT || (!isVertical && !isFirst) ? 1 : 0,
351                             (isVertical && isFirst) ? 1 : 0,
352                             anchor == ToolWindowAnchor.LEFT || (!isVertical && isFirst) ? 1 : 0);
353         }
354         component = parent;
355         parent = component.getParent();
356       }
357       return new Insets(0, anchor == ToolWindowAnchor.RIGHT ? 1 : 0, 0, anchor == ToolWindowAnchor.LEFT ? 1 : 0);
358     }
359
360     @Override
361     public boolean isBorderOpaque() {
362       return false;
363     }
364   }
365
366
367   public final ActionGroup createPopupGroup() {
368     final DefaultActionGroup group = createGearPopupGroup();
369
370     group.add(myToggleContentUiTypeAction);
371
372     final DefaultActionGroup moveGroup = new DefaultActionGroup(UIBundle.message("tool.window.move.to.action.group.name"), true);
373     final ToolWindowAnchor anchor = myInfo.getAnchor();
374     if (anchor != ToolWindowAnchor.TOP) {
375       final AnAction topAction = new ChangeAnchorAction(UIBundle.message("tool.window.move.to.top.action.name"), ToolWindowAnchor.TOP);
376       moveGroup.add(topAction);
377     }
378     if (anchor != ToolWindowAnchor.LEFT) {
379       final AnAction leftAction = new ChangeAnchorAction(UIBundle.message("tool.window.move.to.left.action.name"), ToolWindowAnchor.LEFT);
380       moveGroup.add(leftAction);
381     }
382     if (anchor != ToolWindowAnchor.BOTTOM) {
383       final AnAction bottomAction =
384         new ChangeAnchorAction(UIBundle.message("tool.window.move.to.bottom.action.name"), ToolWindowAnchor.BOTTOM);
385       moveGroup.add(bottomAction);
386     }
387     if (anchor != ToolWindowAnchor.RIGHT) {
388       final AnAction rightAction =
389         new ChangeAnchorAction(UIBundle.message("tool.window.move.to.right.action.name"), ToolWindowAnchor.RIGHT);
390       moveGroup.add(rightAction);
391     }
392     group.add(moveGroup);
393
394     DefaultActionGroup resize = new DefaultActionGroup(ActionsBundle.groupText("ResizeToolWindowGroup"), true);
395     resize.add(new ResizeToolWindowAction.Left(myToolWindow, this));
396     resize.add(new ResizeToolWindowAction.Right(myToolWindow, this));
397     resize.add(new ResizeToolWindowAction.Up(myToolWindow, this));
398     resize.add(new ResizeToolWindowAction.Down(myToolWindow, this));
399
400     group.add(resize);
401
402     group.addSeparator();
403     group.add(new HideAction());
404     return group;
405   }
406
407   private DefaultActionGroup createGearPopupGroup() {
408     final DefaultActionGroup group = new DefaultActionGroup();
409
410     if (myAdditionalGearActions != null) {
411       addSorted(group, myAdditionalGearActions);
412       group.addSeparator();
413     }
414     if (myInfo.isDocked()) {
415       group.add(myToggleAutoHideModeAction);
416       group.add(myToggleDockModeAction);
417       group.add(myToggleFloatingModeAction);
418       group.add(myToggleSideModeAction);
419     }
420     else if (myInfo.isFloating()) {
421       group.add(myToggleAutoHideModeAction);
422       group.add(myToggleFloatingModeAction);
423     }
424     else if (myInfo.isSliding()) {
425       group.add(myToggleDockModeAction);
426       group.add(myToggleFloatingModeAction);
427       group.add(myToggleSideModeAction);
428     }
429     return group;
430   }
431
432   private static void addSorted(DefaultActionGroup main, ActionGroup group) {
433     final AnAction[] children = group.getChildren(null);
434     boolean hadSecondary = false;
435     for (AnAction action : children) {
436       if (group.isPrimary(action)) {
437         main.add(action);
438       } else {
439         hadSecondary = true;
440       }
441     }
442     if (hadSecondary) {
443       main.addSeparator();
444       for (AnAction action : children) {
445         if (!group.isPrimary(action)) {
446           main.addAction(action).setAsSecondary(true);
447         }
448       }
449     }
450   }
451
452   /**
453    * @return tool window associated with the decorator.
454    */
455   final ToolWindowImpl getToolWindow() {
456     return myToolWindow;
457   }
458
459   /**
460    * @return last window info applied to the decorator.
461    */
462   @NotNull
463   final WindowInfoImpl getWindowInfo() {
464     return myInfo;
465   }
466
467   public int getHeaderHeight() {
468     return myHeader.getPreferredSize().height;
469   }
470
471   @Override
472   protected final void processComponentEvent(final ComponentEvent e) {
473     super.processComponentEvent(e);
474     if (ComponentEvent.COMPONENT_RESIZED == e.getID()) {
475       fireResized();
476     }
477   }
478
479   private final class ChangeAnchorAction extends AnAction implements DumbAware {
480     private final ToolWindowAnchor myAnchor;
481
482     public ChangeAnchorAction(final String title, final ToolWindowAnchor anchor) {
483       super(title);
484       myAnchor = anchor;
485     }
486
487     @Override
488     public final void actionPerformed(final AnActionEvent e) {
489       fireAnchorChanged(myAnchor);
490     }
491   }
492
493   private final class TogglePinnedModeAction extends ToggleAction implements DumbAware {
494     public TogglePinnedModeAction() {
495       copyFrom(ActionManager.getInstance().getAction(TOGGLE_PINNED_MODE_ACTION_ID));
496     }
497
498     @Override
499     public final boolean isSelected(final AnActionEvent event) {
500       return !myInfo.isAutoHide();
501     }
502
503     @Override
504     public final void setSelected(final AnActionEvent event, final boolean flag) {
505       fireAutoHideChanged(!myInfo.isAutoHide());
506     }
507   }
508
509   private final class ToggleDockModeAction extends ToggleAction implements DumbAware {
510     public ToggleDockModeAction() {
511       copyFrom(ActionManager.getInstance().getAction(TOGGLE_DOCK_MODE_ACTION_ID));
512     }
513
514     @Override
515     public final boolean isSelected(final AnActionEvent event) {
516       return myInfo.isDocked();
517     }
518
519     @Override
520     public final void setSelected(final AnActionEvent event, final boolean flag) {
521       if (myInfo.isDocked()) {
522         fireTypeChanged(ToolWindowType.SLIDING);
523       }
524       else if (myInfo.isSliding()) {
525         fireTypeChanged(ToolWindowType.DOCKED);
526       }
527     }
528   }
529
530   private final class ToggleFloatingModeAction extends ToggleAction implements DumbAware {
531     public ToggleFloatingModeAction() {
532       copyFrom(ActionManager.getInstance().getAction(TOGGLE_FLOATING_MODE_ACTION_ID));
533     }
534
535     @Override
536     public final boolean isSelected(final AnActionEvent event) {
537       return myInfo.isFloating();
538     }
539
540     @Override
541     public final void setSelected(final AnActionEvent event, final boolean flag) {
542       if (myInfo.isFloating()) {
543         fireTypeChanged(myInfo.getInternalType());
544       }
545       else {
546         fireTypeChanged(ToolWindowType.FLOATING);
547       }
548     }
549   }
550
551   private final class ToggleSideModeAction extends ToggleAction implements DumbAware {
552     public ToggleSideModeAction() {
553       copyFrom(ActionManager.getInstance().getAction(TOGGLE_SIDE_MODE_ACTION_ID));
554     }
555
556     @Override
557     public final boolean isSelected(final AnActionEvent event) {
558       return myInfo.isSplit();
559     }
560
561     @Override
562     public final void setSelected(final AnActionEvent event, final boolean flag) {
563       fireSideStatusChanged(flag);
564     }
565
566     @Override
567     public void update(final AnActionEvent e) {
568       super.update(e);
569     }
570   }
571
572   private final class HideAction extends AnAction implements DumbAware {
573     @NonNls public static final String HIDE_ACTIVE_WINDOW_ACTION_ID = InternalDecorator.HIDE_ACTIVE_WINDOW_ACTION_ID;
574
575     public HideAction() {
576       copyFrom(ActionManager.getInstance().getAction(HIDE_ACTIVE_WINDOW_ACTION_ID));
577       getTemplatePresentation().setText(UIBundle.message("tool.window.hide.action.name"));
578     }
579
580     @Override
581     public final void actionPerformed(final AnActionEvent e) {
582       fireHidden();
583     }
584
585     @Override
586     public final void update(final AnActionEvent event) {
587       final Presentation presentation = event.getPresentation();
588       presentation.setEnabled(myInfo.isVisible());
589     }
590   }
591
592
593   private final class ToggleContentUiTypeAction extends ToggleAction implements DumbAware {
594     private ToggleContentUiTypeAction() {
595       copyFrom(ActionManager.getInstance().getAction(TOGGLE_CONTENT_UI_TYPE_ACTION_ID));
596     }
597
598     @Override
599     public boolean isSelected(AnActionEvent e) {
600       return myInfo.getContentUiType() == ToolWindowContentUiType.TABBED;
601     }
602
603     @Override
604     public void setSelected(AnActionEvent e, boolean state) {
605       fireContentUiTypeChanges(state ? ToolWindowContentUiType.TABBED : ToolWindowContentUiType.COMBO);
606     }
607   }
608
609   private final class MyDivider extends JPanel {
610     private boolean myDragging;
611     private Point myLastPoint;
612
613     private MyDivider() {
614       myDragging = false;
615       enableEvents(MouseEvent.MOUSE_EVENT_MASK | MouseEvent.MOUSE_MOTION_EVENT_MASK);
616       setBorder(new DividerBorder());
617     }
618
619     @Override
620     protected final void processMouseMotionEvent(final MouseEvent e) {
621       super.processMouseMotionEvent(e);
622       if (MouseEvent.MOUSE_DRAGGED == e.getID()) {
623         myDragging = true;
624         final ToolWindowAnchor anchor = myInfo.getAnchor();
625         final boolean isVerticalCursor = myInfo.isDocked() ? anchor.isSplitVertically() : anchor.isHorizontal();
626         setCursor(isVerticalCursor ? Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR) : Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
627         final Point point = e.getPoint();
628
629         final Container windowPane = InternalDecorator.this.getParent();
630         myLastPoint = SwingUtilities.convertPoint(this, point, windowPane);
631         myLastPoint.x = Math.min(Math.max(myLastPoint.x, 0), windowPane.getWidth());
632         myLastPoint.y = Math.min(Math.max(myLastPoint.y, 0), windowPane.getHeight());
633
634         final Rectangle bounds = InternalDecorator.this.getBounds();
635         if (anchor == ToolWindowAnchor.TOP) {
636           if (myLastPoint.y < DIVIDER_WIDTH) {
637             myLastPoint.y = DIVIDER_WIDTH;
638           }
639           InternalDecorator.this.setBounds(0, 0, bounds.width, myLastPoint.y);
640         }
641         else if (anchor == ToolWindowAnchor.LEFT) {
642           if (myLastPoint.x < DIVIDER_WIDTH) {
643             myLastPoint.x = DIVIDER_WIDTH;
644           }
645           InternalDecorator.this.setBounds(0, 0, myLastPoint.x, bounds.height);
646         }
647         else if (anchor == ToolWindowAnchor.BOTTOM) {
648           if (myLastPoint.y > windowPane.getHeight() - DIVIDER_WIDTH) {
649             myLastPoint.y = windowPane.getHeight() - DIVIDER_WIDTH;
650           }
651           InternalDecorator.this.setBounds(0, myLastPoint.y, bounds.width, windowPane.getHeight() - myLastPoint.y);
652         }
653         else if (anchor == ToolWindowAnchor.RIGHT) {
654           if (myLastPoint.x > windowPane.getWidth() - DIVIDER_WIDTH) {
655             myLastPoint.x = windowPane.getWidth() - DIVIDER_WIDTH;
656           }
657           InternalDecorator.this.setBounds(myLastPoint.x, 0, windowPane.getWidth() - myLastPoint.x, bounds.height);
658         }
659         InternalDecorator.this.validate();
660       }
661     }
662
663     @Override
664     protected final void processMouseEvent(final MouseEvent e) {
665       super.processMouseEvent(e);
666       final boolean isVerticalCursor = myInfo.isDocked() ? myInfo.getAnchor().isSplitVertically() : myInfo.getAnchor().isHorizontal();
667       switch (e.getID()) {
668         case MouseEvent.MOUSE_MOVED:
669         default:
670           break;
671         case MouseEvent.MOUSE_ENTERED:
672           setCursor(
673             isVerticalCursor ? Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR) : Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
674           break;
675         case MouseEvent.MOUSE_EXITED:
676           if (!myDragging) {
677             setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
678           }
679           break;
680         case MouseEvent.MOUSE_PRESSED:
681           setCursor(
682             isVerticalCursor ? Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR) : Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
683           break;
684         case MouseEvent.MOUSE_RELEASED:
685           myDragging = false;
686           myLastPoint = null;
687           break;
688         case MouseEvent.MOUSE_CLICKED:
689           break;
690       }
691     }
692
693     private final class DividerBorder implements Border {
694       @Override
695       public final void paintBorder(final Component c, final Graphics g, final int x, final int y, final int width, final int height) {
696         final ToolWindowAnchor anchor = myInfo.getAnchor();
697         final boolean isVertical = !anchor.isSplitVertically();
698         final JBColor outer = new JBColor(Color.white, Color.darkGray);
699         if (isVertical) {
700           if (anchor == ToolWindowAnchor.TOP) {
701             g.setColor(outer);
702             UIUtil.drawLine(g, x, y, x + width - 1, y);
703             g.setColor(Color.darkGray);
704             UIUtil.drawLine(g, x, y + height - 1, x + width - 1, y + height - 1);
705           }
706           else {
707             g.setColor(Color.darkGray);
708             UIUtil.drawLine(g, x, y, x + width - 1, y);
709             g.setColor(outer);
710             UIUtil.drawLine(g, x, y + height - 1, x + width - 1, y + height - 1);
711           }
712         }
713         else {
714           if (anchor == ToolWindowAnchor.LEFT) {
715             g.setColor(outer);
716             UIUtil.drawLine(g, x, y, x, y + height - 1);
717             g.setColor(Color.darkGray);
718             UIUtil.drawLine(g, x + width - 1, y, x + width - 1, y + height - 1);
719           }
720           else {
721             g.setColor(Color.darkGray);
722             UIUtil.drawLine(g, x, y, x, y + height - 1);
723             g.setColor(outer);
724             UIUtil.drawLine(g, x + width - 1, y, x + width - 1, y + height - 1);
725           }
726         }
727       }
728
729       @Override
730       public final Insets getBorderInsets(final Component c) {
731         if (c instanceof MyDivider) {
732           return new Insets(1, 1, 1, 1);
733         }
734         return new Insets(0, 0, 0, 0);
735       }
736
737       @Override
738       public final boolean isBorderOpaque() {
739         return true;
740       }
741     }
742   }
743
744   /**
745    * Updates tooltips.
746    */
747   private final class MyKeymapManagerListener implements KeymapManagerListener {
748     @Override
749     public final void activeKeymapChanged(final Keymap keymap) {
750       if (myHeader != null) {
751         myHeader.updateTooltips();
752       }
753     }
754   }
755
756   @Override
757   public void putInfo(@NotNull Map<String, String> info) {
758     info.put("toolWindowTitle", myToolWindow.getTitle());
759
760     final Content selection = myToolWindow.getContentManager().getSelectedContent();
761     if (selection != null) {
762       info.put("toolWindowTab", selection.getTabName());
763     }
764   }
765
766   public void setAdditionalGearActions(@Nullable ActionGroup additionalGearActions) {
767     myAdditionalGearActions = additionalGearActions;
768   }
769 }