source: trunk/src/org/expeditee/gio/swing/MouseEventRouter.java@ 1097

Last change on this file since 1097 was 1097, checked in by davidb, 6 years ago

Newly structured files from Corey's work on logic/graphics separation

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