mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			362 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			362 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();
 | 
						|
    if (target != null)
 | 
						|
      {
 | 
						|
        // 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;
 | 
						|
  }
 | 
						|
}
 |