Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/batik/bridge/BridgeEventSupport.java


1   /*
2   
3      Copyright 2001-2004  The Apache Software Foundation 
4   
5      Licensed under the Apache License, Version 2.0 (the "License");
6      you may not use this file except in compliance with the License.
7      You may obtain a copy of the License at
8   
9          http://www.apache.org/licenses/LICENSE-2.0
10  
11     Unless required by applicable law or agreed to in writing, software
12     distributed under the License is distributed on an "AS IS" BASIS,
13     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14     See the License for the specific language governing permissions and
15     limitations under the License.
16  
17   */
18  package org.apache.batik.bridge;
19  
20  import java.awt.Point;
21  import java.awt.event.KeyEvent;
22  import java.awt.geom.NoninvertibleTransformException;
23  import java.awt.geom.Point2D;
24  import java.awt.geom.Rectangle2D;
25  import java.text.AttributedCharacterIterator;
26  import java.util.List;
27  
28  import org.apache.batik.dom.events.DOMKeyEvent;
29  import org.apache.batik.gvt.GraphicsNode;
30  import org.apache.batik.gvt.TextNode;
31  import org.apache.batik.gvt.event.EventDispatcher;
32  import org.apache.batik.gvt.event.GraphicsNodeKeyEvent;
33  import org.apache.batik.gvt.event.GraphicsNodeKeyListener;
34  import org.apache.batik.gvt.event.GraphicsNodeMouseEvent;
35  import org.apache.batik.gvt.event.GraphicsNodeMouseListener;
36  import org.apache.batik.gvt.renderer.StrokingTextPainter;
37  import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
38  import org.apache.batik.gvt.text.TextHit;
39  import org.apache.batik.gvt.text.TextSpanLayout;
40  import org.apache.batik.util.SVGConstants;
41  import org.w3c.dom.Document;
42  import org.w3c.dom.Element;
43  import org.w3c.dom.events.DocumentEvent;
44  import org.w3c.dom.events.Event;
45  import org.w3c.dom.events.EventListener;
46  import org.w3c.dom.events.EventTarget;
47  import org.w3c.dom.events.MouseEvent;
48  
49  /**
50   * This class is responsible of tracking GraphicsNodeMouseEvent and
51   * fowarding them to the DOM as regular DOM MouseEvent.
52   *
53   * @author <a href="mailto:tkormann@ilog.fr">Thierry Kormann</a>
54   * @version $Id: BridgeEventSupport.java,v 1.59 2005/03/27 08:58:30 cam Exp $
55   */
56  public class BridgeEventSupport implements SVGConstants {
57  
58      private BridgeEventSupport() {}
59  
60      /**
61       * Is called only for the root element in order to dispatch GVT
62       * events to the DOM.
63       */
64      public static void addGVTListener(BridgeContext ctx, Document doc) {
65          UserAgent ua = ctx.getUserAgent();
66          if (ua != null) {
67              EventDispatcher dispatcher = ua.getEventDispatcher();
68              if (dispatcher != null) {
69                  final Listener listener = new Listener(ctx, ua);
70                  dispatcher.addGraphicsNodeMouseListener(listener);
71                  dispatcher.addGraphicsNodeKeyListener(listener);
72                  // add an unload listener on the SVGDocument to remove
73                  // that listener for dispatching events
74                  EventListener l = new GVTUnloadListener(dispatcher, listener);
75                  EventTarget target = (EventTarget)doc;
76                  target.addEventListener("SVGUnload", l, false);
77                  ctx.storeEventListener(target, "SVGUnload", l, false);
78              }
79          }
80      }
81  
82      protected static class GVTUnloadListener implements EventListener {
83  
84          protected EventDispatcher dispatcher;
85          protected Listener listener;
86  
87          public GVTUnloadListener(EventDispatcher dispatcher, 
88                                   Listener listener) {
89              this.dispatcher = dispatcher;
90              this.listener = listener;
91          }
92  
93          public void handleEvent(Event evt) {
94              dispatcher.removeGraphicsNodeMouseListener(listener);
95              dispatcher.removeGraphicsNodeKeyListener(listener);
96              evt.getTarget().removeEventListener
97                  (SVGConstants.SVG_SVGUNLOAD_EVENT_TYPE, this, false);
98          }
99      }
100 
101     /**
102      * A GraphicsNodeMouseListener that dispatch DOM events accordingly.
103      */
104     protected static class Listener 
105         implements GraphicsNodeMouseListener, GraphicsNodeKeyListener {
106         
107         protected BridgeContext context;
108         protected UserAgent ua;
109         protected Element lastTargetElement;
110         protected boolean isDown;
111 
112         public Listener(BridgeContext ctx, UserAgent u) {
113             context = ctx;
114             ua = u;
115         }
116 
117         // Key -------------------------------------------------------------
118 
119         /**
120          * Invoked when a key has been pressed.
121          * @param evt the graphics node key event
122          */
123         public void keyPressed(GraphicsNodeKeyEvent evt) {
124             if (!isDown) {
125                 isDown = true;
126                 dispatchKeyEvent("keydown", evt);
127             }
128             if (evt.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
129                 // We will not get a KEY_TYPED event for this char
130                 // so generate a keypress event here.
131                 dispatchKeyEvent("keypress", evt);
132             }
133         }
134 
135         /**
136          * Invoked when a key has been released.
137          * @param evt the graphics node key event
138          */
139         public void keyReleased(GraphicsNodeKeyEvent evt) {
140             dispatchKeyEvent("keyup", evt);
141             isDown = false;
142         }
143 
144         /**
145          * Invoked when a key has been typed.
146          * @param evt the graphics node key event
147          */
148         public void keyTyped(GraphicsNodeKeyEvent evt) {
149             dispatchKeyEvent("keypress", evt);
150         }
151 
152         protected void dispatchKeyEvent(String eventType, 
153                                         GraphicsNodeKeyEvent evt) {
154             FocusManager fmgr = context.getFocusManager();
155             if (fmgr == null) return;
156 
157             Element targetElement = (Element)fmgr.getCurrentEventTarget();
158             if (targetElement == null) {
159                 return;
160             }
161             DocumentEvent d = (DocumentEvent)targetElement.getOwnerDocument();
162             DOMKeyEvent keyEvt = (DOMKeyEvent)d.createEvent("KeyEvents");
163             keyEvt.initKeyEvent(eventType, 
164                                 true, 
165                                 true, 
166                                 evt.isControlDown(), 
167                                 evt.isAltDown(),
168                                 evt.isShiftDown(), 
169                                 evt.isMetaDown(),
170                                 mapKeyCode(evt.getKeyCode()), 
171                                 evt.getKeyChar(),
172                                 null);
173 
174             try {
175                 ((EventTarget)targetElement).dispatchEvent(keyEvt);
176             } catch (RuntimeException e) {
177                 ua.displayError(e);
178             }
179         }
180 
181         /**
182          * The java KeyEvent keyCodes and the DOMKeyEvent keyCodes
183          * map except for the VK_ENTER code (which has a different value
184          * in DOM and the VK_KANA_LOCK and VK_INPUT_METHOD_ON_OFF which
185          * have no DOM equivalent.
186          */
187         protected final int mapKeyCode(int keyCode) {
188             switch (keyCode) {
189                 case KeyEvent.VK_ENTER:
190                     return DOMKeyEvent.DOM_VK_ENTER; 
191             case KeyEvent.VK_KANA_LOCK:
192                 return DOMKeyEvent.DOM_VK_UNDEFINED;
193             case KeyEvent.VK_INPUT_METHOD_ON_OFF:
194                 return DOMKeyEvent.DOM_VK_UNDEFINED;
195             default:
196                 return keyCode;
197             }
198         }
199 
200         // Mouse -----------------------------------------------------------
201 
202         public void mouseClicked(GraphicsNodeMouseEvent evt) {
203             dispatchMouseEvent("click", evt, true);
204         }
205 
206         public void mousePressed(GraphicsNodeMouseEvent evt) {
207             dispatchMouseEvent("mousedown", evt, true);
208         }
209 
210         public void mouseReleased(GraphicsNodeMouseEvent evt) {
211             dispatchMouseEvent("mouseup", evt, true);
212         }
213 
214         public void mouseEntered(GraphicsNodeMouseEvent evt) {
215             dispatchMouseEvent("mouseover", evt, true);
216         }
217 
218         public void mouseExited(GraphicsNodeMouseEvent evt) {
219             Point clientXY = evt.getClientPoint();
220             // Get the 'new' node for the DOM event.
221             GraphicsNode node = evt.getRelatedNode();
222             Element targetElement = getEventTarget(node, clientXY);
223             if (lastTargetElement != null) {
224                 dispatchMouseEvent("mouseout", 
225                                    lastTargetElement, // target
226                                    targetElement,     // relatedTarget
227                                    clientXY,
228                                    evt,
229                                    true);
230             }
231         }
232 
233         public void mouseDragged(GraphicsNodeMouseEvent evt) {
234             dispatchMouseEvent("mousemove", evt, false);
235         }
236 
237         public void mouseMoved(GraphicsNodeMouseEvent evt) {
238             Point clientXY = evt.getClientPoint();
239             GraphicsNode node = evt.getGraphicsNode();
240             Element targetElement = getEventTarget(node, clientXY);
241             Element holdLTE = lastTargetElement;
242             if (holdLTE != targetElement) {
243                 if (holdLTE != null) {
244                     dispatchMouseEvent("mouseout", 
245                                        holdLTE, // target
246                                        targetElement,     // relatedTarget
247                                        clientXY,
248                                        evt,
249                                        true);
250                 }
251                 if (targetElement != null) {
252                     dispatchMouseEvent("mouseover", 
253                                        targetElement,     // target
254                                        holdLTE, // relatedTarget
255                                        clientXY,
256                                        evt,
257                                        true);
258                 }
259             }
260             dispatchMouseEvent("mousemove", 
261                                targetElement,     // target
262                                null,              // relatedTarget
263                                clientXY,
264                                evt,
265                                false);
266         }
267 
268         /**
269          * Dispatches a DOM MouseEvent according to the specified
270          * parameters.
271          *
272          * @param eventType the event type
273          * @param evt the GVT GraphicsNodeMouseEvent
274          * @param cancelable true means the event is cancelable
275          */
276         protected void dispatchMouseEvent(String eventType,
277                                           GraphicsNodeMouseEvent evt,
278                                           boolean cancelable) {
279             Point clientXY = evt.getClientPoint();
280             GraphicsNode node = evt.getGraphicsNode();
281             Element targetElement = getEventTarget
282                 (node, new Point2D.Float(evt.getX(), evt.getY()));
283             Element relatedElement = getRelatedElement(evt);
284             dispatchMouseEvent(eventType, 
285                                targetElement,
286                                relatedElement,
287                                clientXY, 
288                                evt, 
289                                cancelable);
290         }
291 
292         /**
293          * Dispatches a DOM MouseEvent according to the specified
294          * parameters.
295          *
296          * @param eventType the event type
297          * @param targetElement the target of the event
298          * @param relatedElement the related target if any
299          * @param clientXY the mouse coordinates in the client space
300          * @param evt the GVT GraphicsNodeMouseEvent
301          * @param cancelable true means the event is cancelable
302          */
303         protected void dispatchMouseEvent(String eventType,
304                                           Element targetElement,
305                                           Element relatedElement,
306                                           Point clientXY,
307                                           GraphicsNodeMouseEvent evt,
308                                           boolean cancelable) {
309             if (targetElement == null) {
310                 return;
311             }
312             /*
313             if (relatedElement != null) {
314                 System.out.println
315                     ("dispatching "+eventType+
316                      " target:"+targetElement.getLocalName()+
317                      " relatedElement:"+relatedElement.getLocalName());
318             } else {
319                 System.out.println
320                     ("dispatching "+eventType+
321                      " target:"+targetElement.getLocalName());
322 
323             }
324             */
325             short button = getButton(evt);
326             Point screenXY = evt.getScreenPoint();
327             // create the coresponding DOM MouseEvent
328             DocumentEvent d = (DocumentEvent)targetElement.getOwnerDocument();
329             MouseEvent mouseEvt = (MouseEvent)d.createEvent("MouseEvents");
330             mouseEvt.initMouseEvent(eventType, 
331                                     true, 
332                                     cancelable, 
333                                     null,
334                                     evt.getClickCount(),
335                                     screenXY.x, 
336                                     screenXY.y,
337                                     clientXY.x,
338                                     clientXY.y,
339                                     evt.isControlDown(), 
340                                     evt.isAltDown(),
341                                     evt.isShiftDown(), 
342                                     evt.isMetaDown(),
343                                     button, 
344                                     (EventTarget)relatedElement);
345 
346             try {
347                 ((EventTarget)targetElement).dispatchEvent(mouseEvt);
348             } catch (RuntimeException e) {
349                 ua.displayError(e);
350             } finally {
351                 lastTargetElement = targetElement;
352             }
353         }
354 
355         /**
356          * Returns the related element according to the specified event.
357          *
358          * @param evt the GVT GraphicsNodeMouseEvent
359          */
360         protected Element getRelatedElement(GraphicsNodeMouseEvent evt) {
361             GraphicsNode relatedNode = evt.getRelatedNode();
362             Element relatedElement = null;
363             if (relatedNode != null) {
364                 relatedElement = context.getElement(relatedNode);
365             }
366             return relatedElement;
367         }
368 
369         /**
370          * Returns the mouse event button.
371          *
372          * @param evt the GVT GraphicsNodeMouseEvent
373          */
374         protected short getButton(GraphicsNodeMouseEvent evt) {
375             short button = 1;
376             if ((GraphicsNodeMouseEvent.BUTTON1_MASK & evt.getModifiers()) != 0) {
377                 button = 0;
378             } else if ((GraphicsNodeMouseEvent.BUTTON3_MASK & evt.getModifiers()) != 0) {
379                 button = 2;
380             }
381             return button;
382         }
383 
384         /**
385          * Returns the element that is the target of the specified
386          * event or null if any.
387          *
388          * @param node the graphics node that received the event
389          * @param coords the mouse coordinates in the GVT tree space
390          */
391         protected Element getEventTarget(GraphicsNode node, Point2D coords) {
392             Element target = context.getElement(node);
393             // Lookup inside the text element children to see if the target
394             // is a tspan or textPath
395 
396             if (target != null && node instanceof TextNode) {
397     TextNode textNode = (TextNode)node;
398     List list = textNode.getTextRuns();
399                 Point2D pt = (Point2D)coords.clone();
400                 // place coords in text node coordinate system
401                 try {
402                     node.getGlobalTransform().createInverse().transform(pt, pt);
403                 } catch (NoninvertibleTransformException ex) {
404                 }
405                 if (list != null){
406                     for (int i = 0 ; i < list.size(); i++) {
407                         StrokingTextPainter.TextRun run =
408                             (StrokingTextPainter.TextRun)list.get(i);
409                         AttributedCharacterIterator aci = run.getACI();
410                         TextSpanLayout layout = run.getLayout();
411                         float x = (float)pt.getX();
412                         float y = (float)pt.getY();
413                         TextHit textHit = layout.hitTestChar(x, y);
414                         Rectangle2D bounds = layout.getBounds2D();
415                         if ((textHit != null) && 
416                             (bounds != null) && bounds.contains(x, y)) {
417                             Object delimiter = aci.getAttribute
418                                 (GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER);
419                             if (delimiter instanceof Element) {
420                                 return (Element)delimiter;
421                             }
422                         }
423                     }
424                 }
425             }
426             return target;
427         }
428     }
429 }