source: trunk/src/org/expeditee/gui/MouseEventRouter.java@ 944

Last change on this file since 944 was 939, checked in by bln4, 9 years ago
File size: 12.0 KB
Line 
1/**
2 * MouseEventRouter.java
3 * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package org.expeditee.gui;
20
21import java.awt.AWTEvent;
22import java.awt.Component;
23import java.awt.Container;
24import java.awt.Image;
25import java.awt.Point;
26import java.awt.Toolkit;
27import java.awt.datatransfer.Clipboard;
28import java.awt.datatransfer.DataFlavor;
29import java.awt.datatransfer.Transferable;
30import java.awt.event.AWTEventListener;
31import java.awt.event.MouseEvent;
32import java.awt.event.MouseListener;
33import java.awt.event.MouseMotionListener;
34import java.awt.event.MouseWheelEvent;
35import java.awt.event.MouseWheelListener;
36import java.util.LinkedList;
37import java.util.List;
38
39import javax.swing.JComponent;
40import javax.swing.JMenuBar;
41import javax.swing.JPopupMenu;
42import javax.swing.SwingUtilities;
43
44import org.expeditee.io.ExpClipReader;
45import org.expeditee.io.ItemSelection;
46import org.expeditee.io.ItemSelection.ExpDataHandler;
47import org.expeditee.items.Item;
48import org.expeditee.items.ItemUtils;
49
50/**
51 * The gateway for mouse input; conditionally forwards mouse messages to swing
52 * components / expeditee frames for the Browser.
53 *
54 * Various mouse listeners can attatch themselves here to listen to mouse events
55 * forwared to expeditee .. this excludes widget-exclusive mouse messages.
56 *
57 * @author Brook Novak
58 *
59 */
60public class MouseEventRouter extends JComponent {
61
62 private static final long serialVersionUID = 1L;
63
64 private JMenuBar _menuBar;
65
66 private Container _contentPane;
67
68 private List<MouseListener> _mouseListeners = new LinkedList<MouseListener>();
69
70 private List<MouseMotionListener> _mouseMotionListeners = new LinkedList<MouseMotionListener>();
71
72 private List<MouseWheelListener> _mouseWheelListeners = new LinkedList<MouseWheelListener>();
73
74 private static MouseEvent _currentMouseEvent = null;
75
76 /**
77 * Routes events on given frame layers... the menu bar and content pane
78 *
79 * @param menuBar
80 * Must not be null.
81 *
82 * @param contentPane
83 * Must not be null.
84 */
85 public MouseEventRouter(JMenuBar menuBar, Container contentPane) {
86
87 if (contentPane == null)
88 throw new NullPointerException("contentPane");
89
90 // Listen for all AWT events (ensures popups are included)
91 Toolkit.getDefaultToolkit().addAWTEventListener(
92 new EventCatcher(),
93 AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK
94 | AWTEvent.MOUSE_WHEEL_EVENT_MASK);
95
96 this._menuBar = menuBar;
97 this._contentPane = contentPane;
98 }
99
100 /**
101 * Listens only to events to frames... i.e. to exeditee, not to widgets.
102 *
103 * @param listener
104 * The listener to add.
105 */
106 public void addExpediteeMouseListener(MouseListener listener) {
107 if (listener == null)
108 throw new NullPointerException("listener");
109 _mouseListeners.add(listener);
110 }
111
112 public void removeExpediteeMouseListener(MouseListener listener) {
113 if (listener == null)
114 throw new NullPointerException("listener");
115 _mouseListeners.remove(listener);
116 }
117
118 public void addExpediteeMouseMotionListener(MouseMotionListener listener) {
119 if (listener == null)
120 throw new NullPointerException("listener");
121 _mouseMotionListeners.add(listener);
122 }
123
124 public void removeExpediteeMouseMotionListener(MouseMotionListener listener) {
125 if (listener == null)
126 throw new NullPointerException("listener");
127 _mouseMotionListeners.remove(listener);
128 }
129
130 public void addExpediteeMouseWheelListener(MouseWheelListener listener) {
131 if (listener == null)
132 throw new NullPointerException("listener");
133 _mouseWheelListeners.add(listener);
134 }
135
136 public void removeExpediteeMouseWheelListener(MouseWheelListener listener) {
137 if (listener == null)
138 throw new NullPointerException("listener");
139 _mouseWheelListeners.remove(listener);
140 }
141
142 /**
143 * Conceal event catching from outside
144 *
145 * @author Brook Novak
146 */
147 private class EventCatcher implements AWTEventListener {
148
149 /**
150 * All events for every component in frame are fired to here
151 */
152 public void eventDispatched(AWTEvent event) {
153 if (event instanceof MouseEvent) {
154 routeMouseEvent((MouseEvent) event);
155 }
156 }
157
158 }
159
160 /**
161 * Forwards the mouse event to its appropriate destination. For events that
162 * belong to Expiditee space the events are consumed and manually routed to
163 * the mouse actions handler.
164 *
165 * @param e
166 * The mouse event for any component in the browser frame
167 */
168 private void routeMouseEvent(MouseEvent e) {
169 _currentMouseEvent = e;
170
171 // First convert the point to expeditee space
172 Point containerPoint = SwingUtilities.convertPoint(e.getComponent(), e
173 .getPoint(), _contentPane);
174
175 // TODO: Find a reliable way of detecting when the mouse moved onto a window that isn't a child of ours
176 if(e.getID() == MouseEvent.MOUSE_EXITED) {
177 // System.out.println(e.getComponent());
178 if(containerPoint.x <= 0 || containerPoint.x >= _contentPane.getWidth() ||
179 containerPoint.y <= 0 || containerPoint.y >= _contentPane.getHeight()) {
180 FrameMouseActions.mouseExitedWindow(e);
181 }
182 }
183
184 if (containerPoint.y < 0) { // not in the content pane
185
186 if (_menuBar != null
187 && containerPoint.y + _menuBar.getHeight() >= 0) {
188 // The mouse event is over the menu bar.
189 // Could handle specially.
190 } else {
191 // The mouse event is over non-system window
192 // decorations, such as the ones provided by
193 // the Java look and feel.
194 // Could handle specially.
195 }
196
197 } else {
198
199 // Check to see if the mouse is over an expeditee item or
200 // whether an expeditee item is currently picked up
201 boolean forwardToExpiditee = false;
202 boolean isOverPopup = PopupManager.getInstance().isPointOverPopup(
203 containerPoint);
204 if (isOverPopup) {
205 // Popups have highest preference
206 // forwardToExpiditee = false...
207
208 // If there ate items in free space - keep them moving with the
209 // cursor over the
210 // popups.
211 if (!FreeItems.getInstance().isEmpty()) {
212 FrameMouseActions.move(FreeItems.getInstance());
213 }
214
215 // Note: all frame.content pane events belong to expeditee
216 } else if (e.getSource() == _contentPane
217 || e.getSource() == Browser._theBrowser
218 || !FreeItems.getInstance().isEmpty()) {
219 forwardToExpiditee = true;
220 } else if (DisplayIO.getCurrentFrame() != null) {
221 /* is mouse over a specific expeditee item? */
222 // NOTE: Do not use FrameUtils.getCurrentItem() - thats relevent
223 // for old mouse position only
224 /*
225 * for (Item i : DisplayIO.getCurrentFrame().getItems()) { if
226 * (i.getPolygon().contains(containerPoint)) {
227 * forwardToExpiditee = true; break; } }
228 */// ABOVE: Does not consider overlays
229 // NOTE: Below is an expensive operation and could be re-used
230 // when passing mouse events!!!
231 forwardToExpiditee = (FrameUtils.onItem(DisplayIO
232 .getCurrentFrame(), containerPoint.x, containerPoint.y,
233 true) != null);
234 }
235
236 // Create artificial mouse event and forward it to expeditee
237 MouseEvent expediteeEvent = SwingUtilities.convertMouseEvent(e
238 .getComponent(), e, _contentPane);
239
240 // NOTE: Convert util masks-out the needed extensions
241 MouseEvent withExtensions = null;
242
243 if (forwardToExpiditee) {
244
245 // Ensure that underlying widgets don't get the event
246 e.consume();
247
248 switch (e.getID()) {
249 case MouseEvent.MOUSE_MOVED:
250
251 for (MouseMotionListener listener : _mouseMotionListeners) {
252 listener.mouseMoved(expediteeEvent);
253 }
254
255 // Ensure that expiditee has focus only if no popups exist
256 if (Browser._theBrowser != null
257 && Browser._theBrowser.getContentPane() != null) {
258 if (!Browser._theBrowser.getContentPane()
259 .isFocusOwner()
260 && !isPopupVisible()) {
261 Browser._theBrowser.getContentPane().requestFocus();
262 }
263 }
264
265 break;
266
267 case MouseEvent.MOUSE_CLICKED:
268
269 withExtensions = duplicateMouseEvent(expediteeEvent, e
270 .getModifiers()
271 | e.getModifiersEx());
272
273 for (MouseListener listener : _mouseListeners) {
274 listener.mouseClicked(withExtensions);
275 }
276
277 break;
278
279 case MouseEvent.MOUSE_PRESSED:
280
281 withExtensions = duplicateMouseEvent(expediteeEvent, e
282 .getModifiers()
283 | e.getModifiersEx());
284
285 for (MouseListener listener : _mouseListeners) {
286 listener.mousePressed(withExtensions);
287 }
288
289 break;
290
291 case MouseEvent.MOUSE_RELEASED:
292 withExtensions = duplicateMouseEvent(expediteeEvent, e
293 .getModifiers()
294 | e.getModifiersEx());
295
296 for (MouseListener listener : _mouseListeners) {
297 listener.mouseReleased(withExtensions);
298 }
299
300 break;
301 case MouseEvent.MOUSE_WHEEL:
302 MouseWheelEvent mwe = (MouseWheelEvent) expediteeEvent;
303 for (MouseWheelListener listener : _mouseWheelListeners) {
304 listener.mouseWheelMoved(mwe);
305 }
306 break;
307 case MouseEvent.MOUSE_ENTERED:
308 withExtensions = duplicateMouseEvent(expediteeEvent, e
309 .getModifiers()
310 | e.getModifiersEx());
311 for (MouseListener listener : _mouseListeners) {
312 listener.mouseEntered(withExtensions);
313 }
314 break;
315 case MouseEvent.MOUSE_EXITED:
316 for (MouseListener listener : _mouseListeners) {
317 listener.mouseExited(withExtensions);
318 }
319 break;
320 case MouseEvent.MOUSE_DRAGGED:
321 for (MouseMotionListener listener : _mouseMotionListeners) {
322 listener.mouseDragged(expediteeEvent);
323 }
324 break;
325 }
326
327 } else {
328
329 // Keep expeditees mouse X/Y updated
330 FrameMouseActions.MouseX = expediteeEvent.getX();
331 FrameMouseActions.MouseY = expediteeEvent.getY();
332
333 // If forwarding to swing ensure that widgets are not
334 // highlighted
335 // to give visual feedback yo users such that swing has focus.
336 Item i = FrameMouseActions.getlastHighlightedItem();
337 if (i != null
338 && i.getHighlightMode() != Item.HighlightMode.None) {
339 FrameGraphics.changeHighlightMode(i,
340 Item.HighlightMode.None);
341 }
342
343 // Also bring expideditee behaviour to swing: Auto-focus on
344 // component
345 // whenever the mouse moves over it.
346 if (e.getID() == MouseEvent.MOUSE_MOVED) {
347 if (e.getSource() instanceof Component) {
348 Component target = (Component) e.getSource();
349 if (!target.isFocusOwner()) {
350 target.requestFocus();
351 }
352 }
353 // Auto-hide popups when user click on something other other
354 // than
355 // a popup - and is not a on a popup invoker
356 } else if (!isOverPopup
357 && e.getID() == MouseEvent.MOUSE_PRESSED
358 && !PopupManager.getInstance().isInvoker(
359 e.getComponent())) {
360 PopupManager.getInstance().hideAutohidePopups();
361 }
362 }
363 }
364
365 Help.updateStatus();
366 }
367
368 public static boolean isPopupVisible() {
369 return isPopupVisible(Browser._theBrowser.getLayeredPane());
370 }
371
372 private static boolean isPopupVisible(Container parent) {
373
374 for (Component c : parent.getComponents()) {
375
376 if (c instanceof JPopupMenu && ((JPopupMenu) c).isVisible()) {
377 return true;
378
379 } else if (c instanceof Container
380 && c != Browser._theBrowser.getContentPane()) {
381 if (isPopupVisible((Container) c))
382 return true;
383 }
384 }
385
386 return false;
387 }
388
389 private MouseEvent duplicateMouseEvent(MouseEvent e, int modifiers) {
390 return new MouseEvent(e.getComponent(), e.getID(), e.getWhen(),
391 modifiers, e.getX(), e.getY(),
392 /** The below methods are not compatible with Java 1.5 */
393 /*
394 * e.getXOnScreen(), e.getYOnScreen(),
395 */
396 e.getClickCount(), e.isPopupTrigger(), e.getButton());
397 }
398
399 public static MouseEvent getCurrentMouseEvent() {
400
401 return _currentMouseEvent;
402
403 }
404}
Note: See TracBrowser for help on using the repository browser.