toolwindows: stretching for undocked mode
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / wm / impl / InternalDecorator.java
blob6e07ab8b7eaca46eb98108891ddf85df0101c03c
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
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;
49 import javax.swing.*;
50 import javax.swing.border.Border;
51 import javax.swing.event.EventListenerList;
52 import java.awt.*;
53 import java.awt.event.*;
54 import java.beans.PropertyChangeEvent;
55 import java.beans.PropertyChangeListener;
56 import java.util.Map;
58 /**
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;
114 * Actions
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());
142 myProject = project;
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);
174 init();
176 myToolWindowHandler = new ToolWindowHandler();
177 myToolWindow.addPropertyChangeListener(myToolWindowHandler);
181 apply(info);
185 * Applies specified decoration.
187 public final void apply(final WindowInfoImpl info) {
188 if (Comparing.equal(myInfo, info) || myProject == null || myProject.isDisposed()) {
189 return;
191 myInfo = info;
192 // Active
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);
201 // Anchor
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
220 remove(myDivider);
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);
239 else {
240 myHideSideButton.setVisible(false);
243 validate();
244 repaint();
245 // Type
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);
281 updateTitle();
282 updateTooltips();
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() {
311 removeAll();
312 myToolWindow.removePropertyChangeListener(myToolWindowHandler);
313 KeymapManagerEx.getInstanceEx().removeKeymapManagerListener(myWeakKeymapManagerListener);
314 myProject = null;
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;
323 return text;
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);
400 // Compose title bar
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);
435 // Add listeners
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() {
456 return true;
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));
509 group.add(resize);
511 group.addSeparator();
512 group.add(myHideAction);
513 return group;
517 * @return tool window associated with the decorator.
519 final ToolWindowImpl getToolWindow() {
520 return myToolWindow;
524 * @return last window info applied to the decorator.
526 final WindowInfoImpl getWindowInfo() {
527 return myInfo;
530 protected final void processComponentEvent(final ComponentEvent e) {
531 super.processComponentEvent(e);
532 if (ComponentEvent.COMPONENT_RESIZED == e.getID()) {
533 fireResized();
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")));
574 myHideSideButton
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) {
582 super(title);
583 myAnchor = 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) {
600 fireHidden();
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) {
618 fireHiddenSide();
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());
673 else {
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);
692 @Override
693 public void update(final AnActionEvent e) {
694 super.update(e);
698 private final class ToggleContentUiTypeAction extends ToggleAction implements DumbAware {
699 private ToggleContentUiTypeAction() {
700 copyFrom(ActionManager.getInstance().getAction(TOGGLE_CONTENT_UI_TYPE_ACTION_ID));
703 @Override
704 public boolean isSelected(AnActionEvent e) {
705 return myInfo.getContentUiType() == ToolWindowContentUiType.TABBED;
708 @Override
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;
718 public MyDivider() {
719 myDragging = false;
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()) {
727 myDragging = true;
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;
771 switch (e.getID()) {
772 case MouseEvent.MOUSE_MOVED:
773 default:
774 break;
775 case MouseEvent.MOUSE_ENTERED:
776 setCursor(
777 isVerticalCursor ? Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR) : Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
778 break;
779 case MouseEvent.MOUSE_EXITED:
780 if (!myDragging) {
781 setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
783 break;
784 case MouseEvent.MOUSE_PRESSED:
785 setCursor(
786 isVerticalCursor ? Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR) : Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
787 break;
788 case MouseEvent.MOUSE_RELEASED:
789 myDragging = false;
790 myLastPoint = null;
791 break;
792 case MouseEvent.MOUSE_CLICKED:
793 break;
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;
801 if (isVertical) {
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);
808 else {
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);
815 else {
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);
822 else {
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() {
839 return true;
845 * Updates tooltips.
847 private final class MyKeymapManagerListener implements KeymapManagerListener {
848 public final void activeKeymapChanged(final Keymap keymap) {
849 updateTooltips();
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) {
860 myAction = action;
861 myButton = new InplaceButton(null, new EmptyIcon(16), this);
862 //myButton.setTransform(0, -1);
863 setContent(myButton);
864 setOpaque(false);
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()) {
875 return;
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() {
892 // super(16);
893 // setBorder(BorderFactory.createEmptyBorder());
894 // }
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()) {
905 // return;
906 // }
907 // action.actionPerformed(new AnActionEvent(null, dataContext, ActionPlaces.UNKNOWN, action.getTemplatePresentation(),
908 // ActionManager.getInstance(),
909 // 0));
910 // }
911 // };
913 // addActionListener(actionListener);
914 // }
916 // /**
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.
919 // */
920 // public final void updateUI() {
921 // setUI(new MyTitleButtonUI());
922 // setOpaque(false);
923 // setRolloverEnabled(true);
924 // setContentAreaFilled(false);
925 // }
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);
933 // }
934 // super.paintIcon(g, c, iconRect);
935 // }
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() {
947 super.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)) {
959 updateTitle();
965 public TitlePanel getTitlePanel() {
966 return myTitlePanel;
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());