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.
16 package com
.intellij
.openapi
.wm
.impl
;
18 import com
.intellij
.ide
.DataManager
;
19 import com
.intellij
.ide
.actions
.ResizeToolWindowAction
;
20 import com
.intellij
.idea
.ActionsBundle
;
21 import com
.intellij
.openapi
.actionSystem
.*;
22 import com
.intellij
.openapi
.actionSystem
.ex
.ActionManagerEx
;
23 import com
.intellij
.openapi
.diagnostic
.Logger
;
24 import com
.intellij
.openapi
.keymap
.Keymap
;
25 import com
.intellij
.openapi
.keymap
.KeymapManagerListener
;
26 import com
.intellij
.openapi
.keymap
.KeymapUtil
;
27 import com
.intellij
.openapi
.keymap
.ex
.KeymapManagerEx
;
28 import com
.intellij
.openapi
.keymap
.ex
.WeakKeymapManagerListener
;
29 import com
.intellij
.openapi
.project
.DumbAware
;
30 import com
.intellij
.openapi
.project
.Project
;
31 import com
.intellij
.openapi
.ui
.TestableUi
;
32 import com
.intellij
.openapi
.util
.Comparing
;
33 import com
.intellij
.openapi
.util
.IconLoader
;
34 import com
.intellij
.openapi
.util
.SystemInfo
;
35 import com
.intellij
.openapi
.wm
.ToolWindowAnchor
;
36 import com
.intellij
.openapi
.wm
.ToolWindowContentUiType
;
37 import com
.intellij
.openapi
.wm
.ToolWindowManager
;
38 import com
.intellij
.openapi
.wm
.ToolWindowType
;
39 import com
.intellij
.openapi
.wm
.ex
.ToolWindowEx
;
40 import com
.intellij
.ui
.InplaceButton
;
41 import com
.intellij
.ui
.UIBundle
;
42 import com
.intellij
.ui
.components
.panels
.NonOpaquePanel
;
43 import com
.intellij
.ui
.components
.panels
.Wrapper
;
44 import com
.intellij
.ui
.content
.Content
;
45 import com
.intellij
.util
.ui
.EmptyIcon
;
46 import com
.intellij
.util
.ui
.UIUtil
;
47 import org
.jetbrains
.annotations
.NonNls
;
50 import javax
.swing
.border
.Border
;
51 import javax
.swing
.event
.EventListenerList
;
53 import java
.awt
.event
.*;
54 import java
.beans
.PropertyChangeEvent
;
55 import java
.beans
.PropertyChangeListener
;
59 * @author Eugene Belyaev
60 * @author Vladimir Kondratyev
62 public final class InternalDecorator
extends JPanel
implements TestableUi
, TypeSafeDataProvider
{
63 private static final Logger LOG
= Logger
.getInstance("#com.intellij.openapi.wm.impl.InternalDecorator");
65 private static final int DIVIDER_WIDTH
= 5;
68 * Icons for buttons in the title bar.
70 private static final Icon ourSlidingIcon
= IconLoader
.getIcon("/general/sliding.png");
71 private static final Icon ourSlidingInactiveIcon
= IconLoader
.getIcon("/general/slidingInactive.png");
72 private static final Icon ourDockedIcon
= IconLoader
.getIcon("/general/docked.png");
73 private static final Icon ourDockedInactiveIcon
= IconLoader
.getIcon("/general/dockedInactive.png");
75 private static final Icon ourAuthoHideOnIcon
= IconLoader
.getIcon("/general/autohideOn.png");
76 private static final Icon ourAuthoHideOnInactiveIcon
= IconLoader
.getIcon("/general/autohideOnInactive.png");
77 private static final Icon ourAuthoHideOffIcon
= IconLoader
.getIcon("/general/autohideOff.png");
78 private static final Icon ourAuthoHideOffInactiveIcon
= IconLoader
.getIcon("/general/autohideOffInactive.png");
80 private static final Icon ourHideIcon
= IconLoader
.getIcon("/general/hideToolWindow.png");
81 private static final Icon ourHideInactiveIcon
= IconLoader
.getIcon("/general/hideToolWindowInactive.png");
83 private static final Icon ourFloatingIcon
= IconLoader
.getIcon("/general/floating.png");
84 private static final Icon ourFloatingInactiveIcon
= IconLoader
.getIcon("/general/floatingInactive.png");
85 private static final Icon ourFixIcon
= IconLoader
.getIcon("/general/fix.png");
86 private static final Icon ourFixInactiveIcon
= IconLoader
.getIcon("/general/fixInactive.png");
88 private static final Icon ourHideSideUp
= IconLoader
.getIcon("/general/hideSideUp.png");
89 private static final Icon ourHideSideUpInactive
= IconLoader
.getIcon("/general/hideSideUpInactive.png");
91 private static final Icon ourHideSideDown
= IconLoader
.getIcon("/general/hideSideDown.png");
92 private static final Icon ourHideSideDownInactive
= IconLoader
.getIcon("/general/hideSideDownInactive.png");
94 private static final Icon ourHideSideLeft
= IconLoader
.getIcon("/general/hideSideLeft.png");
95 private static final Icon ourHideSideLeftInactive
= IconLoader
.getIcon("/general/hideSideLeftInactive.png");
97 private static final Icon ourHideSideRight
= IconLoader
.getIcon("/general/hideSideRight.png");
98 private static final Icon ourHideSideRightInactive
= IconLoader
.getIcon("/general/hideSideRightInactive.png");
100 private Project myProject
;
101 private WindowInfoImpl myInfo
;
102 private final ToolWindowImpl myToolWindow
;
103 private final MyDivider myDivider
;
104 private final TitlePanel myTitlePanel
;
105 private final MyTitleButton myToggleFloatingModeButton
;
106 private final Separator myFloatingDockSeparator
;
107 private final MyTitleButton myToggleDockModeButton
;
108 private final Separator myDockAutoHideSeparator
;
109 private final MyTitleButton myToggleAutoHideModeButton
;
110 private final Separator myAutoHideHideSeparator
;
111 private final MyTitleButton myHideButton
;
112 private final EventListenerList myListenerList
;
116 private final TogglePinnedModeAction myToggleAutoHideModeAction
;
117 private final HideAction myHideAction
;
118 private final HideSideAction myHideSideAction
;
119 private final ToggleDockModeAction myToggleDockModeAction
;
120 private final ToggleFloatingModeAction myToggleFloatingModeAction
;
121 private final ToggleSideModeAction myToggleSideModeAction
;
122 private final ToggleContentUiTypeAction myToggleContentUiTypeAction
;
125 * Catches all event from tool window and modifies decorator's appearance.
127 private final ToolWindowHandler myToolWindowHandler
;
128 private final MyKeymapManagerListener myKeymapManagerListener
;
129 private final WeakKeymapManagerListener myWeakKeymapManagerListener
;
130 @NonNls protected static final String TOGGLE_PINNED_MODE_ACTION_ID
= "TogglePinnedMode";
131 @NonNls protected static final String TOGGLE_DOCK_MODE_ACTION_ID
= "ToggleDockMode";
132 @NonNls protected static final String TOGGLE_FLOATING_MODE_ACTION_ID
= "ToggleFloatingMode";
133 @NonNls protected static final String TOGGLE_SIDE_MODE_ACTION_ID
= "ToggleSideMode";
134 @NonNls protected static final String HIDE_ACTIVE_WINDOW_ACTION_ID
= "HideActiveWindow";
135 @NonNls protected static final String HIDE_ACTIVE_SIDE_WINDOW_ACTION_ID
= "HideSideWindows";
136 @NonNls protected static final String TOGGLE_CONTENT_UI_TYPE_ACTION_ID
= "ToggleContentUiTypeMode";
137 private final MyTitleButton myHideSideButton
;
138 private final JComponent myTitleTabs
;
140 InternalDecorator(final Project project
, final WindowInfoImpl info
, final ToolWindowImpl toolWindow
) {
141 super(new BorderLayout());
143 myToolWindow
= toolWindow
;
144 myToolWindow
.setDecorator(this);
145 myDivider
= new MyDivider();
146 myTitlePanel
= new TitlePanel();
147 myTitleTabs
= toolWindow
.getContentUI().getTabComponent();
149 myToggleFloatingModeAction
= new ToggleFloatingModeAction();
150 myToggleSideModeAction
= new ToggleSideModeAction();
151 myFloatingDockSeparator
= new Separator();
152 myFloatingDockSeparator
.setBorder(BorderFactory
.createEmptyBorder(0, 2, 0, 2));
153 myToggleDockModeAction
= new ToggleDockModeAction();
154 myDockAutoHideSeparator
= new Separator();
155 myToggleAutoHideModeAction
= new TogglePinnedModeAction();
156 myAutoHideHideSeparator
= new Separator();
157 myHideAction
= new HideAction();
158 myHideSideAction
= new HideSideAction();
159 myToggleContentUiTypeAction
= new ToggleContentUiTypeAction();
161 myToggleFloatingModeButton
= new MyTitleButton(myToggleFloatingModeAction
);
162 myToggleDockModeButton
= new MyTitleButton(myToggleDockModeAction
);
163 myToggleAutoHideModeButton
= new MyTitleButton(myToggleAutoHideModeAction
);
164 myHideButton
= new MyTitleButton(myHideAction
);
165 myHideSideButton
= new MyTitleButton(myHideSideAction
);
166 myListenerList
= new EventListenerList();
169 myKeymapManagerListener
= new MyKeymapManagerListener();
170 final KeymapManagerEx keymapManager
= KeymapManagerEx
.getInstanceEx();
171 myWeakKeymapManagerListener
= new WeakKeymapManagerListener(keymapManager
, myKeymapManagerListener
);
172 keymapManager
.addKeymapManagerListener(myWeakKeymapManagerListener
);
176 myToolWindowHandler
= new ToolWindowHandler();
177 myToolWindow
.addPropertyChangeListener(myToolWindowHandler
);
185 * Applies specified decoration.
187 public final void apply(final WindowInfoImpl info
) {
188 if (Comparing
.equal(myInfo
, info
) || myProject
== null || myProject
.isDisposed()) {
193 final boolean active
= info
.isActive();
194 myTitlePanel
.setActive(active
, !info
.isSliding());
196 //todo myToolWindowBorder.setActive(active);
197 myFloatingDockSeparator
.setActive(active
);
198 myDockAutoHideSeparator
.setActive(active
);
199 myAutoHideHideSeparator
.setActive(active
);
202 final ToolWindowAnchor anchor
= myInfo
.getAnchor();
203 if (info
.isSliding()) {
204 myDivider
.invalidate();
205 if (ToolWindowAnchor
.TOP
== anchor
) {
206 add(myDivider
, BorderLayout
.SOUTH
);
208 else if (ToolWindowAnchor
.LEFT
== anchor
) {
209 add(myDivider
, BorderLayout
.EAST
);
211 else if (ToolWindowAnchor
.BOTTOM
== anchor
) {
212 add(myDivider
, BorderLayout
.NORTH
);
214 else if (ToolWindowAnchor
.RIGHT
== anchor
) {
215 add(myDivider
, BorderLayout
.WEST
);
217 myDivider
.setPreferredSize(new Dimension(DIVIDER_WIDTH
, DIVIDER_WIDTH
));
219 else { // docked and floating windows don't have divider
224 if (!info
.isFloating()) {
225 myHideSideButton
.setVisible(true);
226 if (ToolWindowAnchor
.TOP
== anchor
) {
227 myHideSideButton
.setIcon(active ? ourHideSideUp
: ourHideSideUpInactive
);
229 else if (ToolWindowAnchor
.LEFT
== anchor
) {
230 myHideSideButton
.setIcon(active ? ourHideSideLeft
: ourHideSideLeftInactive
);
232 else if (ToolWindowAnchor
.BOTTOM
== anchor
) {
233 myHideSideButton
.setIcon(active ? ourHideSideDown
: ourHideSideDownInactive
);
235 else if (ToolWindowAnchor
.RIGHT
== anchor
) {
236 myHideSideButton
.setIcon(active ? ourHideSideRight
: ourHideSideRightInactive
);
240 myHideSideButton
.setVisible(false);
246 if (myInfo
.isDocked()) {
247 myToggleFloatingModeButton
.setIcon(active ? ourFloatingIcon
: ourFloatingInactiveIcon
);
248 myToggleDockModeButton
.setVisible(true);
249 myDockAutoHideSeparator
.setVisible(true);
250 myToggleDockModeButton
.setIcon(active ? ourSlidingIcon
: ourSlidingInactiveIcon
);
251 myToggleAutoHideModeButton
.setVisible(true);
252 myAutoHideHideSeparator
.setVisible(true);
253 myToggleAutoHideModeButton
.setIcon(active
254 ?
(myInfo
.isAutoHide() ? ourAuthoHideOnIcon
: ourAuthoHideOffIcon
)
255 : (myInfo
.isAutoHide() ? ourAuthoHideOnInactiveIcon
: ourAuthoHideOffInactiveIcon
));
256 myHideButton
.setVisible(true);
258 else if (myInfo
.isFloating()) {
259 myToggleFloatingModeButton
.setIcon(active ? ourFixIcon
: ourFixInactiveIcon
);
260 myToggleDockModeButton
.setVisible(false);
261 myDockAutoHideSeparator
.setVisible(false);
262 myToggleAutoHideModeButton
.setVisible(true);
263 myAutoHideHideSeparator
.setVisible(true);
264 myToggleAutoHideModeButton
.setIcon(active
265 ? myInfo
.isAutoHide() ? ourAuthoHideOnIcon
: ourAuthoHideOffIcon
266 : myInfo
.isAutoHide() ? ourAuthoHideOnInactiveIcon
: ourAuthoHideOffInactiveIcon
);
267 myHideButton
.setVisible(true);
269 else if (myInfo
.isSliding()) {
270 myToggleFloatingModeButton
.setIcon(active ? ourFloatingIcon
: ourFloatingInactiveIcon
);
271 myToggleDockModeButton
.setVisible(true);
272 myDockAutoHideSeparator
.setVisible(true);
273 myToggleDockModeButton
.setIcon(active ? ourDockedIcon
: ourDockedInactiveIcon
);
274 myToggleAutoHideModeButton
.setVisible(false);
275 myAutoHideHideSeparator
.setVisible(false);
276 myHideButton
.setVisible(true);
278 myHideButton
.setIcon(active ? ourHideIcon
: ourHideInactiveIcon
);
284 // Push "apply" request forward
286 if (myInfo
.isFloating() && myInfo
.isVisible()) {
287 final FloatingDecorator floatingDecorator
= (FloatingDecorator
)SwingUtilities
.getAncestorOfClass(FloatingDecorator
.class, this);
288 if (floatingDecorator
!= null) {
289 floatingDecorator
.apply(myInfo
);
293 myToolWindow
.getContentUI().setType(myInfo
.getContentUiType());
296 public void calcData(DataKey key
, DataSink sink
) {
297 if (PlatformDataKeys
.TOOL_WINDOW
.equals(key
)) {
298 sink
.put(PlatformDataKeys
.TOOL_WINDOW
, myToolWindow
);
302 final void addInternalDecoratorListener(final InternalDecoratorListener l
) {
303 myListenerList
.add(InternalDecoratorListener
.class, l
);
306 final void removeInternalDecoratorListener(final InternalDecoratorListener l
) {
307 myListenerList
.remove(InternalDecoratorListener
.class, l
);
310 final void dispose() {
312 myToolWindow
.removePropertyChangeListener(myToolWindowHandler
);
313 KeymapManagerEx
.getInstanceEx().removeKeymapManagerListener(myWeakKeymapManagerListener
);
317 private static String
getToolTipTextByAction(@NonNls final String actionId
, final String description
) {
318 String text
= description
;
319 final String shortcutForAction
= KeymapUtil
.getFirstKeyboardShortcutText(ActionManager
.getInstance().getAction(actionId
));
320 if (shortcutForAction
.length() > 0) {
321 text
+= " " + shortcutForAction
;
326 private void fireAnchorChanged(final ToolWindowAnchor anchor
) {
327 final InternalDecoratorListener
[] listeners
= (InternalDecoratorListener
[])myListenerList
.getListeners(InternalDecoratorListener
.class);
328 for (int i
= 0; i
< listeners
.length
; i
++) {
329 listeners
[i
].anchorChanged(this, anchor
);
333 private void fireAutoHideChanged(final boolean autoHide
) {
334 final InternalDecoratorListener
[] listeners
= (InternalDecoratorListener
[])myListenerList
.getListeners(InternalDecoratorListener
.class);
335 for (int i
= 0; i
< listeners
.length
; i
++) {
336 listeners
[i
].autoHideChanged(this, autoHide
);
341 * Fires event that "hide" button has been pressed.
343 final void fireHidden() {
344 final InternalDecoratorListener
[] listeners
= (InternalDecoratorListener
[])myListenerList
.getListeners(InternalDecoratorListener
.class);
345 for (int i
= 0; i
< listeners
.length
; i
++) {
346 listeners
[i
].hidden(this);
351 * Fires event that "hide" button has been pressed.
353 final void fireHiddenSide() {
354 final InternalDecoratorListener
[] listeners
= (InternalDecoratorListener
[])myListenerList
.getListeners(InternalDecoratorListener
.class);
355 for (int i
= 0; i
< listeners
.length
; i
++) {
356 listeners
[i
].hiddenSide(this);
361 * Fires event that user performed click into the title bar area.
363 final void fireActivated() {
364 final InternalDecoratorListener
[] listeners
= (InternalDecoratorListener
[])myListenerList
.getListeners(InternalDecoratorListener
.class);
365 for (int i
= 0; i
< listeners
.length
; i
++) {
366 listeners
[i
].activated(this);
370 private void fireTypeChanged(final ToolWindowType type
) {
371 final InternalDecoratorListener
[] listeners
= (InternalDecoratorListener
[])myListenerList
.getListeners(InternalDecoratorListener
.class);
372 for (int i
= 0; i
< listeners
.length
; i
++) {
373 listeners
[i
].typeChanged(this, type
);
377 final void fireResized() {
378 final InternalDecoratorListener
[] listeners
= (InternalDecoratorListener
[])myListenerList
.getListeners(InternalDecoratorListener
.class);
379 for (int i
= 0; i
< listeners
.length
; i
++) {
380 listeners
[i
].resized(this);
384 private void fireSideStatusChanged(boolean isSide
) {
385 final InternalDecoratorListener
[] listeners
= (InternalDecoratorListener
[])myListenerList
.getListeners(InternalDecoratorListener
.class);
386 for (int i
= 0; i
< listeners
.length
; i
++) {
387 listeners
[i
].sideStatusChanged(this, isSide
);
391 private void fireContentUiTypeChanges(ToolWindowContentUiType type
) {
392 final InternalDecoratorListener
[] listeners
= (InternalDecoratorListener
[])myListenerList
.getListeners(InternalDecoratorListener
.class);
393 for (int i
= 0; i
< listeners
.length
; i
++) {
394 listeners
[i
].contentUiTypeChanges(this, type
);
398 private void init() {
399 enableEvents(ComponentEvent
.COMPONENT_EVENT_MASK
);
401 myTitlePanel
.addTitle(myTitleTabs
);
403 final JPanel buttonPanel
= new JPanel(new GridBagLayout());
404 buttonPanel
.setOpaque(false);
405 buttonPanel
.setBorder(BorderFactory
.createEmptyBorder(0, 0, 0, 2));
406 buttonPanel
.add(myToggleFloatingModeButton
, new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints
.EAST
, GridBagConstraints
.NONE
,
407 new Insets(0, 0, 0, 0), 0, 0));
408 buttonPanel
.add(myFloatingDockSeparator
, new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints
.CENTER
, GridBagConstraints
.NONE
,
409 new Insets(0, 0, 0, 0), 0, 0));
410 buttonPanel
.add(myToggleDockModeButton
, new GridBagConstraints(2, 0, 1, 1, 0, 0, GridBagConstraints
.CENTER
, GridBagConstraints
.NONE
,
411 new Insets(0, 0, 0, 0), 0, 0));
412 buttonPanel
.add(myDockAutoHideSeparator
, new GridBagConstraints(3, 0, 1, 1, 0, 0, GridBagConstraints
.CENTER
, GridBagConstraints
.NONE
,
413 new Insets(0, 0, 0, 0), 0, 0));
414 buttonPanel
.add(myToggleAutoHideModeButton
, new GridBagConstraints(4, 0, 1, 1, 0, 0, GridBagConstraints
.CENTER
, GridBagConstraints
.NONE
,
415 new Insets(0, 0, 0, 0), 0, 0));
416 buttonPanel
.add(myAutoHideHideSeparator
, new GridBagConstraints(5, 0, 1, 1, 0, 0, GridBagConstraints
.CENTER
, GridBagConstraints
.NONE
,
417 new Insets(0, 0, 0, 0), 0, 0));
418 buttonPanel
.add(myHideButton
, new GridBagConstraints(6, 0, 1, 1, 0, 0, GridBagConstraints
.CENTER
, GridBagConstraints
.NONE
,
419 new Insets(0, 0, 0, 0), 0, 0));
421 myTitlePanel
.addButtons(buttonPanel
, myHideSideButton
);
423 final JPanel contentPane
= new JPanel(new BorderLayout());
424 contentPane
.add(myTitlePanel
, BorderLayout
.NORTH
);
425 JPanel innerPanel
= new JPanel(new BorderLayout());
426 JComponent toolWindowComponent
= myToolWindow
.getComponent();
427 innerPanel
.add(toolWindowComponent
, BorderLayout
.CENTER
);
429 final NonOpaquePanel inner
= new NonOpaquePanel(innerPanel
);
430 inner
.setBorder(new InnerPanelBorder());
432 contentPane
.add(inner
, BorderLayout
.CENTER
);
433 add(contentPane
, BorderLayout
.CENTER
);
436 registerKeyboardAction(new ActionListener() {
437 public void actionPerformed(final ActionEvent e
) {
438 ToolWindowManager
.getInstance(myProject
).activateEditorComponent();
440 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0), JComponent
.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
);
443 private static class InnerPanelBorder
implements Border
{
444 public void paintBorder(final Component c
, final Graphics g
, final int x
, final int y
, final int width
, final int height
) {
445 g
.setColor(UIUtil
.getBorderInactiveColor());
446 UIUtil
.drawLine(g
, x
, y
, x
, y
+ height
- 2);
447 UIUtil
.drawLine(g
, x
+ width
- 1, y
, x
+ width
- 1, y
+ height
- 2);
448 UIUtil
.drawLine(g
, x
+ 1, y
+ height
- 1, x
+ width
- 2, y
+ height
- 1);
451 public Insets
getBorderInsets(final Component c
) {
452 return new Insets(0, 1, 1, 1);
455 public boolean isBorderOpaque() {
461 public final ActionGroup
createPopupGroup() {
462 final DefaultActionGroup group
= new DefaultActionGroup();
464 if (myInfo
.isDocked()) {
465 group
.add(myToggleAutoHideModeAction
);
466 group
.add(myToggleDockModeAction
);
467 group
.add(myToggleFloatingModeAction
);
468 group
.add(myToggleSideModeAction
);
470 else if (myInfo
.isFloating()) {
471 group
.add(myToggleAutoHideModeAction
);
472 group
.add(myToggleFloatingModeAction
);
474 else if (myInfo
.isSliding()) {
475 group
.add(myToggleDockModeAction
);
476 group
.add(myToggleFloatingModeAction
);
479 group
.add(myToggleContentUiTypeAction
);
481 final DefaultActionGroup moveGroup
= new DefaultActionGroup(UIBundle
.message("tool.window.move.to.action.group.name"), true);
482 final ToolWindowAnchor anchor
= myInfo
.getAnchor();
483 if (anchor
!= ToolWindowAnchor
.TOP
) {
484 final AnAction topAction
= new ChangeAnchorAction(UIBundle
.message("tool.window.move.to.top.action.name"), ToolWindowAnchor
.TOP
);
485 moveGroup
.add(topAction
);
487 if (anchor
!= ToolWindowAnchor
.LEFT
) {
488 final AnAction leftAction
= new ChangeAnchorAction(UIBundle
.message("tool.window.move.to.left.action.name"), ToolWindowAnchor
.LEFT
);
489 moveGroup
.add(leftAction
);
491 if (anchor
!= ToolWindowAnchor
.BOTTOM
) {
492 final AnAction bottomAction
=
493 new ChangeAnchorAction(UIBundle
.message("tool.window.move.to.bottom.action.name"), ToolWindowAnchor
.BOTTOM
);
494 moveGroup
.add(bottomAction
);
496 if (anchor
!= ToolWindowAnchor
.RIGHT
) {
497 final AnAction rightAction
=
498 new ChangeAnchorAction(UIBundle
.message("tool.window.move.to.right.action.name"), ToolWindowAnchor
.RIGHT
);
499 moveGroup
.add(rightAction
);
501 group
.add(moveGroup
);
503 DefaultActionGroup resize
= new DefaultActionGroup(ActionsBundle
.groupText("ResizeToolWindowGroup"), true);
504 resize
.add(new ResizeToolWindowAction
.Left(myToolWindow
, this));
505 resize
.add(new ResizeToolWindowAction
.Right(myToolWindow
, this));
506 resize
.add(new ResizeToolWindowAction
.Up(myToolWindow
, this));
507 resize
.add(new ResizeToolWindowAction
.Down(myToolWindow
, this));
511 group
.addSeparator();
512 group
.add(myHideAction
);
517 * @return tool window associated with the decorator.
519 final ToolWindowImpl
getToolWindow() {
524 * @return last window info applied to the decorator.
526 final WindowInfoImpl
getWindowInfo() {
530 protected final void processComponentEvent(final ComponentEvent e
) {
531 super.processComponentEvent(e
);
532 if (ComponentEvent
.COMPONENT_RESIZED
== e
.getID()) {
537 private void updateTitle() {
538 final StringBuffer fullTitle
= new StringBuffer();
539 // Due to JDK's bug #4234645 we cannot support custom decoration on Linux platform.
540 // The prblem is that Window.setLocation() doesn't work properly wjen the dialod is displayable.
541 // Therefore we use native WM decoration. When the dialog has native decoration we show window ID
542 // in the dialog's title and window title at the custom title panel. If the custom decoration
543 // is used we show composite string at the custom title panel.
544 // TODO[vova] investigate the problem under Mac OSX.
545 if (SystemInfo
.isWindows
|| !myInfo
.isFloating()) {
546 fullTitle
.append(myInfo
.getId());
547 final String title
= myToolWindow
.getTitle();
548 if (title
!= null && title
.length() > 0) {
549 fullTitle
.append(" - ").append(title
);
552 else { // Unixes ans MacOSX go here when tool window is in floating mode
553 final String title
= myToolWindow
.getTitle();
554 if (title
!= null && title
.length() > 0) {
555 fullTitle
.append(title
);
560 private void updateTooltips() {
561 if (myInfo
.isDocked()) {
562 myToggleDockModeButton
563 .setToolTipText(getToolTipTextByAction(TOGGLE_DOCK_MODE_ACTION_ID
, UIBundle
.message("tool.wondow.undock.action.name")));
565 else if (myInfo
.isSliding()) {
566 myToggleDockModeButton
567 .setToolTipText(getToolTipTextByAction(TOGGLE_DOCK_MODE_ACTION_ID
, UIBundle
.message("tool.wondow.dock.action.name")));
569 myToggleFloatingModeButton
.setToolTipText(getToolTipTextByAction(TOGGLE_FLOATING_MODE_ACTION_ID
, myInfo
.isFloating() ? UIBundle
570 .message("tool.wondow.fix.action.name") : UIBundle
.message("tool.wondow.float.action.name")));
571 myToggleAutoHideModeButton
.setToolTipText(getToolTipTextByAction(TOGGLE_PINNED_MODE_ACTION_ID
, myInfo
.isAutoHide() ? UIBundle
572 .message("tool.wondow.pin.action.name") : UIBundle
.message("tool.wondow.unpin.action.name")));
573 myHideButton
.setToolTipText(getToolTipTextByAction(HIDE_ACTIVE_WINDOW_ACTION_ID
, UIBundle
.message("tool.window.hide.action.name")));
575 .setToolTipText(getToolTipTextByAction(HIDE_ACTIVE_SIDE_WINDOW_ACTION_ID
, UIBundle
.message("tool.window.hideSide.action.name")));
578 private final class ChangeAnchorAction
extends AnAction
implements DumbAware
{
579 private final ToolWindowAnchor myAnchor
;
581 public ChangeAnchorAction(final String title
, final ToolWindowAnchor anchor
) {
586 public final void actionPerformed(final AnActionEvent e
) {
587 fireAnchorChanged(myAnchor
);
591 private final class HideAction
extends AnAction
implements DumbAware
{
592 @NonNls public static final String HIDE_ACTIVE_WINDOW_ACTION_ID
= InternalDecorator
.HIDE_ACTIVE_WINDOW_ACTION_ID
;
594 public HideAction() {
595 copyFrom(ActionManager
.getInstance().getAction(HIDE_ACTIVE_WINDOW_ACTION_ID
));
596 getTemplatePresentation().setText(UIBundle
.message("tool.window.hide.action.name"));
599 public final void actionPerformed(final AnActionEvent e
) {
603 public final void update(final AnActionEvent event
) {
604 final Presentation presentation
= event
.getPresentation();
605 presentation
.setEnabled(myInfo
.isVisible());
609 private final class HideSideAction
extends AnAction
implements DumbAware
{
610 @NonNls public static final String HIDE_ACTIVE_SIDE_WINDOW_ACTION_ID
= InternalDecorator
.HIDE_ACTIVE_SIDE_WINDOW_ACTION_ID
;
612 public HideSideAction() {
613 copyFrom(ActionManager
.getInstance().getAction(HIDE_ACTIVE_SIDE_WINDOW_ACTION_ID
));
614 getTemplatePresentation().setText(UIBundle
.message("tool.window.hideSide.action.name"));
617 public final void actionPerformed(final AnActionEvent e
) {
621 public final void update(final AnActionEvent event
) {
622 final Presentation presentation
= event
.getPresentation();
623 presentation
.setEnabled(myInfo
.isVisible());
627 private final class TogglePinnedModeAction
extends ToggleAction
implements DumbAware
{
628 public TogglePinnedModeAction() {
629 copyFrom(ActionManager
.getInstance().getAction(TOGGLE_PINNED_MODE_ACTION_ID
));
632 public final boolean isSelected(final AnActionEvent event
) {
633 return !myInfo
.isAutoHide();
636 public final void setSelected(final AnActionEvent event
, final boolean flag
) {
637 fireAutoHideChanged(!myInfo
.isAutoHide());
641 private final class ToggleDockModeAction
extends ToggleAction
implements DumbAware
{
642 public ToggleDockModeAction() {
643 copyFrom(ActionManager
.getInstance().getAction(TOGGLE_DOCK_MODE_ACTION_ID
));
646 public final boolean isSelected(final AnActionEvent event
) {
647 return myInfo
.isDocked();
650 public final void setSelected(final AnActionEvent event
, final boolean flag
) {
651 if (myInfo
.isDocked()) {
652 fireTypeChanged(ToolWindowType
.SLIDING
);
654 else if (myInfo
.isSliding()) {
655 fireTypeChanged(ToolWindowType
.DOCKED
);
660 private final class ToggleFloatingModeAction
extends ToggleAction
implements DumbAware
{
661 public ToggleFloatingModeAction() {
662 copyFrom(ActionManager
.getInstance().getAction(TOGGLE_FLOATING_MODE_ACTION_ID
));
665 public final boolean isSelected(final AnActionEvent event
) {
666 return myInfo
.isFloating();
669 public final void setSelected(final AnActionEvent event
, final boolean flag
) {
670 if (myInfo
.isFloating()) {
671 fireTypeChanged(myInfo
.getInternalType());
674 fireTypeChanged(ToolWindowType
.FLOATING
);
679 private final class ToggleSideModeAction
extends ToggleAction
implements DumbAware
{
680 public ToggleSideModeAction() {
681 copyFrom(ActionManager
.getInstance().getAction(TOGGLE_SIDE_MODE_ACTION_ID
));
684 public final boolean isSelected(final AnActionEvent event
) {
685 return myInfo
.isSplit();
688 public final void setSelected(final AnActionEvent event
, final boolean flag
) {
689 fireSideStatusChanged(flag
);
693 public void update(final AnActionEvent e
) {
698 private final class ToggleContentUiTypeAction
extends ToggleAction
implements DumbAware
{
699 private ToggleContentUiTypeAction() {
700 copyFrom(ActionManager
.getInstance().getAction(TOGGLE_CONTENT_UI_TYPE_ACTION_ID
));
704 public boolean isSelected(AnActionEvent e
) {
705 return myInfo
.getContentUiType() == ToolWindowContentUiType
.TABBED
;
709 public void setSelected(AnActionEvent e
, boolean state
) {
710 fireContentUiTypeChanges(state ? ToolWindowContentUiType
.TABBED
: ToolWindowContentUiType
.COMBO
);
714 private final class MyDivider
extends JPanel
{
715 private boolean myDragging
;
716 private Point myLastPoint
;
720 enableEvents(MouseEvent
.MOUSE_EVENT_MASK
| MouseEvent
.MOUSE_MOTION_EVENT_MASK
);
721 setBorder(new DividerBorder());
724 protected final void processMouseMotionEvent(final MouseEvent e
) {
725 super.processMouseMotionEvent(e
);
726 if (MouseEvent
.MOUSE_DRAGGED
== e
.getID()) {
728 final ToolWindowAnchor anchor
= myInfo
.getAnchor();
729 final boolean isVertical
= anchor
== ToolWindowAnchor
.TOP
|| anchor
== ToolWindowAnchor
.BOTTOM
;
730 setCursor(isVertical ? Cursor
.getPredefinedCursor(9) : Cursor
.getPredefinedCursor(11));
731 final Point point
= e
.getPoint();
733 final Container windowPane
= InternalDecorator
.this.getParent();
734 myLastPoint
= SwingUtilities
.convertPoint(this, point
, windowPane
);
735 myLastPoint
.x
= Math
.min(Math
.max(myLastPoint
.x
, 0), windowPane
.getWidth());
736 myLastPoint
.y
= Math
.min(Math
.max(myLastPoint
.y
, 0), windowPane
.getHeight());
738 final Rectangle bounds
= InternalDecorator
.this.getBounds();
739 if (anchor
== ToolWindowAnchor
.TOP
) {
740 if (myLastPoint
.y
< DIVIDER_WIDTH
) {
741 myLastPoint
.y
= DIVIDER_WIDTH
;
743 InternalDecorator
.this.setBounds(0, 0, bounds
.width
, myLastPoint
.y
);
745 else if (anchor
== ToolWindowAnchor
.LEFT
) {
746 if (myLastPoint
.x
< DIVIDER_WIDTH
) {
747 myLastPoint
.x
= DIVIDER_WIDTH
;
749 InternalDecorator
.this.setBounds(0, 0, myLastPoint
.x
, bounds
.height
);
751 else if (anchor
== ToolWindowAnchor
.BOTTOM
) {
752 if (myLastPoint
.y
> windowPane
.getHeight() - DIVIDER_WIDTH
) {
753 myLastPoint
.y
= windowPane
.getHeight() - DIVIDER_WIDTH
;
755 InternalDecorator
.this.setBounds(0, myLastPoint
.y
, bounds
.width
, windowPane
.getHeight() - myLastPoint
.y
);
757 else if (anchor
== ToolWindowAnchor
.RIGHT
) {
758 if (myLastPoint
.x
> windowPane
.getWidth() - DIVIDER_WIDTH
) {
759 myLastPoint
.x
= windowPane
.getWidth() - DIVIDER_WIDTH
;
761 InternalDecorator
.this.setBounds(myLastPoint
.x
, 0, windowPane
.getWidth() - myLastPoint
.x
, bounds
.height
);
763 InternalDecorator
.this.validate();
767 protected final void processMouseEvent(final MouseEvent e
) {
768 super.processMouseEvent(e
);
769 final ToolWindowAnchor anchor
= myInfo
.getAnchor();
770 final boolean isVerticalCursor
= anchor
== ToolWindowAnchor
.TOP
|| anchor
== ToolWindowAnchor
.BOTTOM
;
772 case MouseEvent
.MOUSE_MOVED
:
775 case MouseEvent
.MOUSE_ENTERED
:
777 isVerticalCursor ? Cursor
.getPredefinedCursor(Cursor
.S_RESIZE_CURSOR
) : Cursor
.getPredefinedCursor(Cursor
.E_RESIZE_CURSOR
));
779 case MouseEvent
.MOUSE_EXITED
:
781 setCursor(Cursor
.getPredefinedCursor(Cursor
.DEFAULT_CURSOR
));
784 case MouseEvent
.MOUSE_PRESSED
:
786 isVerticalCursor ? Cursor
.getPredefinedCursor(Cursor
.S_RESIZE_CURSOR
) : Cursor
.getPredefinedCursor(Cursor
.E_RESIZE_CURSOR
));
788 case MouseEvent
.MOUSE_RELEASED
:
792 case MouseEvent
.MOUSE_CLICKED
:
797 private final class DividerBorder
implements Border
{
798 public final void paintBorder(final Component c
, final Graphics g
, final int x
, final int y
, final int width
, final int height
) {
799 final ToolWindowAnchor anchor
= myInfo
.getAnchor();
800 final boolean isVertical
= anchor
== ToolWindowAnchor
.TOP
|| anchor
== ToolWindowAnchor
.BOTTOM
;
802 if (anchor
== ToolWindowAnchor
.TOP
) {
803 g
.setColor(Color
.white
);
804 UIUtil
.drawLine(g
, x
, y
, x
+ width
- 1, y
);
805 g
.setColor(Color
.darkGray
);
806 UIUtil
.drawLine(g
, x
, y
+ height
- 1, x
+ width
- 1, y
+ height
- 1);
809 g
.setColor(Color
.darkGray
);
810 UIUtil
.drawLine(g
, x
, y
, x
+ width
- 1, y
);
811 g
.setColor(Color
.white
);
812 UIUtil
.drawLine(g
, x
, y
+ height
- 1, x
+ width
- 1, y
+ height
- 1);
816 if (anchor
== ToolWindowAnchor
.LEFT
) {
817 g
.setColor(Color
.white
);
818 UIUtil
.drawLine(g
, x
, y
, x
, y
+ height
- 1);
819 g
.setColor(Color
.darkGray
);
820 UIUtil
.drawLine(g
, x
+ width
- 1, y
, x
+ width
- 1, y
+ height
- 1);
823 g
.setColor(Color
.darkGray
);
824 UIUtil
.drawLine(g
, x
, y
, x
, y
+ height
- 1);
825 g
.setColor(Color
.white
);
826 UIUtil
.drawLine(g
, x
+ width
- 1, y
, x
+ width
- 1, y
+ height
- 1);
831 public final Insets
getBorderInsets(final Component c
) {
832 if (c
instanceof MyDivider
) {
833 return new Insets(1, 1, 1, 1);
835 return new Insets(0, 0, 0, 0);
838 public final boolean isBorderOpaque() {
847 private final class MyKeymapManagerListener
implements KeymapManagerListener
{
848 public final void activeKeymapChanged(final Keymap keymap
) {
854 private static final class MyTitleButton
extends Wrapper
implements ActionListener
{
856 private final InplaceButton myButton
;
857 private final AnAction myAction
;
859 public MyTitleButton(AnAction action
) {
861 myButton
= new InplaceButton(null, new EmptyIcon(16), this);
862 //myButton.setTransform(0, -1);
863 setContent(myButton
);
867 public void actionPerformed(final ActionEvent e
) {
868 final DataContext dataContext
= DataManager
.getInstance().getDataContext(this);
869 final ActionManagerEx actionManager
= ActionManagerEx
.getInstanceEx();
870 final AnActionEvent event
=
871 new AnActionEvent(null, dataContext
, ActionPlaces
.UNKNOWN
, myAction
.getTemplatePresentation(), ActionManager
.getInstance(), 0);
872 actionManager
.fireBeforeActionPerformed(myAction
, dataContext
, event
);
873 final Component component
= PlatformDataKeys
.CONTEXT_COMPONENT
.getData(dataContext
);
874 if (component
!= null && !component
.isShowing()) {
877 myAction
.actionPerformed(event
);
880 public void setIcon(final Icon icon
) {
881 myButton
.setIcon(icon
);
885 public void setToolTipText(final String text
) {
886 myButton
.setToolTipText(text
);
890 //private static final class MyTitleButton extends FixedSizeButton {
891 // public MyTitleButton() {
893 // setBorder(BorderFactory.createEmptyBorder());
896 // public final void addActionListener(final AnAction action) {
897 // final DataContext dataContext = DataManager.getInstance().getDataContext(this);
899 // final ActionListener actionListener = new ActionListener() {
900 // public void actionPerformed(final ActionEvent e) {
901 // final ActionManagerEx actionManager = ActionManagerEx.getInstanceEx();
902 // actionManager.fireBeforeActionPerformed(action, dataContext);
903 // final Component component = ((Component) dataContext.getData(DataConstantsEx.CONTEXT_COMPONENT));
904 // if (component != null && !component.isShowing()) {
907 // action.actionPerformed(new AnActionEvent(null, dataContext, ActionPlaces.UNKNOWN, action.getTemplatePresentation(),
908 // ActionManager.getInstance(),
913 // addActionListener(actionListener);
917 // * Some UIs paint only opague buttons. It causes that button has gray background color.
918 // * To prevent this I don't allow to change UI.
920 // public final void updateUI() {
921 // setUI(new MyTitleButtonUI());
923 // setRolloverEnabled(true);
924 // setContentAreaFilled(false);
928 //private static final class MyTitleButtonUI extends MetalButtonUI {
929 // @Override protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect) {
930 // MyTitleButton btn = (MyTitleButton)c;
931 // if (btn.getModel().isArmed() && btn.getModel().isPressed()) {
932 // iconRect = new Rectangle(iconRect.x - 1, iconRect.y, iconRect.width, iconRect.height);
934 // super.paintIcon(g, c, iconRect);
938 private static final class Separator
extends JLabel
{
939 private static final Icon ourActiveSeparator
= IconLoader
.getIcon("/general/separator.png");
940 private static final Icon ourInactiveSeparator
= IconLoader
.getIcon("/general/inactiveSeparator.png");
942 public final void setActive(final boolean active
) {
943 setIcon(active ? ourActiveSeparator
: ourInactiveSeparator
);
946 public final void updateUI() {
948 setBorder(BorderFactory
.createEmptyBorder(0, 3, 0, 3));
953 * Synchronizes decorator with IdeToolWindow changes.
955 private final class ToolWindowHandler
implements PropertyChangeListener
{
956 public final void propertyChange(final PropertyChangeEvent e
) {
957 final String name
= e
.getPropertyName();
958 if (ToolWindowEx
.PROP_TITLE
.equals(name
)) {
965 public TitlePanel
getTitlePanel() {
969 public void putInfo(Map
<String
, String
> info
) {
970 info
.put("toolWindowTitle", myToolWindow
.getTitle());
972 final Content selection
= myToolWindow
.getContentManager().getSelectedContent();
973 if (selection
!= null) {
974 info
.put("toolWindowTab", selection
.getTabName());