sticky documentation popup [take 1]
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / wm / impl / StripeButton.java
blobbe8c999427c6bb256a13011e7d5632f9b32d8453
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.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;
32 import javax.swing.*;
33 import javax.swing.border.Border;
34 import java.awt.*;
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;
43 /**
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);
50 /**
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();
68 myPane = pane;
70 init();
73 /**
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;
84 revalidate();
85 repaint();
88 public int getMnemonic2() {
89 return myMnemonic;
92 WindowInfoImpl getWindowInfo() {
93 return myDecorator.getWindowInfo();
96 private void init() {
97 setFocusable(false);
98 setBackground(ourBackgroundColor);
99 final Border border = BorderFactory.createEmptyBorder(5, 5, 0, 5);
100 setBorder(border);
101 updateText();
102 updateState();
103 apply(myDecorator.getWindowInfo());
104 myDecorator.getToolWindow().addPropertyChangeListener(myToolWindowHandler);
105 addActionListener(this);
106 addMouseListener(new MyPopupHandler());
107 setRolloverEnabled(true);
108 setOpaque(false);
110 enableEvents(MouseEvent.MOUSE_EVENT_MASK);
112 addMouseMotionListener(new MouseMotionAdapter() {
113 public void mouseDragged(final MouseEvent e) {
114 processDrag(e);
120 public InternalDecorator getDecorator() {
121 return myDecorator;
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());
141 setVisible(false);
142 myPane.startDrag();
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();
160 } else {
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;
175 @Nullable
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);
185 return;
188 if (UIUtil.isCloseClick(e)) {
189 myDecorator.fireHiddenSide();
190 return;
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()) {
200 finishDragging();
201 myPressedPoint = null;
202 myDragButtonImage = null;
204 super.processMouseEvent(e);
207 public void actionPerformed(final ActionEvent e) {
208 if (myPressedWhenSelected) {
209 myDecorator.fireHidden();
211 else {
212 myDecorator.fireActivated();
214 myPressedWhenSelected = false;
217 public void apply(final WindowInfoImpl info) {
218 setSelected(info.isVisible() || info.isActive());
221 void dispose() {
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);
237 setFont(font);
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.
244 void updateText() {
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);
253 else {
254 setMnemonic2(0);
257 setText(text);
260 void updateState() {
261 final ToolWindowImpl window = myDecorator.getToolWindow();
262 final boolean toShow = window.isAvailable() || window.isPlaceholderMode();
263 if (UISettings.getInstance().ALWAYS_SHOW_WINDOW_BUTTONS) {
264 setVisible(true);
266 else {
267 setVisible(toShow);
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)) {
282 updateState();
284 else if (ToolWindowEx.PROP_TITLE.equals(name)) {
285 updateText();
287 else if (ToolWindowEx.PROP_ICON.equals(name)) {
288 final Icon icon = (Icon)e.getNewValue();
289 final Icon disabledIcon = IconLoader.getDisabledIcon(icon);
290 setIcon(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;
304 myPane.stopDrag();
305 myDragPane.repaint();
306 setVisible(true);
307 if (myLastStripe != null) {
308 myLastStripe.finishDrop();
309 myLastStripe = null;
314 public String toString() {
315 return StringUtil.getShortName(getClass().getName()) + " text: " + getText();