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
.actions
.ActivateToolWindowAction
;
19 import com
.intellij
.ide
.ui
.UISettings
;
20 import com
.intellij
.openapi
.actionSystem
.ActionGroup
;
21 import com
.intellij
.openapi
.actionSystem
.ActionManager
;
22 import com
.intellij
.openapi
.actionSystem
.ActionPlaces
;
23 import com
.intellij
.openapi
.actionSystem
.ActionPopupMenu
;
24 import com
.intellij
.openapi
.util
.IconLoader
;
25 import com
.intellij
.openapi
.util
.text
.StringUtil
;
26 import com
.intellij
.openapi
.wm
.ex
.ToolWindowEx
;
27 import com
.intellij
.ui
.PopupHandler
;
28 import com
.intellij
.ui
.MouseDragHelper
;
29 import com
.intellij
.util
.ui
.UIUtil
;
30 import org
.jetbrains
.annotations
.Nullable
;
33 import javax
.swing
.border
.Border
;
35 import java
.awt
.event
.ActionEvent
;
36 import java
.awt
.event
.ActionListener
;
37 import java
.awt
.event
.MouseEvent
;
38 import java
.awt
.event
.MouseMotionAdapter
;
39 import java
.awt
.image
.BufferedImage
;
40 import java
.beans
.PropertyChangeEvent
;
41 import java
.beans
.PropertyChangeListener
;
44 * @author Eugene Belyaev
45 * @author Vladimir Kondratyev
47 public final class StripeButton
extends JToggleButton
implements ActionListener
{
48 private final Color ourBackgroundColor
= new Color(247, 243, 239);
51 * This is analog of Swing mnemomic. We cannot use the standard ones
52 * because it causes typing of "funny" characters into the editor.
54 private int myMnemonic
;
55 private final InternalDecorator myDecorator
;
56 private final MyPropertyChangeListener myToolWindowHandler
;
57 private boolean myPressedWhenSelected
;
59 private JLayeredPane myDragPane
;
60 private final ToolWindowsPane myPane
;
61 private JLabel myDragButtonImage
;
62 private Point myPressedPoint
;
63 private Stripe myLastStripe
;
65 StripeButton(final InternalDecorator decorator
, ToolWindowsPane pane
) {
66 myDecorator
= decorator
;
67 myToolWindowHandler
= new MyPropertyChangeListener();
74 * We are using the trick here: the method does all things that super method does
75 * excepting firing of the MNEMONIC_CHANGED_PROPERTY event. After that mnemonic
76 * doesn't work via standard Swing rules (processing of Alt keystrokes).
78 public void setMnemonic(final int mnemonic
) {
79 throw new UnsupportedOperationException("use setMnemonic2(int)");
82 private void setMnemonic2(final int mnemonic
) {
83 myMnemonic
= mnemonic
;
88 public int getMnemonic2() {
92 WindowInfoImpl
getWindowInfo() {
93 return myDecorator
.getWindowInfo();
98 setBackground(ourBackgroundColor
);
99 final Border border
= BorderFactory
.createEmptyBorder(5, 5, 0, 5);
103 apply(myDecorator
.getWindowInfo());
104 myDecorator
.getToolWindow().addPropertyChangeListener(myToolWindowHandler
);
105 addActionListener(this);
106 addMouseListener(new MyPopupHandler());
107 setRolloverEnabled(true);
110 enableEvents(MouseEvent
.MOUSE_EVENT_MASK
);
112 addMouseMotionListener(new MouseMotionAdapter() {
113 public void mouseDragged(final MouseEvent e
) {
120 public InternalDecorator
getDecorator() {
124 private void processDrag(final MouseEvent e
) {
125 if (!isDraggingNow()) {
126 if (myPressedPoint
== null) return;
127 if (isWithinDeadZone(e
)) return;
129 myDragPane
= findLayeredPane(e
);
130 if (myDragPane
== null) return;
131 final BufferedImage image
= new BufferedImage(getWidth(), getHeight(), BufferedImage
.TYPE_INT_ARGB
);
132 paint(image
.getGraphics());
133 myDragButtonImage
= new JLabel(new ImageIcon(image
)) {
135 public String
toString() {
136 return "Image for: " + StripeButton
.this.toString();
139 myDragPane
.add(myDragButtonImage
, JLayeredPane
.POPUP_LAYER
);
140 myDragButtonImage
.setSize(myDragButtonImage
.getPreferredSize());
144 if (!isDraggingNow()) return;
146 Point xy
= SwingUtilities
.convertPoint(e
.getComponent(), e
.getPoint(), myDragPane
);
147 if (myPressedPoint
!= null) {
148 xy
.x
-= myPressedPoint
.x
;
149 xy
.y
-= myPressedPoint
.y
;
151 myDragButtonImage
.setLocation(xy
);
153 SwingUtilities
.convertPointToScreen(xy
, myDragPane
);
155 final Stripe stripe
= myPane
.getStripeFor(new Rectangle(xy
, myDragButtonImage
.getSize()), (Stripe
)getParent());
156 if (stripe
== null) {
157 if (myLastStripe
!= null) {
158 myLastStripe
.resetDrop();
161 if (myLastStripe
!= null && myLastStripe
!= stripe
) {
162 myLastStripe
.resetDrop();
164 stripe
.processDropButton(this, myDragButtonImage
, xy
);
167 myLastStripe
= stripe
;
170 private boolean isWithinDeadZone(final MouseEvent e
) {
171 return Math
.abs(myPressedPoint
.x
- e
.getPoint().x
) < MouseDragHelper
.DRAG_START_DEADZONE
&& Math
.abs(myPressedPoint
.y
- e
.getPoint().y
) < MouseDragHelper
172 .DRAG_START_DEADZONE
;
176 private static JLayeredPane
findLayeredPane(MouseEvent e
) {
177 if (!(e
.getComponent() instanceof JComponent
)) return null;
178 final JRootPane root
= ((JComponent
)e
.getComponent()).getRootPane();
179 return root
.getLayeredPane();
182 protected void processMouseEvent(final MouseEvent e
) {
183 if (e
.isPopupTrigger() && e
.getComponent().isShowing()) {
184 super.processMouseEvent(e
);
188 if (UIUtil
.isCloseClick(e
)) {
189 myDecorator
.fireHiddenSide();
193 if (e
.getButton() != MouseEvent
.BUTTON1
) return;
195 if (MouseEvent
.MOUSE_PRESSED
== e
.getID()) {
196 myPressedPoint
= e
.getPoint();
197 myPressedWhenSelected
= isSelected();
199 else if (MouseEvent
.MOUSE_RELEASED
== e
.getID()) {
201 myPressedPoint
= null;
202 myDragButtonImage
= null;
204 super.processMouseEvent(e
);
207 public void actionPerformed(final ActionEvent e
) {
208 if (myPressedWhenSelected
) {
209 myDecorator
.fireHidden();
212 myDecorator
.fireActivated();
214 myPressedWhenSelected
= false;
217 public void apply(final WindowInfoImpl info
) {
218 setSelected(info
.isVisible() || info
.isActive());
222 myDecorator
.getToolWindow().removePropertyChangeListener(myToolWindowHandler
);
225 private void showPopup(final Component component
, final int x
, final int y
) {
226 final ActionGroup group
= myDecorator
.createPopupGroup();
227 final ActionPopupMenu popupMenu
= ActionManager
.getInstance().createActionPopupMenu(ActionPlaces
.UNKNOWN
, group
);
228 popupMenu
.getComponent().show(component
, x
, y
);
231 public void updateUI() {
232 setUI(StripeButtonUI
.createUI(this));
233 Font font
= UIUtil
.getButtonFont();
234 if (font
.getSize() % 2 == 1) { // that's a trick. Size of antialiased font isn't properly calculated for fonts with odd size
235 font
= font
.deriveFont(font
.getStyle(), font
.getSize() - 1);
241 * Updates button's text. It composes text as combination of tool window <code>id</code>
242 * and short cut registered in the key map.
245 final String toolWindowId
= getWindowInfo().getId();
246 String text
= toolWindowId
;
247 if (UISettings
.getInstance().SHOW_TOOL_WINDOW_NUMBERS
) {
248 final int mnemonic
= ActivateToolWindowAction
.getMnemonicForToolWindow(toolWindowId
);
249 if (mnemonic
!= -1) {
250 text
= ((char)mnemonic
) + ": " + text
;
251 setMnemonic2(mnemonic
);
261 final ToolWindowImpl window
= myDecorator
.getToolWindow();
262 final boolean toShow
= window
.isAvailable() || window
.isPlaceholderMode();
263 if (UISettings
.getInstance().ALWAYS_SHOW_WINDOW_BUTTONS
) {
269 setEnabled(toShow
&& !window
.isPlaceholderMode());
272 private final class MyPopupHandler
extends PopupHandler
{
273 public void invokePopup(final Component component
, final int x
, final int y
) {
274 showPopup(component
, x
, y
);
278 private final class MyPropertyChangeListener
implements PropertyChangeListener
{
279 public void propertyChange(final PropertyChangeEvent e
) {
280 final String name
= e
.getPropertyName();
281 if (ToolWindowEx
.PROP_AVAILABLE
.equals(name
)) {
284 else if (ToolWindowEx
.PROP_TITLE
.equals(name
)) {
287 else if (ToolWindowEx
.PROP_ICON
.equals(name
)) {
288 final Icon icon
= (Icon
)e
.getNewValue();
289 final Icon disabledIcon
= IconLoader
.getDisabledIcon(icon
);
291 setDisabledIcon(disabledIcon
);
296 private boolean isDraggingNow() {
297 return myDragButtonImage
!= null;
300 private void finishDragging() {
301 if (!isDraggingNow()) return;
302 myDragPane
.remove(myDragButtonImage
);
303 myDragButtonImage
= null;
305 myDragPane
.repaint();
307 if (myLastStripe
!= null) {
308 myLastStripe
.finishDrop();
314 public String
toString() {
315 return StringUtil
.getShortName(getClass().getName()) + " text: " + getText();