sticky documentation popup [take 1]
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / wm / impl / IdeGlassPaneImpl.java
blobb846457682a321f478d9864506b34e419fb25992
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.IdeEventQueue;
19 import com.intellij.openapi.Disposable;
20 import com.intellij.openapi.ui.Painter;
21 import com.intellij.openapi.ui.impl.GlassPaneDialogWrapperPeer;
22 import com.intellij.openapi.util.Disposer;
24 import javax.swing.*;
25 import javax.swing.event.MenuDragMouseEvent;
26 import java.awt.*;
27 import java.awt.event.MouseEvent;
28 import java.awt.event.MouseListener;
29 import java.awt.event.MouseMotionListener;
30 import java.awt.event.MouseWheelEvent;
31 import java.lang.ref.WeakReference;
32 import java.util.*;
34 public class IdeGlassPaneImpl extends JPanel implements IdeGlassPaneEx, IdeEventQueue.EventDispatcher, Painter.Listener {
36 private final Set<EventListener> myMouseListeners = new LinkedHashSet<EventListener>();
37 private final JRootPane myRootPane;
39 private final WeakReference<Component> myCurrentOverComponent = new WeakReference<Component>(null);
40 private final WeakReference<Component> myMousePressedComponent = new WeakReference<Component>(null);
42 private final Set<Painter> myPainters = new LinkedHashSet<Painter>();
43 private final Map<Painter, Component> myPainter2Component = new LinkedHashMap<Painter, Component>();
45 private boolean myPaintingActive;
46 private boolean myPreprocessorActive;
48 public IdeGlassPaneImpl(JRootPane rootPane) {
49 myRootPane = rootPane;
50 setOpaque(false);
51 setVisible(false);
52 setLayout(null);
55 public boolean dispatch(final AWTEvent e) {
56 boolean dispatched = false;
58 if (e instanceof MouseEvent) {
59 final MouseEvent me = (MouseEvent)e;
60 Window eventWindow = me.getComponent() instanceof Window ? ((Window)me.getComponent()) : SwingUtilities.getWindowAncestor(me.getComponent());
61 final Window thisGlassWindow = SwingUtilities.getWindowAncestor(myRootPane);
62 if (eventWindow != thisGlassWindow) return false;
65 if (e.getID() == MouseEvent.MOUSE_PRESSED || e.getID() == MouseEvent.MOUSE_RELEASED || e.getID() == MouseEvent.MOUSE_CLICKED) {
66 dispatched = preprocess((MouseEvent)e, false);
67 } else if (e.getID() == MouseEvent.MOUSE_MOVED || e.getID() == MouseEvent.MOUSE_DRAGGED) {
68 dispatched = preprocess((MouseEvent)e, true);
69 } else if (e.getID() == MouseEvent.MOUSE_EXITED || e.getID() == MouseEvent.MOUSE_ENTERED) {
70 dispatched = preprocess((MouseEvent)e, false);
71 } else {
72 return false;
75 if (isVisible() && getComponentCount() == 0) {
76 boolean cursorSet = false;
77 MouseEvent me = (MouseEvent)e;
78 if (me.getComponent() != null) {
79 final Point point = SwingUtilities.convertPoint(me.getComponent(), me.getPoint(), myRootPane.getContentPane());
81 if (myRootPane.getMenuBar() != null && myRootPane.getMenuBar().isVisible()) {
82 point.y += myRootPane.getMenuBar().getHeight();
85 final Component target =
86 SwingUtilities.getDeepestComponentAt(myRootPane.getContentPane().getParent(), point.x, point.y);
87 if (target != null) {
88 setCursor(target.getCursor());
89 cursorSet = true;
93 if (!cursorSet) {
94 setCursor(Cursor.getDefaultCursor());
98 return dispatched;
101 private boolean preprocess(final MouseEvent e, final boolean motion) {
102 final MouseEvent event = convertEvent(e, myRootPane);
103 for (EventListener each : myMouseListeners) {
104 if (motion && each instanceof MouseMotionListener) {
105 fireMouseMotion((MouseMotionListener)each, event);
106 } else if (!motion && each instanceof MouseListener) {
107 fireMouseEvent((MouseListener)each, event);
110 if (event.isConsumed()) {
111 return true;
115 return false;
118 private MouseEvent convertEvent(final MouseEvent e, final Component target) {
119 final Point point = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), target);
120 if (e instanceof MouseWheelEvent) {
121 final MouseWheelEvent mwe = (MouseWheelEvent)e;
122 return new MouseWheelEvent(target, mwe.getID(), mwe.getWhen(), mwe.getModifiersEx(), point.x, point.y, mwe.getClickCount(),
123 mwe.isPopupTrigger(), mwe.getScrollType(), mwe.getScrollAmount(), mwe.getWheelRotation());
125 else if (e instanceof MenuDragMouseEvent) {
126 final MenuDragMouseEvent de = (MenuDragMouseEvent)e;
127 return new MenuDragMouseEvent(target, de.getID(), de.getWhen(), de.getModifiersEx(), point.x, point.y, e.getClickCount(),
128 e.isPopupTrigger(), de.getPath(), de.getMenuSelectionManager());
131 else {
132 return new MouseEvent(target, e.getID(), e.getWhen(), e.getModifiersEx(), point.x, point.y, e.getClickCount(), e.isPopupTrigger(),
133 e.getButton());
137 private static void fireMouseEvent(final MouseListener listener, final MouseEvent event) {
138 switch (event.getID()) {
139 case MouseEvent.MOUSE_PRESSED:
140 listener.mousePressed(event);
141 break;
142 case MouseEvent.MOUSE_RELEASED:
143 listener.mouseReleased(event);
144 break;
145 case MouseEvent.MOUSE_ENTERED:
146 listener.mouseEntered(event);
147 break;
148 case MouseEvent.MOUSE_EXITED:
149 listener.mouseExited(event);
150 break;
151 case MouseEvent.MOUSE_CLICKED:
152 listener.mouseClicked(event);
153 break;
157 private static void fireMouseMotion(MouseMotionListener listener, final MouseEvent event) {
158 switch (event.getID()) {
159 case MouseEvent.MOUSE_DRAGGED:
160 listener.mouseDragged(event);
161 case MouseEvent.MOUSE_MOVED:
162 listener.mouseMoved(event);
166 public void addMousePreprocessor(final MouseListener listener, Disposable parent) {
167 _addListener(listener, parent);
171 public void addMouseMotionPreprocessor(final MouseMotionListener listener, final Disposable parent) {
172 _addListener(listener, parent);
175 private void _addListener(final EventListener listener, final Disposable parent) {
176 myMouseListeners.add(listener);
177 activateIfNeeded();
178 Disposer.register(parent, new Disposable() {
179 public void dispose() {
180 SwingUtilities.invokeLater(new Runnable() {
181 public void run() {
182 removeListener(listener);
189 public void removeMousePreprocessor(final MouseListener listener) {
190 removeListener(listener);
193 public void removeMouseMotionPreprocessor(final MouseMotionListener listener) {
194 removeListener(listener);
197 private void removeListener(final EventListener listener) {
198 myMouseListeners.remove(listener);
199 deactivateIfNeeded();
202 private void deactivateIfNeeded() {
203 if (myPaintingActive) {
204 if (myPainters.size() == 0 && getComponentCount() == 0) {
205 myPaintingActive = false;
209 if (myPreprocessorActive && myMouseListeners.size() == 0) {
210 myPreprocessorActive = false;
213 applyActivationState();
216 private void activateIfNeeded() {
217 if (!myPaintingActive) {
218 if (myPainters.size() > 0 || getComponentCount() > 0) {
219 myPaintingActive = true;
223 if (!myPreprocessorActive && myMouseListeners.size() > 0) {
224 myPreprocessorActive = true;
227 applyActivationState();
230 private void applyActivationState() {
231 if (isVisible() != myPaintingActive) {
232 setVisible(myPaintingActive);
235 IdeEventQueue queue = IdeEventQueue.getInstance();
236 if (!queue.containsDispatcher(this) && myPreprocessorActive) {
237 queue.addDispatcher(this, null);
238 } else if (queue.containsDispatcher(this) && !myPreprocessorActive) {
239 queue.removeDispatcher(this);
244 public void addPainter(final Component component, final Painter painter, final Disposable parent) {
245 myPainters.add(painter);
246 myPainter2Component.put(painter, component == null ? this : component);
247 painter.addListener(this);
248 activateIfNeeded();
249 Disposer.register(parent, new Disposable() {
250 public void dispose() {
251 SwingUtilities.invokeLater(new Runnable() {
252 public void run() {
253 removePainter(painter);
260 public void removePainter(final Painter painter) {
261 myPainters.remove(painter);
262 myPainter2Component.remove(painter);
263 painter.removeListener(this);
264 deactivateIfNeeded();
267 @Override
268 public Component add(final Component comp) {
269 final Component result = super.add(comp);
270 activateIfNeeded();
271 return result;
274 @Override
275 public void remove(final Component comp) {
276 super.remove(comp);
277 deactivateIfNeeded();
280 public boolean isInModalContext() {
281 final Component[] components = getComponents();
282 for (Component component : components) {
283 if (component instanceof GlassPaneDialogWrapperPeer.TransparentLayeredPane) {
284 return true;
288 return false;
291 protected void paintComponent(final Graphics g) {
292 if (myPainters.size() == 0) return;
294 Graphics2D g2d = (Graphics2D)g;
295 for (Painter painter : myPainters) {
296 final Rectangle clip = g.getClipBounds();
298 final Component component = myPainter2Component.get(painter);
299 if (component.getParent() == null) continue;
300 final Rectangle componentBounds = SwingUtilities.convertRectangle(component.getParent(), component.getBounds(), this);
302 if (!painter.needsRepaint()) continue;
304 if (clip.contains(componentBounds) || clip.intersects(componentBounds)) {
305 final Point targetPoint = SwingUtilities.convertPoint(this, 0, 0, component);
306 final Rectangle targetRect = new Rectangle(targetPoint, component.getSize());
307 g2d.translate(-targetRect.x, -targetRect.y);
308 painter.paint(component, g2d);
309 g2d.translate(targetRect.x, targetRect.y);
314 public boolean hasPainters() {
315 return myPainters.size() > 0;
318 public void onNeedsRepaint(final Painter painter, final JComponent dirtyComponent) {
319 if (dirtyComponent != null && dirtyComponent.isShowing()) {
320 final Rectangle rec = SwingUtilities.convertRectangle(dirtyComponent, dirtyComponent.getBounds(), this);
321 if (rec != null) {
322 repaint(rec);
323 return;
327 repaint();
330 public Component getTargetComponentFor(MouseEvent e) {
331 Component candidate = findComponent(e, myRootPane.getLayeredPane());
332 if (candidate != null) return candidate;
333 candidate = findComponent(e, myRootPane.getContentPane());
334 if (candidate != null) return candidate;
335 return e.getComponent();
338 private Component findComponent(final MouseEvent e, final Container container) {
339 final Point lpPoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), container);
340 final Component lpComponent = SwingUtilities.getDeepestComponentAt(container, lpPoint.x, lpPoint.y);
341 return lpComponent;