KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > swing > svg > JSVGComponent


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.swing.svg;
19
20 import java.awt.Cursor JavaDoc;
21 import java.awt.Dimension JavaDoc;
22 import java.awt.EventQueue JavaDoc;
23 import java.awt.Point JavaDoc;
24 import java.awt.Rectangle JavaDoc;
25 import java.awt.Shape JavaDoc;
26 import java.awt.event.ComponentAdapter JavaDoc;
27 import java.awt.event.ComponentEvent JavaDoc;
28 import java.awt.event.KeyEvent JavaDoc;
29 import java.awt.event.MouseEvent JavaDoc;
30 import java.awt.geom.AffineTransform JavaDoc;
31 import java.awt.geom.Dimension2D JavaDoc;
32 import java.awt.geom.NoninvertibleTransformException JavaDoc;
33 import java.awt.geom.Point2D JavaDoc;
34 import java.util.ArrayList JavaDoc;
35 import java.util.HashMap JavaDoc;
36 import java.util.HashSet JavaDoc;
37 import java.util.Iterator JavaDoc;
38 import java.util.LinkedList JavaDoc;
39 import java.util.List JavaDoc;
40 import java.util.Map JavaDoc;
41 import java.util.Set JavaDoc;
42
43 import javax.swing.JOptionPane JavaDoc;
44
45 import org.apache.batik.bridge.BridgeContext;
46 import org.apache.batik.bridge.BridgeException;
47 import org.apache.batik.bridge.BridgeExtension;
48 import org.apache.batik.bridge.DefaultScriptSecurity;
49 import org.apache.batik.bridge.DocumentLoader;
50 import org.apache.batik.bridge.ExternalResourceSecurity;
51 import org.apache.batik.bridge.RelaxedExternalResourceSecurity;
52 import org.apache.batik.bridge.ScriptSecurity;
53 import org.apache.batik.bridge.UpdateManager;
54 import org.apache.batik.bridge.UpdateManagerAdapter;
55 import org.apache.batik.bridge.UpdateManagerEvent;
56 import org.apache.batik.bridge.UpdateManagerListener;
57 import org.apache.batik.bridge.UserAgent;
58 import org.apache.batik.bridge.ViewBox;
59 import org.apache.batik.dom.svg.SVGDOMImplementation;
60 import org.apache.batik.dom.util.DOMUtilities;
61 import org.apache.batik.dom.util.XLinkSupport;
62 import org.apache.batik.ext.awt.image.spi.ImageTagRegistry;
63 import org.apache.batik.gvt.CanvasGraphicsNode;
64 import org.apache.batik.gvt.CompositeGraphicsNode;
65 import org.apache.batik.gvt.GraphicsNode;
66 import org.apache.batik.gvt.event.EventDispatcher;
67 import org.apache.batik.gvt.text.Mark;
68 import org.apache.batik.gvt.renderer.ImageRenderer;
69 import org.apache.batik.swing.gvt.GVTTreeRenderer;
70 import org.apache.batik.swing.gvt.GVTTreeRendererEvent;
71 import org.apache.batik.swing.gvt.GVTTreeRendererAdapter;
72 import org.apache.batik.swing.gvt.JGVTComponent;
73 import org.apache.batik.swing.gvt.JGVTComponentListener;
74 import org.apache.batik.util.ParsedURL;
75 import org.apache.batik.util.RunnableQueue;
76 import org.apache.batik.util.SVGConstants;
77 import org.apache.batik.util.XMLResourceDescriptor;
78 import org.w3c.dom.Document JavaDoc;
79 import org.w3c.dom.DOMImplementation JavaDoc;
80 import org.w3c.dom.Element JavaDoc;
81 import org.w3c.dom.svg.SVGAElement;
82 import org.w3c.dom.svg.SVGDocument;
83 import org.w3c.dom.svg.SVGSVGElement;
84
85 /**
86  * This class represents a swing component that can display SVG documents. This
87  * component also lets you translate, zoom and rotate the document being
88  * displayed. This is the fundamental class for rendering SVG documents in a
89  * swing application.
90  *
91  * <h2>Rendering Process</h2>
92  *
93  * <p>The rendering process can be broken down into five phases. Not all of
94  * those steps are required - depending on the method used to specify the SVG
95  * document to display, but basically the steps in the rendering process
96  * are:</p>
97  *
98  * <ol>
99  *
100  * <li><b>Building a DOM tree</b>
101  *
102  * <blockquote>If the <tt>{@link #loadSVGDocument(String)}</tt> method is used,
103  * the SVG file is parsed and an SVG DOM Tree is built.</blockquote></li>
104  *
105  * <li><b>Building a GVT tree</b>
106  *
107  * <blockquote>Once an SVGDocument is created (using the step 1 or if the
108  * <tt>{@link #setSVGDocument(SVGDocument)}</tt> method has been used) - a GVT
109  * tree is constructed. The GVT tree is the data structure used internally to
110  * render an SVG document. see the <tt>{@link org.apache.batik.gvt}
111  * package.</tt></blockquote></li>
112  *
113  * <li><b>Executing the SVGLoad event handlers</b>
114  *
115  * <blockquote>
116  * If the document is dynamic, the scripts are initialized and the
117  * SVGLoad event is dispatched before the initial rendering.
118  * </blockquote></li>
119  *
120  * <li><b>Rendering the GVT tree</b>
121  *
122  * <blockquote>Then the GVT tree is rendered. see the <tt>{@link
123  * org.apache.batik.gvt.renderer}</tt> package.</blockquote></li>
124  *
125  * <li><b>Running the document</b>
126  *
127  * <blockquote>
128  * If the document is dynamic, the update threads are started.
129  * </blockquote></li>
130  *
131  * </ol>
132  *
133  * <p>Those steps are performed in a separate thread. To be notified to what
134  * happens and eventually perform some operations - such as resizing the window
135  * to the size of the document or get the SVGDocument built via a URI, five
136  * different listeners are provided (one per step):
137  * <tt>{@link SVGDocumentLoaderListener}</tt>,
138  * <tt>{@link GVTTreeBuilderListener}</tt>,
139  * <tt>{@link SVGLoadEventDispatcherListener}</tt>,
140  * <tt>{@link org.apache.batik.swing.gvt.GVTTreeRendererListener}</tt>,
141  * <tt>{@link org.apache.batik.bridge.UpdateManagerListener}</tt>.</p>
142  *
143  * <p>Each listener has methods to be notified of the start of a phase,
144  * and methods to be notified of the end of a phase.
145  * A phase cannot start before the preceding has finished.</p>
146  *
147  * <p>The following example shows how you can get the size of an SVG
148  * document. Note that due to how SVG is designed (units, percentages...), the
149  * size of an SVG document can be known only once the SVGDocument has been
150  * analyzed (ie. the GVT tree has been constructed).</p>
151  *
152  * <pre>
153  * final JSVGComponent svgComp = new JSVGComponent();
154  * svgComp.loadSVGDocument("foo.svg");
155  * svgComp.addGVTTreeBuilderListener(new GVTTreeBuilderAdapter() {
156  * public void gvtBuildCompleted(GVTTreeBuilderEvent evt) {
157  * Dimension2D size = svgComp.getSVGDocumentSize();
158  * // ...
159  * }
160  * });
161  * </pre>
162  *
163  * <p>The second example shows how you can access to the DOM tree when a URI has
164  * been used to display an SVG document.
165  *
166  * <pre>
167  * final JSVGComponent svgComp = new JSVGComponent();
168  * svgComp.loadSVGDocument("foo.svg");
169  * svgComp.addSVGDocumentLoaderListener(new SVGDocumentLoaderAdapter() {
170  * public void documentLoadingCompleted(SVGDocumentLoaderEvent evt) {
171  * SVGDocument svgDoc = svgComp.getSVGDocument();
172  * //...
173  * }
174  * });
175  * </pre>
176  *
177  * <p>Conformed to the <a HREF=
178  * "http://java.sun.com/docs/books/tutorial/uiswing/overview/threads.html">
179  * single thread rule of swing</a>, the listeners are executed in the swing
180  * thread. The sequence of the method calls for a particular listener and
181  * the order of the listeners themselves are <em>guaranteed</em>.</p>
182  *
183  * <h2>User Agent</h2>
184  *
185  * <p>The <tt>JSVGComponent</tt> can pick up some informations to a user
186  * agent. The <tt>{@link SVGUserAgent}</tt> provides a way to control the
187  * resolution used to display an SVG document (controling the pixel to
188  * millimeter conversion factor), perform an operation in respond to a click on
189  * an hyperlink, control the default language to use, or specify a user
190  * stylesheet, or how to display errors when an error occured while
191  * building/rendering a document (invalid XML file, missing attributes...).</p>
192  *
193  * @author <a HREF="mailto:stephane@hillion.org">Stephane Hillion</a>
194  * @version $Id: JSVGComponent.java,v 1.104 2005/03/29 10:48:02 deweese Exp $
195  */

196 public class JSVGComponent extends JGVTComponent {
197
198     /**
199      * Means that the component must auto detect whether
200      * the current document is static or dynamic.
201      */

202     public final static int AUTODETECT = 0;
203
204     /**
205      * Means that all document must be considered as dynamic.
206      *
207      * Indicates that all DOM listeners should be registered. This supports
208      * 'interactivity' (anchors and cursors), as well as DOM modifications
209      * listeners to update the GVT rendering tree.
210      */

211     public final static int ALWAYS_DYNAMIC = 1;
212
213     /**
214      * Means that all document must be considered as static.
215      *
216      * Indicates that no DOM listeners should be registered.
217      * In this case the generated GVT tree should be totally
218      * independent of the DOM tree (in practice text holds
219      * references to the source text elements for font resolution).
220      */

221     public final static int ALWAYS_STATIC = 2;
222
223     /**
224      * Means that all document must be considered as interactive.
225      *
226      * Indicates that DOM listeners should be registered to support,
227      * 'interactivity' this includes anchors and cursors, but does not
228      * include support for DOM modifications.
229      */

230     public final static int ALWAYS_INTERACTIVE = 3;
231
232     /**
233      * The document loader.
234      */

235     protected SVGDocumentLoader documentLoader;
236
237     /**
238      * The next document loader to run.
239      */

240     protected SVGDocumentLoader nextDocumentLoader;
241
242     /**
243      * The concrete bridge document loader.
244      */

245     protected DocumentLoader loader;
246
247     /**
248      * The GVT tree builder.
249      */

250     protected GVTTreeBuilder gvtTreeBuilder;
251
252     /**
253      * The next GVT tree builder to run.
254      */

255     protected GVTTreeBuilder nextGVTTreeBuilder;
256
257     /**
258      * The SVGLoadEventDispatcher.
259      */

260     protected SVGLoadEventDispatcher svgLoadEventDispatcher;
261
262     /**
263      * The update manager.
264      */

265     protected UpdateManager updateManager;
266
267     /**
268      * The next update manager.
269      */

270     protected UpdateManager nextUpdateManager;
271
272     /**
273      * The current SVG document.
274      */

275     protected SVGDocument svgDocument;
276
277     /**
278      * The document loader listeners.
279      */

280     protected List JavaDoc svgDocumentLoaderListeners = new LinkedList JavaDoc();
281
282     /**
283      * The GVT tree builder listeners.
284      */

285     protected List JavaDoc gvtTreeBuilderListeners = new LinkedList JavaDoc();
286
287     /**
288      * The SVG onload dispatcher listeners.
289      */

290     protected List JavaDoc svgLoadEventDispatcherListeners = new LinkedList JavaDoc();
291
292     /**
293      * The link activation listeners.
294      */

295     protected List JavaDoc linkActivationListeners = new LinkedList JavaDoc();
296
297     /**
298      * The update manager listeners.
299      */

300     protected List JavaDoc updateManagerListeners = new LinkedList JavaDoc();
301
302     /**
303      * The user agent.
304      */

305     protected UserAgent userAgent;
306
307     /**
308      * The SVG user agent.
309      */

310     protected SVGUserAgent svgUserAgent;
311
312     /**
313      * The current bridge context.
314      */

315     protected BridgeContext bridgeContext;
316
317     /**
318      * The current document fragment identifier.
319      */

320     protected String JavaDoc fragmentIdentifier;
321
322     /**
323      * Whether the current document has dynamic features.
324      */

325     protected boolean isDynamicDocument;
326
327     /**
328      * Whether the current document has dynamic features.
329      */

330     protected boolean isInteractiveDocument;
331
332     /**
333      * The document state.
334      */

335     protected int documentState;
336
337     protected Dimension JavaDoc prevComponentSize;
338
339     protected Runnable JavaDoc afterStopRunnable = null;
340
341     protected SVGUpdateOverlay updateOverlay; // = new SVGUpdateOverlay(20, 4);
342

343     protected boolean recenterOnResize = true;
344
345     protected AffineTransform JavaDoc viewingTransform = null;
346
347     /**
348      * Creates a new JSVGComponent.
349      */

350     public JSVGComponent() {
351         this(null, false, false);
352     }
353
354     /**
355      * Creates a new JSVGComponent.
356      * @param ua a SVGUserAgent instance or null.
357      * @param eventsEnabled Whether the GVT tree should be reactive
358      * to mouse and key events.
359      * @param selectableText Whether the text should be selectable.
360      */

361     public JSVGComponent(SVGUserAgent ua, boolean eventsEnabled,
362                          boolean selectableText) {
363         super(eventsEnabled, selectableText);
364
365         svgUserAgent = ua;
366
367         userAgent = new BridgeUserAgentWrapper(createUserAgent());
368
369         addSVGDocumentLoaderListener((SVGListener)listener);
370         addGVTTreeBuilderListener((SVGListener)listener);
371         addSVGLoadEventDispatcherListener((SVGListener)listener);
372         if (updateOverlay != null)
373             getOverlays().add(updateOverlay);
374     }
375
376     public void dispose() {
377         setSVGDocument(null);
378     }
379
380     /**
381      * Indicates if the canvas should recenter the content after
382      * the canvas is resized. If true it will try and keep the
383      * point that was at the center at the center after resize.
384      * Otherwise the upper left corner will be kept at the same point.
385      */

386     public boolean getRecenterOnResize() {
387         return recenterOnResize;
388     }
389     /**
390      * Returns sate of the recenter on resize flag.
391      */

392     public void setRecenterOnResize(boolean recenterOnResize) {
393         this.recenterOnResize = recenterOnResize;
394     }
395
396     /**
397      * Tells whether the component use dynamic features to
398      * process the current document.
399      */

400     public boolean isDynamic() {
401         return isDynamicDocument;
402     }
403
404     /**
405      * Tells whether the component use dynamic features to
406      * process the current document.
407      */

408     public boolean isInteractive() {
409         return isInteractiveDocument;
410     }
411
412     /**
413      * Sets the document state. The given value must be one of
414      * AUTODETECT, ALWAYS_DYNAMIC or ALWAYS_STATIC. This only
415      * effects the loading of subsequent documents, it has no
416      * effect on the currently loaded document.
417      */

418     public void setDocumentState(int state) {
419         documentState = state;
420     }
421
422     /**
423      * Returns the current update manager. The update manager becomes
424      * available after the first rendering completes. You can be
425      * notifed when the rendering completes by registering a
426      * GVTTreeRendererListener with the component and waiting for the
427      * <tt>gvtRenderingCompleted</tt> event.
428      *
429      * An UpdateManager is only created for Dynamic documents. By
430      * default the Canvas attempts to autodetect dynamic documents by
431      * looking for script elements and/or event attributes in the
432      * document, if it does not find these it assumes the document is
433      * static. Callers of this method will almost certainly want to
434      * call setDocumentState(ALWAYS_DYNAMIC) before loading the document
435      * (with setURI, setDocument, setSVGDocument etc.) so that an
436      * UpdateManager is always created (even for apparently static documents).
437      */

438     public UpdateManager getUpdateManager() {
439         if (svgLoadEventDispatcher != null) {
440             return svgLoadEventDispatcher.getUpdateManager();
441         }
442         if (nextUpdateManager != null) {
443             return nextUpdateManager;
444         }
445         return updateManager;
446     }
447
448     /**
449      * Resumes the processing of the current document.
450      */

451     public void resumeProcessing() {
452         if (updateManager != null) {
453             updateManager.resume();
454         }
455     }
456
457     /**
458      * Suspend the processing of the current document.
459      */

460     public void suspendProcessing() {
461         if (updateManager != null) {
462             updateManager.suspend();
463         }
464     }
465
466     /**
467      * Stops the processing of the current document.
468      */

469     public void stopProcessing() {
470         nextDocumentLoader = null;
471         nextGVTTreeBuilder = null;
472
473         if (documentLoader != null) {
474             documentLoader.halt();
475         }
476         if (gvtTreeBuilder != null) {
477             gvtTreeBuilder.halt();
478         }
479         if (svgLoadEventDispatcher != null) {
480             svgLoadEventDispatcher.halt();
481         }
482         if (nextUpdateManager != null) {
483             nextUpdateManager.interrupt();
484             nextUpdateManager = null;
485         }
486         if (updateManager != null) {
487             updateManager.interrupt();
488         }
489         super.stopProcessing();
490     }
491
492     /**
493      * Loads a SVG document from the given URL.
494      * <em>Note: Because the loading is multi-threaded, the current
495      * SVG document is not garanteed to be updated after this method
496      * returns. The only way to be notified a document has been loaded
497      * is to listen to the <tt>SVGDocumentLoaderEvent</tt>s.</em>
498      */

499     public void loadSVGDocument(String JavaDoc url) {
500         String JavaDoc oldURI = null;
501         if (svgDocument != null) {
502             oldURI = svgDocument.getURL();
503         }
504         final ParsedURL newURI = new ParsedURL(oldURI, url);
505
506         stopThenRun(new Runnable JavaDoc() {
507                 public void run() {
508                     String JavaDoc url = newURI.toString();
509                     fragmentIdentifier = newURI.getRef();
510
511                     loader = new DocumentLoader(userAgent);
512                     nextDocumentLoader = new SVGDocumentLoader(url, loader);
513                     nextDocumentLoader.setPriority(Thread.MIN_PRIORITY);
514
515                     Iterator JavaDoc it = svgDocumentLoaderListeners.iterator();
516                     while (it.hasNext()) {
517                         nextDocumentLoader.addSVGDocumentLoaderListener
518                             ((SVGDocumentLoaderListener)it.next());
519                     }
520                     startDocumentLoader();
521                 }
522             });
523     }
524
525     /**
526      * Starts a loading thread.
527      */

528     private void startDocumentLoader() {
529         documentLoader = nextDocumentLoader;
530         nextDocumentLoader = null;
531         documentLoader.start();
532     }
533
534     /**
535      * Sets the Document to display. If the document does not use
536      * Batik's SVG DOM Implemenation it will be cloned into that
537      * implementation. In this case you should use 'getSVGDocument()'
538      * to get the actual DOM that is attached to the rendering interface.
539      *
540      * Note that the prepartation for rendering and the rendering it's
541      * self occur asynchronously so you need to register event handlers
542      * if you want to know when the document is truely displayed.
543      *
544      * Notes for documents that you want to change in Java:
545      * From this point on you may only modify the
546      * the document in the UpdateManager thread @see #getUpdateManager.
547      * In many cases you also need to tell Batik to treat the document
548      * as a dynamic document by calling setDocumentState(ALWAYS_DYNAMIC).
549      */

550     public void setDocument(Document JavaDoc doc) {
551         if ((doc != null) &&
552             !(doc.getImplementation() instanceof SVGDOMImplementation)) {
553             DOMImplementation impl;
554             impl = SVGDOMImplementation.getDOMImplementation();
555             Document JavaDoc d = DOMUtilities.deepCloneDocument(doc, impl);
556             doc = d;
557         }
558         setSVGDocument((SVGDocument)doc);
559     }
560
561     /**
562      * Sets the SVGDocument to display. If the document does not use
563      * Batik's SVG DOM Implemenation it will be cloned into that
564      * implementation. In this case you should use 'getSVGDocument()'
565      * to get the actual DOM that is attached to the rendering
566      * interface.
567      *
568      * Note that the prepartation for rendering and the rendering it's
569      * self occur asynchronously so you need to register event handlers
570      * if you want to know when the document is truely displayed.
571      *
572      * Notes for documents that you want to change in Java.
573      * From this point on you may only modify the
574      * the document in the UpdateManager thread @see #getUpdateManager.
575      * In many cases you also need to tell Batik to treat the document
576      * as a dynamic document by calling setDocumentState(ALWAYS_DYNAMIC).
577      */

578     public void setSVGDocument(SVGDocument doc) {
579         if ((doc != null) &&
580             !(doc.getImplementation() instanceof SVGDOMImplementation)) {
581             DOMImplementation impl;
582             impl = SVGDOMImplementation.getDOMImplementation();
583             Document JavaDoc d = DOMUtilities.deepCloneDocument(doc, impl);
584             doc = (SVGDocument)d;
585         }
586
587         final SVGDocument svgdoc = doc;
588         stopThenRun(new Runnable JavaDoc() {
589                 public void run() {
590                     installSVGDocument(svgdoc);
591                 }
592             });
593     }
594
595     /**
596      * This method calls stop processing waits for all
597      * threads to die then runs the Runnable in the event
598      * queue thread. It returns immediately.
599      */

600     protected void stopThenRun(final Runnable JavaDoc r) {
601         if (afterStopRunnable != null) {
602             // Have it run our new runnable, and not
603
// run the 'old' runnable.
604
afterStopRunnable = r;
605             return;
606         }
607         afterStopRunnable = r;
608
609         stopProcessing();
610
611         if ((documentLoader == null) &&
612             (gvtTreeBuilder == null) &&
613             (gvtTreeRenderer == null) &&
614             (svgLoadEventDispatcher == null) &&
615             (nextUpdateManager == null) &&
616             (updateManager == null)) {
617             Runnable JavaDoc asr = afterStopRunnable;
618             afterStopRunnable = null;
619             asr.run();
620         }
621     }
622
623     /**
624      * This does the real work of installing the SVG Document after
625      * the update manager from the previous document (if any) has been
626      * properly 'shut down'.
627      */

628     protected void installSVGDocument(SVGDocument doc) {
629         svgDocument = doc;
630
631         if (bridgeContext != null) {
632             bridgeContext.dispose();
633             bridgeContext = null;
634         }
635
636         releaseRenderingReferences();
637
638         if (doc == null) {
639             isDynamicDocument = false;
640             isInteractiveDocument = false;
641             disableInteractions = true;
642             initialTransform = new AffineTransform JavaDoc();
643             setRenderingTransform(initialTransform, false);
644             Rectangle JavaDoc vRect = getRenderRect();
645             repaint(vRect.x, vRect.y,
646                     vRect.width, vRect.height);
647             return;
648         }
649
650         bridgeContext = createBridgeContext();
651
652         switch (documentState) {
653         case ALWAYS_STATIC:
654             isDynamicDocument = false;
655             isInteractiveDocument = false;
656             break;
657         case ALWAYS_INTERACTIVE:
658             isDynamicDocument = false;
659             isInteractiveDocument = true;
660             break;
661         case ALWAYS_DYNAMIC:
662             isDynamicDocument = true;
663             isInteractiveDocument = true;
664             break;
665         case AUTODETECT:
666             isDynamicDocument = bridgeContext.isDynamicDocument(doc);
667             if (isDynamicDocument)
668                 isInteractiveDocument = true;
669             else
670                 isInteractiveDocument =
671                     bridgeContext.isInteractiveDocument(doc);
672         }
673
674         if (isInteractiveDocument) {
675             if (isDynamicDocument)
676                 bridgeContext.setDynamicState(BridgeContext.DYNAMIC);
677             else
678                 bridgeContext.setDynamicState(BridgeContext.INTERACTIVE);
679         }
680
681         Element JavaDoc root = doc.getDocumentElement();
682         String JavaDoc znp = root.getAttributeNS
683             (null, SVGConstants.SVG_ZOOM_AND_PAN_ATTRIBUTE);
684
685         setDisableInteractions(!znp.equals(SVGConstants.SVG_MAGNIFY_VALUE));
686
687         nextGVTTreeBuilder = new GVTTreeBuilder(doc, bridgeContext);
688         nextGVTTreeBuilder.setPriority(Thread.MIN_PRIORITY);
689
690         Iterator JavaDoc it = gvtTreeBuilderListeners.iterator();
691         while (it.hasNext()) {
692             nextGVTTreeBuilder.addGVTTreeBuilderListener
693                 ((GVTTreeBuilderListener)it.next());
694         }
695
696         initializeEventHandling();
697
698         if (gvtTreeBuilder == null &&
699             documentLoader == null &&
700             gvtTreeRenderer == null &&
701             svgLoadEventDispatcher == null &&
702             updateManager == null) {
703             startGVTTreeBuilder();
704         }
705     }
706
707     /**
708      * Starts a tree builder.
709      */

710     protected void startGVTTreeBuilder() {
711         gvtTreeBuilder = nextGVTTreeBuilder;
712         nextGVTTreeBuilder = null;
713         gvtTreeBuilder.start();
714     }
715
716     /**
717      * Returns the current SVG document.
718      */

719     public SVGDocument getSVGDocument() {
720         return svgDocument;
721     }
722
723     /**
724      * Returns the size of the SVG document.
725      */

726     public Dimension2D JavaDoc getSVGDocumentSize() {
727         return bridgeContext.getDocumentSize();
728     }
729
730     /**
731      * Returns the current's document fragment identifier.
732      */

733     public String JavaDoc getFragmentIdentifier() {
734         return fragmentIdentifier;
735     }
736
737     /**
738      * Sets the current fragment identifier.
739      */

740     public void setFragmentIdentifier(String JavaDoc fi) {
741         fragmentIdentifier = fi;
742         if (computeRenderingTransform())
743             scheduleGVTRendering();
744     }
745
746     /**
747      * Removes all images from the image cache.
748      */

749     public void flushImageCache() {
750         ImageTagRegistry reg = ImageTagRegistry.getRegistry();
751         reg.flushCache();
752     }
753
754     public void setGraphicsNode(GraphicsNode gn, boolean createDispatcher) {
755         Dimension2D JavaDoc dim = bridgeContext.getDocumentSize();
756         Dimension JavaDoc mySz = new Dimension JavaDoc((int)dim.getWidth(),
757                                          (int)dim.getHeight());
758         JSVGComponent.this.setMySize(mySz);
759         SVGSVGElement elt = svgDocument.getRootElement();
760         prevComponentSize = getSize();
761         AffineTransform JavaDoc at = calculateViewingTransform
762             (fragmentIdentifier, elt);
763         CanvasGraphicsNode cgn = getCanvasGraphicsNode(gn);
764         cgn.setViewingTransform(at);
765         viewingTransform = null;
766         initialTransform = new AffineTransform JavaDoc();
767         setRenderingTransform(initialTransform, false);
768         jsvgComponentListener.updateMatrix(initialTransform);
769         addJGVTComponentListener(jsvgComponentListener);
770         addComponentListener(jsvgComponentListener);
771         super.setGraphicsNode(gn, createDispatcher);
772     }
773
774     /**
775      * Creates a new bridge context.
776      */

777     protected BridgeContext createBridgeContext() {
778         if (loader == null) {
779             loader = new DocumentLoader(userAgent);
780         }
781         BridgeContext result = new BridgeContext(userAgent, loader);
782         return result;
783     }
784
785     /**
786      * Starts a SVGLoadEventDispatcher thread.
787      */

788     protected void startSVGLoadEventDispatcher(GraphicsNode root) {
789         UpdateManager um = new UpdateManager(bridgeContext,
790                                              root,
791                                              svgDocument);
792         svgLoadEventDispatcher =
793             new SVGLoadEventDispatcher(root,
794                                        svgDocument,
795                                        bridgeContext,
796                                        um);
797         Iterator JavaDoc it = svgLoadEventDispatcherListeners.iterator();
798         while (it.hasNext()) {
799             svgLoadEventDispatcher.addSVGLoadEventDispatcherListener
800                 ((SVGLoadEventDispatcherListener)it.next());
801         }
802
803         svgLoadEventDispatcher.start();
804     }
805
806     /**
807      * Creates a new renderer.
808      */

809     protected ImageRenderer createImageRenderer() {
810         if (isDynamicDocument) {
811             return rendererFactory.createDynamicImageRenderer();
812         } else {
813             return rendererFactory.createStaticImageRenderer();
814         }
815     }
816
817     public CanvasGraphicsNode getCanvasGraphicsNode() {
818         return getCanvasGraphicsNode(gvtRoot);
819         
820     }
821
822     protected CanvasGraphicsNode getCanvasGraphicsNode(GraphicsNode gn) {
823         if (!(gn instanceof CompositeGraphicsNode))
824             return null;
825         CompositeGraphicsNode cgn = (CompositeGraphicsNode)gn;
826         List JavaDoc children = cgn.getChildren();
827         if (children.size() == 0)
828             return null;
829         gn = (GraphicsNode)children.get(0);
830         if (!(gn instanceof CanvasGraphicsNode))
831             return null;
832         return (CanvasGraphicsNode)gn;
833     }
834
835     public AffineTransform JavaDoc getViewingTransform() {
836         AffineTransform JavaDoc vt;
837         synchronized (this) {
838             vt = viewingTransform;
839             if (vt == null) {
840                 CanvasGraphicsNode cgn = getCanvasGraphicsNode();
841                 if (cgn != null)
842                     vt = cgn.getViewingTransform();
843             }
844         }
845         return vt;
846     }
847
848     /**
849      * Returns the transform from viewBox coords to screen coords
850      */

851     public AffineTransform JavaDoc getViewBoxTransform() {
852         AffineTransform JavaDoc at = getRenderingTransform();
853         if (at == null) at = new AffineTransform JavaDoc();
854         else at = new AffineTransform JavaDoc(at);
855         AffineTransform JavaDoc vt = getViewingTransform();
856         if (vt != null) {
857             at.concatenate(vt);
858         }
859         return at;
860     }
861
862     /**
863      * Computes the transform used for rendering.
864      * Returns true if the component needs to be repainted.
865      */

866     protected boolean computeRenderingTransform() {
867         if ((svgDocument == null) || (gvtRoot == null))
868             return false;
869
870         boolean ret = updateRenderingTransform();
871         initialTransform = new AffineTransform JavaDoc();
872         if (!initialTransform.equals(getRenderingTransform())) {
873             setRenderingTransform(initialTransform, false);
874             ret = true;
875         }
876         return ret;
877     }
878
879     protected AffineTransform JavaDoc calculateViewingTransform
880         (String JavaDoc fragIdent, SVGSVGElement svgElt) {
881         Dimension JavaDoc d = getSize();
882         if (d.width < 1) d.width = 1;
883         if (d.height < 1) d.height = 1;
884         return ViewBox.getViewTransform
885             (fragIdent, svgElt, d.width, d.height);
886     }
887
888     /**
889      * Updates the value of the transform used for rendering.
890      * Return true if a repaint is required, otherwise false.
891      */

892     protected boolean updateRenderingTransform() {
893         if ((svgDocument == null) || (gvtRoot == null))
894             return false;
895
896         try {
897             SVGSVGElement elt = svgDocument.getRootElement();
898             Dimension JavaDoc d = getSize();
899             Dimension