I have an application here that subverts the EventQueue.dispatchEvent
method by fetching the events directly from EventQueue.getNextEvent()
and then sending it to Component.dispatchEvent(). Still, it expects that
lightweight events get delivered correctly. So I have to call the
LightweightDispatcher from Container.dispatchEventImpl (like it was
before). However, we still don't need one LightweightDispatcher for
each container. I implemented the LightweightDispatcher so that we have
one instance per ThreadGroup (like we do with the RepaintManager). This
makes sense for applets and otherwise separated applications (OSGI?), so
that multiple apps within one VM don't interfere with each other.

Then I changed the global event dispatching so that it is triggered from
Component.dispatchEventImpl for the same reason as stated above.

And I had to add a little tweak to the LightweightDispatcher so that
MOUSE_CLICK events are guaranteed to be sent to the same component that
received the last MOUSE_RELEASED. The problem here is that when the
MOUSE_RELEASED causes some component to disappear (like closing an
internal frame), then the MOUSE_CLICKED is mistakenly sent to the
component under the disappeared component, which can have _really_
strange effects (in my app here it caused the internal frame to re-popup
making it impossible to actually close the frame).

2006-02-28  Roman Kennke  <[EMAIL PROTECTED]>

        * java/awt/Component.java
        (dispatchEventImpl): Let the Toolkit dispatch global events.
        * java/awt/Container.java
        (dispatchEventImpl): Let the LightweightDispatcher handle events
        first.
        * java/awt/EventQueue.java
        (dispatchEvent): Don't do the global event dispatching here. This
        is moved to the Component.
        (globalDispatchEvent): Moved this method to Toolkit.
        * java/awt/LightweightDispatcher.java
        (instances): New field.
        (getInstance): New method. Delivers an instance of
        LightweightDispatcher.
        (LightweightDispatcher): Made default constructor private.
        (dispatchEvent): New method. Replaces the eventDispatched method.
        This now returns true when the event was actually dispatched.
        (eventDispatched): Replaced by dispatchEvent.
        (handleMouseEvent): Send MOUSE_CLICKED to the same component that
        received the last MOUSE_RELEASED.
        * java/awt/Toolkit.java
        (Toolkit): Don't register LightweightDispatcher as global event
        handler.
        (globalDispatchEvent): Moved here from EventQueue.

/Roman
Index: java/awt/Component.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/Component.java,v
retrieving revision 1.103
diff -u -r1.103 Component.java
--- java/awt/Component.java	24 Feb 2006 18:41:54 -0000	1.103
+++ java/awt/Component.java	28 Feb 2006 11:26:30 -0000
@@ -4921,6 +4921,10 @@
 
   void dispatchEventImpl(AWTEvent e)
   {
+    // Give toolkit a chance to dispatch the event
+    // to globally registered listeners.
+    Toolkit.getDefaultToolkit().globalDispatchEvent(e);
+
     // This boolean tells us not to process focus events when the focus
     // opposite component is the same as the focus component.
     boolean ignoreFocus = 
Index: java/awt/Container.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/Container.java,v
retrieving revision 1.82
diff -u -r1.82 Container.java
--- java/awt/Container.java	24 Feb 2006 20:31:37 -0000	1.82
+++ java/awt/Container.java	28 Feb 2006 11:26:31 -0000
@@ -1711,13 +1711,18 @@
 
   void dispatchEventImpl(AWTEvent e)
   {
-    if ((e.id <= ContainerEvent.CONTAINER_LAST
-             && e.id >= ContainerEvent.CONTAINER_FIRST)
-        && (containerListener != null
-            || (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0))
-      processEvent(e);
-    else
-      super.dispatchEventImpl(e);
+    boolean dispatched =
+      LightweightDispatcher.getInstance().dispatchEvent(e);
+    if (! dispatched)
+      {
+        if ((e.id <= ContainerEvent.CONTAINER_LAST
+            && e.id >= ContainerEvent.CONTAINER_FIRST)
+            && (containerListener != null
+                || (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0))
+          processEvent(e);
+        else
+          super.dispatchEventImpl(e);
+      }
   }
 
   /**
Index: java/awt/EventQueue.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/EventQueue.java,v
retrieving revision 1.27
diff -u -r1.27 EventQueue.java
--- java/awt/EventQueue.java	23 Feb 2006 23:43:51 -0000	1.27
+++ java/awt/EventQueue.java	28 Feb 2006 11:26:31 -0000
@@ -38,7 +38,6 @@
 
 package java.awt;
 
-import java.awt.event.AWTEventListenerProxy;
 import java.awt.event.ActionEvent;
 import java.awt.event.InputEvent;
 import java.awt.event.InputMethodEvent;
@@ -460,8 +459,6 @@
     else if (evt instanceof InvocationEvent)
       lastWhen = ((InvocationEvent) evt).getWhen();
 
-    globalDispatchEvent(evt);
-
     if (evt instanceof ActiveEvent)
       {
         ActiveEvent active_evt = (ActiveEvent) evt;
@@ -485,25 +482,6 @@
   }
 
   /**
-   * Dispatches events to listeners registered to the current Toolkit.
-   *
-   * @param ev the event to dispatch
-   */
-  private void globalDispatchEvent(AWTEvent ev)
-  {
-    Toolkit toolkit = Toolkit.getDefaultToolkit();
-    // We do not use the accessor methods here because they create new
-    // arrays each time. We must be very efficient, so we access this directly.
-    AWTEventListenerProxy[] l = toolkit.awtEventListeners;
-    for (int i = 0; i < l.length; ++i)
-      {
-        AWTEventListenerProxy proxy = l[i];
-        if ((proxy.getEventMask() & AWTEvent.eventIdToMask(ev.getID())) != 0)
-          proxy.eventDispatched(ev);
-      }
-  }
-
-  /**
    * Returns the timestamp of the most recent event that had a timestamp, or
    * the initialization time of the event queue if no events have been fired.
    * At present, only <code>InputEvent</code>s, <code>ActionEvent</code>s,
Index: java/awt/LightweightDispatcher.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/LightweightDispatcher.java,v
retrieving revision 1.1
diff -u -r1.1 LightweightDispatcher.java
--- java/awt/LightweightDispatcher.java	24 Feb 2006 15:30:39 -0000	1.1
+++ java/awt/LightweightDispatcher.java	28 Feb 2006 11:26:31 -0000
@@ -40,8 +40,8 @@
 
 import gnu.java.awt.AWTUtilities;
 
-import java.awt.event.AWTEventListener;
 import java.awt.event.MouseEvent;
+import java.util.WeakHashMap;
 
 /**
  * Redispatches mouse events to lightweight components. The native peers know
@@ -52,10 +52,16 @@
  * @author Roman Kennke ([EMAIL PROTECTED])
  */
 class LightweightDispatcher
-  implements AWTEventListener
 {
 
   /**
+   * 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 component that is the start of a mouse dragging. All MOUSE_DRAGGED
    * events that follow the initial press must have the source set to this,
    * as well as the MOUSE_RELEASED event following the dragging.
@@ -69,17 +75,49 @@
   private Component lastTarget;
 
   /**
+   * 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 [EMAIL PROTECTED] #getInstance()} instead.
+   */
+  private LightweightDispatcher()
+  {
+    // Nothing to do here.
+  }
+  
+  /**
    * Receives notification if a mouse event passes along the eventqueue.
    *
    * @param event the event
    */
-  public void eventDispatched(AWTEvent event)
+  public boolean dispatchEvent(AWTEvent event)
   {
+    boolean dispatched = false;
     if (event instanceof MouseEvent && event.getSource() instanceof Window)
       {
         MouseEvent mouseEvent = (MouseEvent) event;
         handleMouseEvent(mouseEvent);
+        dispatched = true;
       }
+    return dispatched;
   }
 
   /**
@@ -116,8 +154,6 @@
                              ev.isPopupTrigger());
             target.dispatchEvent(mouseEntered);
           }
-        lastTarget = target;
-
         
         switch (ev.getID())
         {
@@ -129,6 +165,14 @@
               target = dragTarget;
             dragTarget = null;
             break;
+          case MouseEvent.MOUSE_CLICKED:
+            // When we receive a MOUSE_CLICKED, we set the target to the
+            // previous target, which must have been a MOUSE_RELEASED event.
+            // This is necessary for the case when the MOUSE_RELEASED has
+            // caused the original target (like an internal component) go
+            // away.
+            target = lastTarget;
+            break;
           case MouseEvent.MOUSE_DRAGGED:
             target = dragTarget;
             break;
@@ -137,6 +181,8 @@
             break;
         }
 
+        lastTarget = target;
+
         Point targetCoordinates =
           AWTUtilities.convertPoint(window, ev.getX(), ev.getY(), target);
         int dx = targetCoordinates.x - ev.getX();
Index: java/awt/Toolkit.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/Toolkit.java,v
retrieving revision 1.33
diff -u -r1.33 Toolkit.java
--- java/awt/Toolkit.java	24 Feb 2006 15:30:39 -0000	1.33
+++ java/awt/Toolkit.java	28 Feb 2006 11:26:32 -0000
@@ -128,11 +128,7 @@
    */
   public Toolkit()
   {
-    awtEventListeners = new AWTEventListenerProxy[1];
-    awtEventListeners[0] =
-      new AWTEventListenerProxy(AWTEvent.MOUSE_EVENT_MASK
-                                | AWTEvent.MOUSE_MOTION_EVENT_MASK,
-                                new LightweightDispatcher());
+    awtEventListeners = new AWTEventListenerProxy[0];
   }
 
   /**
@@ -1186,6 +1182,26 @@
     return (AWTEventListener[] ) l.toArray(new AWTEventListener[l.size()]);
   }
 
+
+  /**
+   * Dispatches events to listeners registered to this Toolkit. This is called
+   * by [EMAIL PROTECTED] Component#dispatchEventImpl(AWTEvent)} in order to dispatch
+   * events globally.
+   *
+   * @param ev the event to dispatch
+   */
+  void globalDispatchEvent(AWTEvent ev)
+  {
+    // We do not use the accessor methods here because they create new
+    // arrays each time. We must be very efficient, so we access this directly.
+    for (int i = 0; i < awtEventListeners.length; ++i)
+      {
+        AWTEventListenerProxy proxy = awtEventListeners[i];
+        if ((proxy.getEventMask() & AWTEvent.eventIdToMask(ev.getID())) != 0)
+          proxy.eventDispatched(ev);
+      }
+  }
+
   /**
    * @since 1.3
    */

Reply via email to