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

Last change on this file since 919 was 919, checked in by jts21, 10 years ago

Added license headers to all files, added full GPL3 license file, moved license header generator script to dev/bin/scripts

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
369 public static boolean isPopupVisible() {
370 return isPopupVisible(Browser._theBrowser.getLayeredPane());
371 }
372
373 private static boolean isPopupVisible(Container parent) {
374
375 for (Component c : parent.getComponents()) {
376
377 if (c instanceof JPopupMenu && ((JPopupMenu) c).isVisible()) {
378 return true;
379
380 } else if (c instanceof Container
381 && c != Browser._theBrowser.getContentPane()) {
382 if (isPopupVisible((Container) c))
383 return true;
384 }
385 }
386
387 return false;
388 }
389
390 private MouseEvent duplicateMouseEvent(MouseEvent e, int modifiers) {
391 return new MouseEvent(e.getComponent(), e.getID(), e.getWhen(),
392 modifiers, e.getX(), e.getY(),
393 /** The below methods are not compatible with Java 1.5 */
394 /*
395 * e.getXOnScreen(), e.getYOnScreen(),
396 */
397 e.getClickCount(), e.isPopupTrigger(), e.getButton());
398 }
399
400 public static MouseEvent getCurrentMouseEvent() {
401
402 return _currentMouseEvent;
403
404 }
405}
Note: See TracBrowser for help on using the repository browser.