mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			363 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			363 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* LightweightDispatcher.java -- Dispatches mouse events to lightweights
 | |
|    Copyright (C) 2006 Free Software Foundation, Inc.
 | |
| 
 | |
| This file is part of GNU Classpath.
 | |
| 
 | |
| GNU Classpath is free software; you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation; either version 2, or (at your option)
 | |
| any later version.
 | |
| 
 | |
| GNU Classpath is distributed in the hope that it will be useful, but
 | |
| WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License
 | |
| along with GNU Classpath; see the file COPYING.  If not, write to the
 | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | |
| 02110-1301 USA.
 | |
| 
 | |
| Linking this library statically or dynamically with other modules is
 | |
| making a combined work based on this library.  Thus, the terms and
 | |
| conditions of the GNU General Public License cover the whole
 | |
| combination.
 | |
| 
 | |
| As a special exception, the copyright holders of this library give you
 | |
| permission to link this library with independent modules to produce an
 | |
| executable, regardless of the license terms of these independent
 | |
| modules, and to copy and distribute the resulting executable under
 | |
| terms of your choice, provided that you also meet, for each linked
 | |
| independent module, the terms and conditions of the license of that
 | |
| module.  An independent module is a module which is not derived from
 | |
| or based on this library.  If you modify this library, you may extend
 | |
| this exception to your version of the library, but you are not
 | |
| obligated to do so.  If you do not wish to do so, delete this
 | |
| exception statement from your version. */
 | |
| 
 | |
| 
 | |
| package java.awt;
 | |
| 
 | |
| import java.awt.event.InputEvent;
 | |
| import java.awt.event.MouseEvent;
 | |
| import java.awt.event.MouseWheelEvent;
 | |
| import java.awt.peer.LightweightPeer;
 | |
| import java.util.WeakHashMap;
 | |
| 
 | |
| /**
 | |
|  * Redispatches mouse events to lightweight components. The native peers know
 | |
|  * nothing about the lightweight components and thus mouse events are always
 | |
|  * targetted at Windows or heavyweight components. This class listenes directly
 | |
|  * on the eventqueue and dispatches mouse events to lightweight components.
 | |
|  *
 | |
|  * @author Roman Kennke (kennke@aicas.com)
 | |
|  */
 | |
| final class LightweightDispatcher
 | |
| {
 | |
| 
 | |
|   /**
 | |
|    * Maps thread groups to lightweight dispatcher instances. We need to
 | |
|    * have one instance per thread group so that 2 or more applets or otherwise
 | |
|    * separated applications (like in OSGI) do not interfer with each other.
 | |
|    */
 | |
|   private static WeakHashMap instances = new WeakHashMap();
 | |
| 
 | |
|   /**
 | |
|    * The last mouse event target. If the target changes, additional
 | |
|    * MOUSE_ENTERED and MOUSE_EXITED events must be dispatched.
 | |
|    */
 | |
|   private Component lastTarget;
 | |
| 
 | |
|   /**
 | |
|    * The current mouseEventTarget.
 | |
|    */
 | |
|   private Component mouseEventTarget;
 | |
| 
 | |
|   /**
 | |
|    * Returns an instance of LightweightDispatcher for the current thread's
 | |
|    * thread group.
 | |
|    *
 | |
|    * @return an instance of LightweightDispatcher for the current thread's
 | |
|    *         thread group
 | |
|    */
 | |
|   static LightweightDispatcher getInstance()
 | |
|   {
 | |
|     Thread t = Thread.currentThread();
 | |
|     ThreadGroup tg = t.getThreadGroup();
 | |
|     LightweightDispatcher instance = (LightweightDispatcher) instances.get(tg);
 | |
|     if (instance == null)
 | |
|       {
 | |
|         instance = new LightweightDispatcher();
 | |
|         instances.put(tg, instance);
 | |
|       }
 | |
|     return instance;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Creates a new LightweightDispatcher. This is private to prevent access
 | |
|    * from outside. Use {@link #getInstance()} instead.
 | |
|    */
 | |
|   private LightweightDispatcher()
 | |
|   {
 | |
|     // Nothing to do here.
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Receives notification if a mouse event passes along the eventqueue.
 | |
|    *
 | |
|    * @param event the event
 | |
|    */
 | |
|   public boolean dispatchEvent(final AWTEvent event)
 | |
|   {
 | |
|     if (event instanceof MouseEvent)
 | |
|       {
 | |
|         MouseEvent mouseEvent = (MouseEvent) event;
 | |
|         return handleMouseEvent(mouseEvent);
 | |
|       }
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Handles all mouse events that are targetted at toplevel containers
 | |
|    * (Window instances) and dispatches them to the correct lightweight child.
 | |
|    *
 | |
|    * @param ev the mouse event
 | |
|    * @return whether or not we found a lightweight that handled the event.
 | |
|    */
 | |
|   private boolean handleMouseEvent(final MouseEvent ev)
 | |
|   {
 | |
|     Container container = (Container) ev.getSource();
 | |
|     Component target = findTarget(container, ev.getX(), ev.getY());
 | |
|     trackEnterExit(target, ev);
 | |
|     int id = ev.getID();
 | |
| 
 | |
|     // Dont update the mouseEventTarget when dragging. Also, MOUSE_CLICKED
 | |
|     // must be dispatched to the original target of MOUSE_PRESSED, so don't
 | |
|     // update in this case either.
 | |
|     if (! isDragging(ev) && id != MouseEvent.MOUSE_CLICKED)
 | |
|       mouseEventTarget = (target != container) ? target : null;
 | |
| 
 | |
|     if (mouseEventTarget != null)
 | |
|       {
 | |
|         switch (id)
 | |
|           {
 | |
|           case MouseEvent.MOUSE_ENTERED:
 | |
|           case MouseEvent.MOUSE_EXITED:
 | |
|             // This is already handled in trackEnterExit().
 | |
|             break;
 | |
|           case MouseEvent.MOUSE_PRESSED:
 | |
|           case MouseEvent.MOUSE_RELEASED:
 | |
|           case MouseEvent.MOUSE_MOVED:
 | |
|             redispatch(ev, mouseEventTarget, id);
 | |
|             break;
 | |
|           case MouseEvent.MOUSE_CLICKED:
 | |
|             // MOUSE_CLICKED must be dispatched to the original target of
 | |
|             // MOUSE_PRESSED.
 | |
|             if (target == mouseEventTarget)
 | |
|               redispatch(ev, mouseEventTarget, id);
 | |
|             break;
 | |
|           case MouseEvent.MOUSE_DRAGGED:
 | |
|             if (isDragging(ev))
 | |
|               redispatch(ev, mouseEventTarget, id);
 | |
|             break;
 | |
|           case MouseEvent.MOUSE_WHEEL:
 | |
|             redispatch(ev, mouseEventTarget, id);
 | |
|           }
 | |
|         ev.consume();
 | |
|       }
 | |
| 
 | |
|     return ev.isConsumed();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Finds the actual target for a mouseevent, starting at <code>c</code>.
 | |
|    * This searches through the children of the container and finds the first
 | |
|    * one which is showing, at the location from the mouse event and has
 | |
|    * a MouseListener or MouseMotionListener attached. If no such child component
 | |
|    * is found, null is returned.
 | |
|    *
 | |
|    * @param c the container to search through
 | |
|    * @param loc the mouse event point
 | |
|    *
 | |
|    * @return the actual receiver of the mouse event, or null, if no such
 | |
|    *         component has been found
 | |
|    */
 | |
|   private Component findTarget(final Container c, final int x, final int y)
 | |
|   {
 | |
|     Component target = null;
 | |
| 
 | |
|     // First we check the children of the container.
 | |
| 
 | |
|     // Note: It is important that we use the package private Container
 | |
|     // fields ncomponents and component here. There are applications
 | |
|     // that override getComponentCount()
 | |
|     // and getComponent() to hide internal components, which makes
 | |
|     // the LightweightDispatcher not work correctly in these cases.
 | |
|     // As a positive sideeffect this is slightly more efficient.
 | |
|     int nChildren = c.ncomponents;
 | |
|     for (int i = 0; i < nChildren && target == null; i++)
 | |
|       {
 | |
|         Component child = c.component[i];
 | |
|         int childX = x - child.x;
 | |
|         int childY = y - child.y;
 | |
|         if (child != null && child.visible
 | |
|             && child.peer instanceof LightweightPeer
 | |
|             && child.contains(childX, childY))
 | |
|           {
 | |
|             // Check if there's a deeper possible target.
 | |
|             if (child instanceof Container)
 | |
|               {
 | |
|                 Component deeper = findTarget((Container) child,
 | |
|                                               childX, childY);
 | |
|                 if (deeper != null)
 | |
|                   target = deeper;
 | |
|               }
 | |
|             // Check if the child itself is interested in mouse events.
 | |
|             else if (isMouseListening(child))
 | |
|               target = child;
 | |
|           }
 | |
|       }
 | |
| 
 | |
|     // Check the container itself, if we didn't find a target yet.
 | |
|     if (target == null  && c.contains(x, y) && isMouseListening(c))
 | |
|       target = c;
 | |
| 
 | |
|     return target;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Checks if the specified component would be interested in a mouse event.
 | |
|    *
 | |
|    * @param c the component to check
 | |
|    *
 | |
|    * @return <code>true</code> if the component has mouse listeners installed,
 | |
|    *         <code>false</code> otherwise
 | |
|    */
 | |
|   private boolean isMouseListening(final Component c)
 | |
|   {
 | |
|     // Note: It is important to NOT check if the component is listening
 | |
|     // for a specific event (for instance, mouse motion events). The event
 | |
|     // gets dispatched to the component if the component is listening
 | |
|     // for ANY mouse event, even when the component is not listening for the
 | |
|     // specific type of event. There are applications that depend on this
 | |
|     // (sadly).
 | |
|     return c.mouseListener != null
 | |
|            || c.mouseMotionListener != null
 | |
|            || c.mouseWheelListener != null
 | |
|            || (c.eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0
 | |
|            || (c.eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0
 | |
|            || (c.eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Tracks MOUSE_ENTERED and MOUSE_EXIT as well as MOUSE_MOVED and
 | |
|    * MOUSE_DRAGGED and creates synthetic MOUSE_ENTERED and MOUSE_EXITED for
 | |
|    * lightweight component.s
 | |
|    *
 | |
|    * @param target the current mouse event target
 | |
|    * @param ev the mouse event
 | |
|    */
 | |
|   private void trackEnterExit(final Component target, final MouseEvent ev)
 | |
|   {
 | |
|     int id = ev.getID();
 | |
|     if (target != lastTarget)
 | |
|       {
 | |
|         if (lastTarget != null)
 | |
|           redispatch(ev, lastTarget, MouseEvent.MOUSE_EXITED);
 | |
|         if (id == MouseEvent.MOUSE_EXITED)
 | |
|           ev.consume();
 | |
|         if (target != null)
 | |
|           redispatch(ev, target, MouseEvent.MOUSE_ENTERED);
 | |
|         if (id == MouseEvent.MOUSE_ENTERED)
 | |
|           ev.consume();
 | |
|         lastTarget = target;
 | |
|       }
 | |
| 
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Redispatches the specified mouse event to the specified target with the
 | |
|    * specified id.
 | |
|    *
 | |
|    * @param ev the mouse event
 | |
|    * @param target the new target
 | |
|    * @param id the new id
 | |
|    */
 | |
|   private void redispatch(MouseEvent ev, Component target, int id)
 | |
|   {
 | |
|     Component source = ev.getComponent();
 | |
|     assert target != null;
 | |
|     if (target.isShowing())
 | |
|       {
 | |
|         // Translate coordinates.
 | |
|         int x = ev.getX();
 | |
|         int y = ev.getY();
 | |
|         for (Component c = target; c != null && c != source; c = c.getParent())
 | |
|           {
 | |
|             x -= c.x;
 | |
|             y -= c.y;
 | |
|           }
 | |
| 
 | |
|         // Retarget event.
 | |
|         MouseEvent retargeted;
 | |
|         if (id == MouseEvent.MOUSE_WHEEL)
 | |
|           {
 | |
|             MouseWheelEvent mwe = (MouseWheelEvent) ev;
 | |
|             retargeted = new MouseWheelEvent(target, id, ev.getWhen(),
 | |
|                                              ev.getModifiers()
 | |
|                                              | ev.getModifiersEx(), x, y,
 | |
|                                              ev.getClickCount(),
 | |
|                                              ev.isPopupTrigger(),
 | |
|                                              mwe.getScrollType(),
 | |
|                                              mwe.getScrollAmount(),
 | |
|                                              mwe.getWheelRotation());
 | |
|           }
 | |
|         else
 | |
|           {
 | |
|             retargeted = new MouseEvent(target, id, ev.getWhen(),
 | |
|                                        ev.getModifiers() | ev.getModifiersEx(),
 | |
|                                        x, y, ev.getClickCount(),
 | |
|                                        ev.isPopupTrigger(), ev.getButton());
 | |
|           }
 | |
| 
 | |
|         if (target == source)
 | |
|           ((Container) target).dispatchNoLightweight(retargeted);
 | |
|         else
 | |
|           target.dispatchEvent(retargeted);
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Determines if we are in the middle of a drag operation, that is, if
 | |
|    * any of the buttons is held down.
 | |
|    *
 | |
|    * @param ev the mouse event to check
 | |
|    *
 | |
|    * @return <code>true</code> if we are in the middle of a drag operation,
 | |
|    *         <code>false</code> otherwise
 | |
|    */
 | |
|   private boolean isDragging(MouseEvent ev)
 | |
|   {
 | |
|     int mods = ev.getModifiersEx();
 | |
|     int id = ev.getID();
 | |
|     if (id == MouseEvent.MOUSE_PRESSED || id == MouseEvent.MOUSE_RELEASED)
 | |
|       {
 | |
|         switch (ev.getButton())
 | |
|           {
 | |
|             case MouseEvent.BUTTON1:
 | |
|               mods ^= InputEvent.BUTTON1_DOWN_MASK;
 | |
|               break;
 | |
|             case MouseEvent.BUTTON2:
 | |
|               mods ^= InputEvent.BUTTON2_DOWN_MASK;
 | |
|               break;
 | |
|             case MouseEvent.BUTTON3:
 | |
|               mods ^= InputEvent.BUTTON3_DOWN_MASK;
 | |
|               break;
 | |
|           }
 | |
|       }
 | |
|     return (mods & (InputEvent.BUTTON1_DOWN_MASK
 | |
|                     | InputEvent.BUTTON2_DOWN_MASK
 | |
|                     | InputEvent.BUTTON3_DOWN_MASK)) != 0;
 | |
|   }
 | |
| }
 |