/*****************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved. *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included with this distribution in *
* the LICENSE file. *
*****************************************************************************/
package org.apache.batik.swing.svg;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Window;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.JOptionPane;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.BridgeException;
import org.apache.batik.bridge.BridgeExtension;
import org.apache.batik.bridge.DefaultScriptSecurity;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.ExternalResourceSecurity;
import org.apache.batik.bridge.RelaxedExternalResourceSecurity;
import org.apache.batik.bridge.ScriptSecurity;
import org.apache.batik.bridge.UpdateManager;
import org.apache.batik.bridge.UpdateManagerEvent;
import org.apache.batik.bridge.UpdateManagerListener;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.bridge.ViewBox;
import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.dom.util.DOMUtilities;
import org.apache.batik.dom.util.XLinkSupport;
import org.apache.batik.ext.awt.image.spi.ImageTagRegistry;
import org.apache.batik.gvt.CanvasGraphicsNode;
import org.apache.batik.gvt.CompositeGraphicsNode;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.gvt.event.EventDispatcher;
import org.apache.batik.gvt.renderer.ImageRenderer;
import org.apache.batik.swing.gvt.GVTTreeRenderer;
import org.apache.batik.swing.gvt.GVTTreeRendererEvent;
import org.apache.batik.swing.gvt.GVTTreeRendererAdapter;
import org.apache.batik.swing.gvt.JGVTComponent;
import org.apache.batik.swing.gvt.JGVTComponentListener;
import org.apache.batik.util.ParsedURL;
import org.apache.batik.util.RunnableQueue;
import org.apache.batik.util.SVGConstants;
import org.apache.batik.util.XMLResourceDescriptor;
import org.w3c.dom.Document;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Element;
import org.w3c.dom.svg.SVGAElement;
import org.w3c.dom.svg.SVGDocument;
import org.w3c.dom.svg.SVGSVGElement;
/**
* This class represents a swing component that can display SVG documents. This
* component also lets you translate, zoom and rotate the document being
* displayed. This is the fundamental class for rendering SVG documents in a
* swing application.
*
* <h2>Rendering Process</h2>
*
* <p>The rendering process can be broken down into five phases. Not all of
* those steps are required - depending on the method used to specify the SVG
* document to display, but basically the steps in the rendering process
* are:</p>
*
* <ol>
*
* <li><b>Building a DOM tree</b>
*
* <blockquote>If the <tt>{@link #loadSVGDocument(String)}</tt> method is used,
* the SVG file is parsed and an SVG DOM Tree is built.</blockquote></li>
*
* <li><b>Building a GVT tree</b>
*
* <blockquote>Once an SVGDocument is created (using the step 1 or if the
* <tt>{@link #setSVGDocument(SVGDocument)}</tt> method has been used) - a GVT
* tree is constructed. The GVT tree is the data structure used internally to
* render an SVG document. see the <tt>{@link org.apache.batik.gvt}
* package.</tt></blockquote></li>
*
* <li><b>Executing the SVGLoad event handlers</b>
*
* <blockquote>
* If the document is dynamic, the scripts are initialized and the
* SVGLoad event is dispatched before the initial rendering.
* </blockquote></li>
*
* <li><b>Rendering the GVT tree</b>
*
* <blockquote>Then the GVT tree is rendered. see the <tt>{@link
* org.apache.batik.gvt.renderer}</tt> package.</blockquote></li>
*
* <li><b>Running the document</b>
*
* <blockquote>
* If the document is dynamic, the update threads are started.
* </blockquote></li>
*
* </ol>
*
* <p>Those steps are performed in a separate thread. To be notified to what
* happens and eventually perform some operations - such as resizing the window
* to the size of the document or get the SVGDocument built via a URI, five
* different listeners are provided (one per step):
* <tt>{@link SVGDocumentLoaderListener}</tt>,
* <tt>{@link GVTTreeBuilderListener}</tt>,
* <tt>{@link SVGLoadEventDispatcherListener}</tt>,
* <tt>{@link org.apache.batik.swing.gvt.GVTTreeRendererListener}</tt>,
* <tt>{@link org.apache.batik.bridge.UpdateManagerListener}</tt>.</p>
*
* <p>Each listener has methods to be notified of the start of a phase,
* and methods to be notified of the end of a phase.
* A phase cannot start before the preceding has finished.</p>
*
* <p>The following example shows how you can get the size of an SVG
* document. Note that due to how SVG is designed (units, percentages...), the
* size of an SVG document can be known only once the SVGDocument has been
* analyzed (ie. the GVT tree has been constructed).</p>
*
* <pre>
* final JSVGComponent svgComp = new JSVGComponent();
* svgComp.loadSVGDocument("foo.svg");
* svgComp.addGVTTreeBuilderListener(new GVTTreeBuilderAdapter() {
* public void gvtBuildCompleted(GVTTreeBuilderEvent evt) {
* Dimension2D size = svgComp.getSVGDocumentSize();
* // ...
* }
* });
* </pre>
*
* <p>The second example shows how you can access to the DOM tree when a URI has
* been used to display an SVG document.
*
* <pre>
* final JSVGComponent svgComp = new JSVGComponent();
* svgComp.loadSVGDocument("foo.svg");
* svgComp.addSVGDocumentLoaderListener(new SVGDocumentLoaderAdapter() {
* public void documentLoadingCompleted(SVGDocumentLoaderEvent evt) {
* SVGDocument svgDoc = svgComp.getSVGDocument();
* //...
* }
* });
* </pre>
*
* <p>Conformed to the <a href=
* "http://java.sun.com/docs/books/tutorial/uiswing/overview/threads.html">
* single thread rule of swing</a>, the listeners are executed in the swing
* thread. The sequence of the method calls for a particular listener and
* the order of the listeners themselves are <em>guaranteed</em>.</p>
*
* <h2>User Agent</h2>
*
* <p>The <tt>JSVGComponent</tt> can pick up some informations to a user
* agent. The <tt>{@link SVGUserAgent}</tt> provides a way to control the
* resolution used to display an SVG document (controling the pixel to
* millimeter conversion factor), perform an operation in respond to a click on
* an hyperlink, control the default language to use, or specify a user
* stylesheet, or how to display errors when an error occured while
* building/rendering a document (invalid XML file, missing attributes...).</p>
*
* @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
* @version $Id: JSVGComponent.java,v 1.75 2003/07/10 12:01:20 deweese Exp $
*/
public class JSVGComponent extends JGVTComponent {
/**
* Means that the component must auto detect whether
* the current document is static or dynamic.
*/
public final static int AUTODETECT = 0;
/**
* Means that all document must be considered as dynamic.
*/
public final static int ALWAYS_DYNAMIC = 1;
/**
* Means that all document must be considered as static.
*/
public final static int ALWAYS_STATIC = 2;
/**
* Means that all document must be considered as interactive.
*/
public final static int ALWAYS_INTERACTIVE = 3;
/**
* The document loader.
*/
protected SVGDocumentLoader documentLoader;
/**
* The next document loader to run.
*/
protected SVGDocumentLoader nextDocumentLoader;
/**
* The concrete bridge document loader.
*/
protected DocumentLoader loader;
/**
* The GVT tree builder.
*/
protected GVTTreeBuilder gvtTreeBuilder;
/**
* The next GVT tree builder to run.
*/
protected GVTTreeBuilder nextGVTTreeBuilder;
/**
* The SVGLoadEventDispatcher.
*/
protected SVGLoadEventDispatcher svgLoadEventDispatcher;
/**
* The update manager.
*/
protected UpdateManager updateManager;
/**
* The next update manager.
*/
protected UpdateManager nextUpdateManager;
/**
* The current SVG document.
*/
protected SVGDocument svgDocument;
/**
* The document loader listeners.
*/
protected List svgDocumentLoaderListeners = new LinkedList();
/**
* The GVT tree builder listeners.
*/
protected List gvtTreeBuilderListeners = new LinkedList();
/**
* The SVG onload dispatcher listeners.
*/
protected List svgLoadEventDispatcherListeners = new LinkedList();
/**
* The link activation listeners.
*/
protected List linkActivationListeners = new LinkedList();
/**
* The update manager listeners.
*/
protected List updateManagerListeners = new LinkedList();
/**
* The user agent.
*/
protected UserAgent userAgent;
/**
* The SVG user agent.
*/
protected SVGUserAgent svgUserAgent;
/**
* The current bridge context.
*/
protected BridgeContext bridgeContext;
/**
* The current document fragment identifier.
*/
protected String fragmentIdentifier;
/**
* Whether the current document has dynamic features.
*/
protected boolean isDynamicDocument;
/**
* Whether the current document has dynamic features.
*/
protected boolean isInteractiveDocument;
/**
* The document state.
*/
protected int documentState;
protected Dimension prevComponentSize;
/**
* Creates a new JSVGComponent.
*/
public JSVGComponent() {
this(null, false, false);
}
/**
* Creates a new JSVGComponent.
* @param ua a SVGUserAgent instance or null.
* @param eventEnabled Whether the GVT tree should be reactive
* to mouse and key events.
* @param selectableText Whether the text should be selectable.
*/
public JSVGComponent(SVGUserAgent ua, boolean eventsEnabled,
boolean selectableText) {
super(eventsEnabled, selectableText);
svgUserAgent = ua;
userAgent = new BridgeUserAgentWrapper(createUserAgent());
addSVGDocumentLoaderListener((SVGListener)listener);
addGVTTreeBuilderListener((SVGListener)listener);
addSVGLoadEventDispatcherListener((SVGListener)listener);
}
public void removeNotify() {
setSVGDocument(null);
super.removeNotify();
}
/**
* Tells whether the component use dynamic features to
* process the current document.
*/
public boolean isDynamic() {
return isDynamicDocument;
}
/**
* Tells whether the component use dynamic features to
* process the current document.
*/
public boolean isInteractive() {
return isInteractiveDocument;
}
/**
* Sets the document state. The given value must be one of
* AUTODETECT, ALWAYS_DYNAMIC or ALWAYS_STATIC.
*/
public void setDocumentState(int state) {
documentState = state;
}
/**
* Returns the current update manager. The update manager becomes
* available after the first rendering completes. You can be
* notifed when the rendering completes by registering a
* GVTTreeRendererListener with the component and waiting for the
* <tt>gvtRenderingCompleted</tt> event.
*/
public UpdateManager getUpdateManager() {
if (svgLoadEventDispatcher != null) {
return svgLoadEventDispatcher.getUpdateManager();
}
if (nextUpdateManager != null) {
return nextUpdateManager;
}
return updateManager;
}
/**
* Resumes the processing of the current document.
*/
public void resumeProcessing() {
if (updateManager != null) {
updateManager.resume();
}
}
/**
* Suspend the processing of the current document.
*/
public void suspendProcessing() {
if (updateManager != null) {
updateManager.suspend();
}
}
/**
* Stops the processing of the current document.
*/
public void stopProcessing() {
nextDocumentLoader = null;
nextGVTTreeBuilder = null;
if (documentLoader != null) {
documentLoader.interrupt();
} else if (gvtTreeBuilder != null) {
gvtTreeBuilder.interrupt();
} else if (svgLoadEventDispatcher != null) {
svgLoadEventDispatcher.interrupt();
} else if (nextUpdateManager != null) {
nextUpdateManager.interrupt();
nextUpdateManager = null;
} else if (updateManager != null) {
updateManager.interrupt();
} else {
super.stopProcessing();
}
}
/**
* Loads a SVG document from the given URL.
* <em>Note: Because the loading is multi-threaded, the current
* SVG document is not garanteed to be updated after this method
* returns. The only way to be notified a document has been loaded
* is to listen to the <tt>SVGDocumentLoaderEvent</tt>s.</em>
*/
public void loadSVGDocument(String url) {
String oldURI = null;
if (svgDocument != null) {
oldURI = svgDocument.getURL();
}
final ParsedURL newURI = new ParsedURL(oldURI, url);
stopThenRun(new Runnable() {
public void run() {
String url = newURI.toString();
fragmentIdentifier = newURI.getRef();
loader = new DocumentLoader(userAgent);
nextDocumentLoader = new SVGDocumentLoader(url, loader);
nextDocumentLoader.setPriority(Thread.MIN_PRIORITY);
Iterator it = svgDocumentLoaderListeners.iterator();
while (it.hasNext()) {
nextDocumentLoader.addSVGDocumentLoaderListener
((SVGDocumentLoaderListener)it.next());
}
startDocumentLoader();
}
});
}
/**
* Starts a loading thread.
*/
private void startDocumentLoader() {
documentLoader = nextDocumentLoader;
nextDocumentLoader = null;
documentLoader.start();
}
/**
* Sets the Document to display. If the document does not use
* Batik's SVG DOM Implemenation it will be cloned into that
* implementation. In this case you should use 'getSVGDocument()'
* to get the actual DOM that is attached to the rendering interface.
*
* Note that the prepartation for rendering and the rendering it's
* self occur asynchronously so you need to register event handlers
* if you want to know when the document is truely displayed.
*
* Notes for documents that you want to change in Java:
* From this point on you may only modify the
* the document in the UpdateManager thread @see #getUpdateManager.
* In many cases you also need to tell Batik to treat the document
* as a dynamic document by calling setDocumentState(ALWAYS_DYNAMIC).
*/
public void setDocument(Document doc) {
if ((doc != null) &&
!(doc.getImplementation() instanceof SVGDOMImplementation)) {
DOMImplementation impl;
impl = SVGDOMImplementation.getDOMImplementation();
Document d = DOMUtilities.deepCloneDocument(doc, impl);
doc = d;
}
setSVGDocument((SVGDocument)doc);
}
/**
* Sets the SVGDocument to display. If the document does not use
* Batik's SVG DOM Implemenation it will be cloned into that
* implementation. In this case you should use 'getSVGDocument()'
* to get the actual DOM that is attached to the rendering
* interface.
*
* Note that the prepartation for rendering and the rendering it's
* self occur asynchronously so you need to register event handlers
* if you want to know when the document is truely displayed.
*
* Notes for documents that you want to change in Java.
* From this point on you may only modify the
* the document in the UpdateManager thread @see #getUpdateManager.
* In many cases you also need to tell Batik to treat the document
* as a dynamic document by calling setDocumentState(ALWAYS_DYNAMIC).
*/
public void setSVGDocument(SVGDocument doc) {
if ((doc != null) &&
!(doc.getImplementation() instanceof SVGDOMImplementation)) {
DOMImplementation impl;
impl = SVGDOMImplementation.getDOMImplementation();
Document d = DOMUtilities.deepCloneDocument(doc, impl);
doc = (SVGDocument)d;
}
final SVGDocument svgdoc = doc;
stopThenRun(new Runnable() {
public void run() {
installSVGDocument(svgdoc);
}
});
}
/**
* This method calls stop processing waits for all
* threads to die then runs the Runnable in the event
* queue thread. It returns immediately.
*/
protected void stopThenRun(final Runnable r) {
if (documentLoader != null) {
documentLoader.addSVGDocumentLoaderListener
(new SVGDocumentLoaderAdapter() {
SVGDocumentLoader sdl = documentLoader;
public void documentLoadingCompleted
(SVGDocumentLoaderEvent e) { install(); }
public void documentLoadingCancelled
(SVGDocumentLoaderEvent e) { install(); }
public void documentLoadingFailed
(SVGDocumentLoaderEvent e) { install(); }
public void install() {
// Remove ourselves from the doc loader,
// and install the new document.
sdl.removeSVGDocumentLoaderListener(this);
EventQueue.invokeLater(r);
}
});
stopProcessing();
} else if (gvtTreeBuilder != null) {
gvtTreeBuilder.addGVTTreeBuilderListener
(new GVTTreeBuilderAdapter() {
GVTTreeBuilder gtb = gvtTreeBuilder;
public void gvtBuildCompleted
(GVTTreeBuilderEvent e) { install(); }
public void gvtBuildCancelled
(GVTTreeBuilderEvent e) { install(); }
public void gvtBuildFailed
(GVTTreeBuilderEvent e) { install(); }
public void install() {
// Remove ourselves from the old tree builder,
// and install the new document.
gtb.removeGVTTreeBuilderListener(this);
EventQueue.invokeLater(r);
}
});
stopProcessing();
} else if (updateManager != null) {
// We have to wait for the update manager to stop
// before we install the new document otherwise bad
// things can happen with the update manager.
updateManager.addUpdateManagerListener
(new UpdateManagerListener () {
UpdateManager um = updateManager;
public void managerStopped(UpdateManagerEvent e) {
// Remove ourselves from the old update manger,
// and install the new document.
um.removeUpdateManagerListener(this);
EventQueue.invokeLater(r);
}
// There really should be an updateManagerAdapter.
public void managerStarted(UpdateManagerEvent e) { }
public void managerSuspended(UpdateManagerEvent e) { }
public void managerResumed(UpdateManagerEvent e) { }
public void updateStarted(UpdateManagerEvent e) { }
public void updateCompleted(UpdateManagerEvent e) { }
public void updateFailed(UpdateManagerEvent e) { }
});
stopProcessing();
} else if (nextUpdateManager != null) {
// We have to wait for the rendering to stop
// before we install the new document.
gvtTreeRenderer.addGVTTreeRendererListener
(new GVTTreeRendererAdapter() {
GVTTreeRenderer gtr = gvtTreeRenderer;
public void gvtRenderingCompleted
(GVTTreeRendererEvent e) { install(); }
public void gvtRenderingCancelled
(GVTTreeRendererEvent e) { install(); }
public void gvtRenderingFailed
(GVTTreeRendererEvent e) { install(); }
public void install() {
// Remove ourselves from the old update manger,
// and install the new document.
gtr.removeGVTTreeRendererListener(this);
EventQueue.invokeLater(r);
}
});
stopProcessing();
} else if (svgLoadEventDispatcher != null) {
// We have to wait for the onload dispatch to stop
// before we install the new document.
svgLoadEventDispatcher.addSVGLoadEventDispatcherListener
(new SVGLoadEventDispatcherAdapter() {
SVGLoadEventDispatcher sled = svgLoadEventDispatcher;
public void svgLoadEventDispatchCompleted
(SVGLoadEventDispatcherEvent e) { install(); }
public void svgLoadEventDispatchCancelled
(SVGLoadEventDispatcherEvent e) { install(); }
public void svgLoadEventDispatchFailed
(SVGLoadEventDispatcherEvent e) { install(); }
public void install() {
// Remove ourselves from the old onload dispacher
// and install the new document.
sled.removeSVGLoadEventDispatcherListener(this);
EventQueue.invokeLater(r);
}
});
stopProcessing();
} else {
stopProcessing();
r.run();
}
}
/**
* This does the real work of installing the SVG Document after
* the update manager from the previous document (if any) has been
* properly 'shut down'.
*/
protected void installSVGDocument(SVGDocument doc) {
svgDocument = doc;
if (bridgeContext != null) {
bridgeContext.dispose();
bridgeContext = null;
}
releaseRenderingReferences();
if (doc == null) {
isDynamicDocument = false;
isInteractiveDocument = false;
disableInteractions = true;
initialTransform = new AffineTransform();
setRenderingTransform(initialTransform, false);
Dimension d = getSize();
repaint(0, 0, d.width, d.height);
return;
}
switch (documentState) {
case ALWAYS_STATIC:
isDynamicDocument = false;
isInteractiveDocument = false;
break;
case ALWAYS_INTERACTIVE:
isDynamicDocument = false;
isInteractiveDocument = true;
break;
case ALWAYS_DYNAMIC:
isDynamicDocument = true;
isInteractiveDocument = true;
break;
case AUTODETECT:
isDynamicDocument = BridgeContext.isDynamicDocument(doc);
if (isDynamicDocument)
isInteractiveDocument = true;
else
isInteractiveDocument =
BridgeContext.isInteractiveDocument(doc);
}
Element root = doc.getDocumentElement();
String znp = root.getAttributeNS
(null, SVGConstants.SVG_ZOOM_AND_PAN_ATTRIBUTE);
disableInteractions = !znp.equals(SVGConstants.SVG_MAGNIFY_VALUE);
bridgeContext = createBridgeContext();
nextGVTTreeBuilder = new GVTTreeBuilder(doc, bridgeContext);
nextGVTTreeBuilder.setPriority(Thread.MIN_PRIORITY);
Iterator it = gvtTreeBuilderListeners.iterator();
while (it.hasNext()) {
nextGVTTreeBuilder.addGVTTreeBuilderListener
((GVTTreeBuilderListener)it.next());
}
initializeEventHandling();
if (gvtTreeBuilder == null &&
documentLoader == null &&
gvtTreeRenderer == null &&
svgLoadEventDispatcher == null &&
updateManager == null) {
startGVTTreeBuilder();
}
}
/**
* Starts a tree builder.
*/
private void startGVTTreeBuilder() {
gvtTreeBuilder = nextGVTTreeBuilder;
nextGVTTreeBuilder = null;
gvtTreeBuilder.start();
}
/**
* Returns the current SVG document.
*/
public SVGDocument getSVGDocument() {
return svgDocument;
}
/**
* Returns the size of the SVG document.
*/
public Dimension2D getSVGDocumentSize() {
return bridgeContext.getDocumentSize();
}
/**
* Returns the current's document fragment identifier.
*/
public String getFragmentIdentifier() {
return fragmentIdentifier;
}
/**
* Sets the current fragment identifier.
*/
public void setFragmentIdentifier(String fi) {
fragmentIdentifier = fi;
if (computeRenderingTransform())
scheduleGVTRendering();
}
/**
* Removes all images from the image cache.
*/
public void flushImageCache() {
ImageTagRegistry reg = ImageTagRegistry.getRegistry();
reg.flushCache();
}
/**
* Creates a new bridge context.
*/
protected BridgeContext createBridgeContext() {
if (loader == null) {
loader = new DocumentLoader(userAgent);
}
BridgeContext result = new BridgeContext(userAgent, loader);
if (isInteractiveDocument) {
if (isDynamicDocument)
result.setDynamicState(BridgeContext.DYNAMIC);
else
result.setDynamicState(BridgeContext.INTERACTIVE);
}
return result;
}
/**
* Starts a SVGLoadEventDispatcher thread.
*/
protected void startSVGLoadEventDispatcher(GraphicsNode root) {
UpdateManager um = new UpdateManager(bridgeContext,
root,
svgDocument);
svgLoadEventDispatcher =
new SVGLoadEventDispatcher(root,
svgDocument,
bridgeContext,
um);
Iterator it = svgLoadEventDispatcherListeners.iterator();
while (it.hasNext()) {
svgLoadEventDispatcher.addSVGLoadEventDispatcherListener
((SVGLoadEventDispatcherListener)it.next());
}
svgLoadEventDispatcher.start();
}
/**
* Creates a new renderer.
*/
protected ImageRenderer createImageRenderer() {
if (isDynamicDocument) {
return rendererFactory.createDynamicImageRenderer();
} else {
return rendererFactory.createStaticImageRenderer();
}
}
public CanvasGraphicsNode getCanvasGraphicsNode() {
return getCanvasGraphicsNode(gvtRoot);
}
protected CanvasGraphicsNode getCanvasGraphicsNode(GraphicsNode gn) {
if (!(gn instanceof CompositeGraphicsNode))
return null;
CompositeGraphicsNode cgn = (CompositeGraphicsNode)gn;
gn = (GraphicsNode)cgn.getChildren().get(0);
if (!(gn instanceof CanvasGraphicsNode))
return null;
return (CanvasGraphicsNode)gn;
}
/**
* Returns the transform from viewBox coords to screen coords
*/
public AffineTransform getViewBoxTransform() {
AffineTransform at = getRenderingTransform();
if (at == null) at = new AffineTransform();
else at = new AffineTransform(at);
CanvasGraphicsNode cgn = getCanvasGraphicsNode();
if (cgn != null) {
AffineTransform vAT = cgn.getViewingTransform();
if (vAT != null)
at.concatenate(vAT);
}
return at;
}
/**
* Computes the transform used for rendering.
* Returns true if the component needs to be repainted.
*/
protected boolean computeRenderingTransform() {
if ((svgDocument == null) || (gvtRoot == null))
return false;
boolean ret = updateRenderingTransform();
initialTransform = new AffineTransform();
if (!initialTransform.equals(getRenderingTransform())) {
setRenderingTransform(initialTransform, false);
ret = true;
}
return ret;
}
/**
* Updates the value of the transform used for rendering.
* Return true if a repaint is required, otherwise false.
*/
protected boolean updateRenderingTransform() {
if ((svgDocument == null) || (gvtRoot == null))
return false;
try {
SVGSVGElement elt = svgDocument.getRootElement();
Dimension d = getSize();
Dimension oldD = prevComponentSize;
if (oldD == null) oldD = d;
prevComponentSize = d;
if (d.width < 1) d.width = 1;
if (d.height < 1) d.height = 1;
AffineTransform at = ViewBox.getViewTransform
(fragmentIdentifier, elt, d.width, d.height);
CanvasGraphicsNode cgn = getCanvasGraphicsNode();
AffineTransform vt = cgn.getViewingTransform();
if (at.equals(vt)) {
// No new transform
// Only repaint if size really changed.
return ((oldD.width != d.width) || (oldD.height != d.height));
}
// Here we map the old center of the component down to
// the user coodinate system with the old viewing
// transform and then back to the screen with the
// new viewing transform. We then adjust the rendering
// transform so it lands in the same place.
Point2D pt = new Point2D.Float(oldD.width/2.0f,
oldD.height/2.0f);
AffineTransform rendAT = getRenderingTransform();
if (rendAT != null) {
try {
AffineTransform invRendAT = rendAT.createInverse();
pt = invRendAT.transform(pt, null);
} catch (NoninvertibleTransformException e) { }
}
if (vt != null) {
try {
AffineTransform invVT = vt.createInverse();
pt = invVT.transform(pt, null);
} catch (NoninvertibleTransformException e) { }
}
if (at != null)
pt = at.transform(pt, null);
if (rendAT != null)
pt = rendAT.transform(pt, null);
// Now figure out how far we need to shift things
// to get the center point to line up again.
float dx = (float)((d.width/2.0f) -pt.getX());
float dy = (float)((d.height/2.0f)-pt.getY());
// Round the values to nearest integer.
dx = (int)((dx < 0)?(dx - .5):(dx + .5));
dy = (int)((dy < 0)?(dy - .5):(dy + .5));
if ((dx != 0) || (dy != 0)) {
rendAT.preConcatenate
(AffineTransform.getTranslateInstance(dx, dy));
setRenderingTransform(rendAT, false);
}
cgn.setViewingTransform(at);
} catch (BridgeException e) {
userAgent.displayError(e);
}
return true;
}
/**
* Renders the GVT tree.
*/
protected void renderGVTTree() {
if (!isInteractiveDocument ||
updateManager == null ||
!updateManager.isRunning()) {
super.renderGVTTree();
return;
}
final Dimension d = getSize();
if (gvtRoot == null || d.width <= 0 || d.height <= 0) {
return;
}
// Area of interest computation.
AffineTransform inv;
try {
inv = renderingTransform.createInverse();
} catch (NoninvertibleTransformException e) {
throw new InternalError(e.getMessage());
}
final Shape s =
inv.createTransformedShape(new Rectangle(0, 0, d.width, d.height));
updateManager.getUpdateRunnableQueue().invokeLater(new Runnable() {
public void run() {
paintingTransform = null;
updateManager.updateRendering(renderingTransform,
doubleBufferedRendering,
s, d.width, d.height);
}
});
}
/**
* Handles an exception.
*/
protected void handleException(Exception e) {
userAgent.displayError(e);
}
/**
* Adds a SVGDocumentLoaderListener to this component.
*/
public void addSVGDocumentLoaderListener(SVGDocumentLoaderListener l) {
svgDocumentLoaderListeners.add(l);
}
/**
* Removes a SVGDocumentLoaderListener from this component.
*/
public void removeSVGDocumentLoaderListener(SVGDocumentLoaderListener l) {
svgDocumentLoaderListeners.remove(l);
}
/**
* Adds a GVTTreeBuilderListener to this component.
*/
public void addGVTTreeBuilderListener(GVTTreeBuilderListener l) {
gvtTreeBuilderListeners.add(l);
}
/**
* Removes a GVTTreeBuilderListener from this component.
*/
public void removeGVTTreeBuilderListener(GVTTreeBuilderListener l) {
gvtTreeBuilderListeners.remove(l);
}
/**
* Adds a SVGLoadEventDispatcherListener to this component.
*/
public void addSVGLoadEventDispatcherListener
(SVGLoadEventDispatcherListener l) {
svgLoadEventDispatcherListeners.add(l);
}
/**
* Removes a SVGLoadEventDispatcherListener from this component.
*/
public void removeSVGLoadEventDispatcherListener
(SVGLoadEventDispatcherListener l) {
svgLoadEventDispatcherListeners.remove(l);
}
/**
* Adds a LinkActivationListener to this component.
*/
public void addLinkActivationListener(LinkActivationListener l) {
linkActivationListeners.add(l);
}
/**
* Removes a LinkActivationListener from this component.
*/
public void removeLinkActivationListener(LinkActivationListener l) {
linkActivationListeners.remove(l);
}
/**
* Adds a UpdateManagerListener to this component.
*/
public void addUpdateManagerListener(UpdateManagerListener l) {
updateManagerListeners.add(l);
}
/**
* Removes a UpdateManagerListener from this component.
*/
public void removeUpdateManagerListener(UpdateManagerListener l) {
updateManagerListeners.remove(l);
}
/**
* Shows an alert dialog box.
*/
public void showAlert(String message) {
JOptionPane.showMessageDialog
(this, Messages.formatMessage("script.alert",
new Object[] { message }));
}
/**
* Shows a prompt dialog box.
*/
public String showPrompt(String message) {
return JOptionPane.showInputDialog
(this, Messages.formatMessage("script.prompt",
new Object[] { message }));
}
/**
* Shows a prompt dialog box.
*/
public String showPrompt(String message, String defaultValue) {
return (String)JOptionPane.showInputDialog
(this,
Messages.formatMessage("script.prompt",
new Object[] { message }),
null,
JOptionPane.PLAIN_MESSAGE,
null, null, defaultValue);
}
/**
* Shows a confirm dialog box.
*/
public boolean showConfirm(String message) {
return JOptionPane.showConfirmDialog
(this, Messages.formatMessage("script.confirm",
new Object[] { message }),
"Confirm", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION;
}
/**
* This method is called when the component knows the desired
* size of the window (based on width/height of outermost SVG
* element).
* The default implementation simply calls setPreferredSize,
* and invalidate.
* However it is often useful to pack the window containing
* this component.
*/
public void setMySize(Dimension d) {
setPreferredSize(d);
invalidate();
}
/**
* The JGVTComponentListener.
*/
protected JSVGComponentListener jsvgComponentListener =
new JSVGComponentListener();
class JSVGComponentListener extends ComponentAdapter
implements JGVTComponentListener {
float prevScale = 0;
float prevTransX = 0;
float prevTransY = 0;
public void componentResized(ComponentEvent ce) {
if (isDynamicDocument &&
(updateManager != null) && updateManager.isRunning()) {
updateManager.getUpdateRunnableQueue().invokeLater
(new Runnable() {
public void run() {
try {
updateManager.dispatchSVGResizeEvent();
} catch (InterruptedException ie) {
}
}});
}
}
public void componentTransformChanged(ComponentEvent event) {
AffineTransform at = getRenderingTransform();
float currScale = (float)Math.sqrt(at.getDeterminant());
float currTransX = (float)at.getTranslateX();
float currTransY = (float)at.getTranslateY();
final boolean dispatchZoom = (currScale != prevScale);
final boolean dispatchScroll = ((currTransX != prevTransX) ||
(currTransX != prevTransX));
if (isDynamicDocument &&
(updateManager != null) && updateManager.isRunning()) {
updateManager.getUpdateRunnableQueue().invokeLater
(new Runnable() {
public void run() {
try {
if (dispatchZoom)
updateManager.dispatchSVGZoomEvent();
if (dispatchScroll)
updateManager.dispatchSVGScrollEvent();
} catch (InterruptedException ie) {
}
}});
}
prevScale = currScale;
prevTransX = currTransX;
prevTransY = currTransY;
}
public void updateMatrix(AffineTransform at) {
prevScale = (float)Math.sqrt(at.getDeterminant());
prevTransX = (float)at.getTranslateX();
prevTransY = (float)at.getTranslateY();
}
}
/**
* Creates an instance of Listener.
*/
protected Listener createListener() {
return new SVGListener();
}
/**
* To hide the listener methods.
*/
protected class SVGListener
extends Listener
implements SVGDocumentLoaderListener,
GVTTreeBuilderListener,
SVGLoadEventDispatcherListener,
UpdateManagerListener {
/**
* Creates a new SVGListener.
*/
protected SVGListener() {
}
// SVGDocumentLoaderListener /////////////////////////////////////////
/**
* Called when the loading of a document was started.
*/
public void documentLoadingStarted(SVGDocumentLoaderEvent e) {
}
/**
* Called when the loading of a document was completed.
*/
public void documentLoadingCompleted(SVGDocumentLoaderEvent e) {
if (nextDocumentLoader != null) {
startDocumentLoader();
return;
}
documentLoader = null;
setSVGDocument(e.getSVGDocument());
}
/**
* Called when the loading of a document was cancelled.
*/
public void documentLoadingCancelled(SVGDocumentLoaderEvent e) {
if (nextDocumentLoader != null) {
startDocumentLoader();
return;
}
documentLoader = null;
if (nextGVTTreeBuilder != null) {
startGVTTreeBuilder();
return;
}
}
/**
* Called when the loading of a document has failed.
*/
public void documentLoadingFailed(SVGDocumentLoaderEvent e) {
if (nextDocumentLoader != null) {
startDocumentLoader();
return;
}
documentLoader = null;
userAgent.displayError(((SVGDocumentLoader)e.getSource()).
getException());
if (nextGVTTreeBuilder != null) {
startGVTTreeBuilder();
return;
}
}
// GVTTreeBuilderListener ////////////////////////////////////////////
/**
* Called when a build started.
* The data of the event is initialized to the old document.
*/
public void gvtBuildStarted(GVTTreeBuilderEvent e) {
removeJGVTComponentListener(jsvgComponentListener);
removeComponentListener(jsvgComponentListener);
}
/**
* Called when a build was completed.
*/
public void gvtBuildCompleted(GVTTreeBuilderEvent e) {
if (nextGVTTreeBuilder != null) {
startGVTTreeBuilder();
return;
}
loader = null;
gvtTreeBuilder = null;
if (nextDocumentLoader != null) {
startDocumentLoader();
return;
}
Dimension2D dim = bridgeContext.getDocumentSize();
Dimension mySz = new Dimension((int)dim.getWidth(),
(int)dim.getHeight());
JSVGComponent.this.setMySize(mySz);
SVGSVGElement elt = svgDocument.getRootElement();
Dimension d = getSize();
prevComponentSize = d;
if (d.width < 1) d.width = 1;
if (d.height < 1) d.height = 1;
AffineTransform at = ViewBox.getViewTransform
(fragmentIdentifier, elt, d.width, d.height);
CanvasGraphicsNode cgn = getCanvasGraphicsNode(e.getGVTRoot());
cgn.setViewingTransform(at);
initialTransform = new AffineTransform();
setRenderingTransform(initialTransform, false);
jsvgComponentListener.updateMatrix(initialTransform);
addJGVTComponentListener(jsvgComponentListener);
addComponentListener(jsvgComponentListener);
gvtRoot = null;
if (isDynamicDocument && JSVGComponent.this.eventsEnabled) {
startSVGLoadEventDispatcher(e.getGVTRoot());
} else {
if (isInteractiveDocument) {
nextUpdateManager = new UpdateManager(bridgeContext,
e.getGVTRoot(),
svgDocument);
}
JSVGComponent.this.setGraphicsNode(e.getGVTRoot(), false);
scheduleGVTRendering();
}
}
/**
* Called when a build was cancelled.
*/
public void gvtBuildCancelled(GVTTreeBuilderEvent e) {
if (nextGVTTreeBuilder != null) {
startGVTTreeBuilder();
return;
}
loader = null;
gvtTreeBuilder = null;
if (nextDocumentLoader != null) {
startDocumentLoader();
return;
}
JSVGComponent.this.image = null;
repaint();
}
/**
* Called when a build failed.
*/
public void gvtBuildFailed(GVTTreeBuilderEvent e) {
if (nextGVTTreeBuilder != null) {
startGVTTreeBuilder();
return;
}
loader = null;
gvtTreeBuilder = null;
if (nextDocumentLoader != null) {
startDocumentLoader();
return;
}
GraphicsNode gn = e.getGVTRoot();
Dimension2D dim = bridgeContext.getDocumentSize();
if (gn == null || dim == null) {
JSVGComponent.this.image = null;
repaint();
} else {
Dimension d= new Dimension((int)dim.getWidth(),
(int)dim.getHeight());
JSVGComponent.this.setMySize(d);
JSVGComponent.this.setGraphicsNode(gn, false);
computeRenderingTransform();
}
userAgent.displayError(((GVTTreeBuilder)e.getSource())
.getException());
}
// SVGLoadEventDispatcherListener ////////////////////////////////////
/**
* Called when a onload event dispatch started.
*/
public void svgLoadEventDispatchStarted
(SVGLoadEventDispatcherEvent e) {
}
/**
* Called when a onload event dispatch was completed.
*/
public void svgLoadEventDispatchCompleted
(SVGLoadEventDispatcherEvent e) {
nextUpdateManager = svgLoadEventDispatcher.getUpdateManager();
svgLoadEventDispatcher = null;
if (nextGVTTreeBuilder != null) {
nextUpdateManager.interrupt();
nextUpdateManager = null;
startGVTTreeBuilder();
return;
}
if (nextDocumentLoader != null) {
nextUpdateManager.interrupt();
nextUpdateManager = null;
startDocumentLoader();
return;
}
JSVGComponent.this.setGraphicsNode(e.getGVTRoot(), false);
scheduleGVTRendering();
}
/**
* Called when a onload event dispatch was cancelled.
*/
public void svgLoadEventDispatchCancelled
(SVGLoadEventDispatcherEvent e) {
nextUpdateManager = svgLoadEventDispatcher.getUpdateManager();
svgLoadEventDispatcher = null;
nextUpdateManager.interrupt();
nextUpdateManager = null;
if (nextGVTTreeBuilder != null) {
startGVTTreeBuilder();
return;
}
if (nextDocumentLoader != null) {
startDocumentLoader();
return;
}
}
/**
* Called when a onload event dispatch failed.
*/
public void svgLoadEventDispatchFailed
(SVGLoadEventDispatcherEvent e) {
nextUpdateManager = svgLoadEventDispatcher.getUpdateManager();
svgLoadEventDispatcher = null;
nextUpdateManager.interrupt();
nextUpdateManager = null;
if (nextGVTTreeBuilder != null) {
startGVTTreeBuilder();
return;
}
if (nextDocumentLoader != null) {
startDocumentLoader();
return;
}
GraphicsNode gn = e.getGVTRoot();
Dimension2D dim = bridgeContext.getDocumentSize();
if (gn == null || dim == null) {
JSVGComponent.this.image = null;
repaint();
} else {
JSVGComponent.this.setGraphicsNode(gn, false);
computeRenderingTransform();
}
userAgent.displayError(((SVGLoadEventDispatcher)e.getSource())
.getException());
}
// GVTTreeRendererListener ///////////////////////////////////////////
/**
* Called when a rendering was completed.
*/
public void gvtRenderingCompleted(GVTTreeRendererEvent e) {
super.gvtRenderingCompleted(e);
if (nextGVTTreeBuilder != null) {
if (nextUpdateManager != null) {
nextUpdateManager.interrupt();
nextUpdateManager = null;
}
startGVTTreeBuilder();
return;
}
if (nextDocumentLoader != null) {
if (nextUpdateManager != null) {
nextUpdateManager.interrupt();
nextUpdateManager = null;
}
startDocumentLoader();
return;
}
if (nextUpdateManager != null) {
updateManager = nextUpdateManager;
nextUpdateManager = null;
updateManager.addUpdateManagerListener((SVGListener)this);
updateManager.manageUpdates(renderer);
}
}
/**
* Called when a rendering was cancelled.
*/
public void gvtRenderingCancelled(GVTTreeRendererEvent e) {
super.gvtRenderingCancelled(e);
if (nextGVTTreeBuilder != null) {
if (nextUpdateManager != null) {
nextUpdateManager.interrupt();
nextUpdateManager = null;
}
startGVTTreeBuilder();
return;
}
if (nextDocumentLoader != null) {
if (nextUpdateManager != null) {
nextUpdateManager.interrupt();
nextUpdateManager = null;
}
startDocumentLoader();
return;
}
}
/**
* Called when a rendering failed.
*/
public void gvtRenderingFailed(GVTTreeRendererEvent e) {
super.gvtRenderingFailed(e);
if (nextGVTTreeBuilder != null) {
if (nextUpdateManager != null) {
nextUpdateManager.interrupt();
nextUpdateManager = null;
}
startGVTTreeBuilder();
return;
}
if (nextDocumentLoader != null) {
if (nextUpdateManager != null) {
nextUpdateManager.interrupt();
nextUpdateManager = null;
}
startDocumentLoader();
return;
}
}
// UpdateManagerListener //////////////////////////////////////////
/**
* Called when the manager was started.
*/
public void managerStarted(final UpdateManagerEvent e) {
EventQueue.invokeLater(new Runnable() {
public void run() {
suspendInteractions = false;
Object[] dll = updateManagerListeners.toArray();
if (dll.length > 0) {
for (int i = 0; i < dll.length; i++) {
((UpdateManagerListener)dll[i]).
managerStarted(e);
}
}
}
});
}
/**
* Called when the manager was suspended.
*/
public void managerSuspended(final UpdateManagerEvent e) {
EventQueue.invokeLater(new Runnable() {
public void run() {
Object[] dll = updateManagerListeners.toArray();
if (dll.length > 0) {
for (int i = 0; i < dll.length; i++) {
((UpdateManagerListener)dll[i]).
managerSuspended(e);
}
}
}
});
}
/**
* Called when the manager was resumed.
*/
public void managerResumed(final UpdateManagerEvent e) {
EventQueue.invokeLater(new Runnable() {
public void run() {
Object[] dll = updateManagerListeners.toArray();
if (dll.length > 0) {
for (int i = 0; i < dll.length; i++) {
((UpdateManagerListener)dll[i]).
managerResumed(e);
}
}
}
});
}
/**
* Called when the manager was stopped.
*/
public void managerStopped(final UpdateManagerEvent e) {
EventQueue.invokeLater(new Runnable() {
public void run() {
updateManager = null;
Object[] dll = updateManagerListeners.toArray();
if (dll.length > 0) {
for (int i = 0; i < dll.length; i++) {
((UpdateManagerListener)dll[i]).
managerStopped(e);
}
}
if (nextGVTTreeBuilder != null) {
startGVTTreeBuilder();
return;
}
if (nextDocumentLoader != null) {
startDocumentLoader();
return;
}
}
});
}
/**
* Called when an update started.
*/
public void updateStarted(final UpdateManagerEvent e) {
EventQueue.invokeLater(new Runnable() {
public void run() {
if (!doubleBufferedRendering) {
image = e.getImage();
}
Object[] dll = updateManagerListeners.toArray();
if (dll.length > 0) {
for (int i = 0; i < dll.length; i++) {
((UpdateManagerListener)dll[i]).
updateStarted(e);
}
}
}
});
}
/**
* Called when an update was completed.
*/
public void updateCompleted(final UpdateManagerEvent e) {
// IMPORTANT:
// ==========
//
// The following call is 'invokeAndWait' and not
// 'invokeLater' because it is essential that the
// UpdateManager thread (which invokes this
// 'updateCompleted' method, blocks until the repaint
// has completed. Otherwise, there is a possibility
// that internal buffers would get updated in the
// middle of a swing repaint.
//
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
image = e.getImage();
List l = e.getDirtyAreas();
if (l != null) {
Dimension dim = getSize();
List ml = mergeRectangles(l, 0, 0,
dim.width - 1,
dim.height - 1);
if (ml.size() < l.size()) {
l = ml;
}
Iterator i = l.iterator();
while (i.hasNext()) {
Rectangle r = (Rectangle)i.next();
if (doubleBufferedRendering)
repaint(r);
else
paintImmediately(r);
}
}
suspendInteractions = false;
}
});
} catch (Exception ex) {
}
EventQueue.invokeLater(new Runnable() {
public void run() {
Object[] dll = updateManagerListeners.toArray();
if (dll.length > 0) {
for (int i = 0; i < dll.length; i++) {
((UpdateManagerListener)dll[i]).
updateCompleted(e);
}
}
}
});
}
/**
* Called when an update failed.
*/
public void updateFailed(final UpdateManagerEvent e) {
EventQueue.invokeLater(new Runnable() {
public void run() {
Object[] dll = updateManagerListeners.toArray();
if (dll.length > 0) {
for (int i = 0; i < dll.length; i++) {
((UpdateManagerListener)dll[i]).
updateFailed(e);
}
}
}
});
}
// Event propagation to GVT ///////////////////////////////////////
/**
* Dispatches the event to the GVT tree.
*/
protected void dispatchKeyTyped(final KeyEvent e) {
if (!isDynamicDocument) {
super.dispatchKeyTyped(e);
return;
}
if (updateManager != null && updateManager.isRunning()) {
updateManager.getUpdateRunnableQueue().invokeLater
(new Runnable() {
public void run() {
eventDispatcher.keyTyped(e);
}
});
}
}
/**
* Dispatches the event to the GVT tree.
*/
protected void dispatchKeyPressed(final KeyEvent e) {
if (!isDynamicDocument) {
super.dispatchKeyPressed(e);
return;
}
if (updateManager != null && updateManager.isRunning()) {
updateManager.getUpdateRunnableQueue().invokeLater
(new Runnable() {
public void run() {
eventDispatcher.keyPressed(e);
}
});
}
}
/**
* Dispatches the event to the GVT tree.
*/
protected void dispatchKeyReleased(final KeyEvent e) {
if (!isDynamicDocument) {
super.dispatchKeyReleased(e);
return;
}
if (updateManager != null && updateManager.isRunning()) {
updateManager.getUpdateRunnableQueue().invokeLater
(new Runnable() {
public void run() {
eventDispatcher.keyReleased(e);
}
});
}
}
/**
* Dispatches the event to the GVT tree.
*/
protected void dispatchMouseClicked(final MouseEvent e) {
if (!isInteractiveDocument) {
super.dispatchMouseClicked(e);
return;
}
if (updateManager != null && updateManager.isRunning()) {
updateManager.getUpdateRunnableQueue().invokeLater
(new Runnable() {
public void run() {
eventDispatcher.mouseClicked(e);
}
});
}
}
/**
* Dispatches the event to the GVT tree.
*/
protected void dispatchMousePressed(final MouseEvent e) {
if (!isDynamicDocument) {
super.dispatchMousePressed(e);
return;
}
if (updateManager != null && updateManager.isRunning()) {
updateManager.getUpdateRunnableQueue().invokeLater
(new Runnable() {
public void run() {
eventDispatcher.mousePressed(e);
}
});
}
}
/**
* Dispatches the event to the GVT tree.
*/
protected void dispatchMouseReleased(final MouseEvent e) {
if (!isDynamicDocument) {
super.dispatchMouseReleased(e);
return;
}
if (updateManager != null && updateManager.isRunning()) {
updateManager.getUpdateRunnableQueue().invokeLater
(new Runnable() {
public void run() {
eventDispatcher.mouseReleased(e);
}
});
}
}
/**
* Dispatches the event to the GVT tree.
*/
protected void dispatchMouseEntered(final MouseEvent e) {
if (!isInteractiveDocument) {
super.dispatchMouseEntered(e);
return;
}
if (updateManager != null && updateManager.isRunning()) {
updateManager.getUpdateRunnableQueue().invokeLater
(new Runnable() {
public void run() {
eventDispatcher.mouseEntered(e);
}
});
}
}
/**
* Dispatches the event to the GVT tree.
*/
protected void dispatchMouseExited(final MouseEvent e) {
if (!isInteractiveDocument) {
super.dispatchMouseExited(e);
return;
}
if (updateManager != null && updateManager.isRunning()) {
updateManager.getUpdateRunnableQueue().invokeLater
(new Runnable() {
public void run() {
eventDispatcher.mouseExited(e);
}
});
}
}
/**
* Dispatches the event to the GVT tree.
*/
protected void dispatchMouseDragged(MouseEvent e) {
if (!isDynamicDocument) {
super.dispatchMouseDragged(e);
return;
}
class MouseDraggedRunnable implements Runnable {
MouseEvent event;
MouseDraggedRunnable(MouseEvent evt) {
event = evt;
}
public void run() {
eventDispatcher.mouseDragged(event);
}
}
if (updateManager != null && updateManager.isRunning()) {
RunnableQueue rq = updateManager.getUpdateRunnableQueue();
// Events compression.
synchronized (rq.getIteratorLock()) {
Iterator it = rq.iterator();
while (it.hasNext()) {
Object next = it.next();
if (next instanceof MouseDraggedRunnable) {
MouseDraggedRunnable mdr;
mdr = (MouseDraggedRunnable)next;
MouseEvent mev = mdr.event;
if (mev.getModifiers() == e.getModifiers()) {
mdr.event = e;
}
return;
}
}
}
rq.invokeLater(new MouseDraggedRunnable(e));
}
}
/**
* Dispatches the event to the GVT tree.
*/
protected void dispatchMouseMoved(MouseEvent e) {
if (!isInteractiveDocument) {
super.dispatchMouseMoved(e);
return;
}
class MouseMovedRunnable implements Runnable {
MouseEvent event;
MouseMovedRunnable(MouseEvent evt) {
event = evt;
}
public void run() {
eventDispatcher.mouseMoved(event);
}
}
if (updateManager != null && updateManager.isRunning()) {
RunnableQueue rq = updateManager.getUpdateRunnableQueue();
// Events compression.
int i = 0;
synchronized (rq.getIteratorLock()) {
Iterator it = rq.iterator();
while (it.hasNext()) {
Object next = it.next();
if (next instanceof MouseMovedRunnable) {
MouseMovedRunnable mmr;
mmr = (MouseMovedRunnable)next;
MouseEvent mev = mmr.event;
if (mev.getModifiers() == e.getModifiers()) {
mmr.event = e;
}
return;
}
i++;
}
}
rq.invokeLater(new MouseMovedRunnable(e));
}
}
}
/**
* Creates a UserAgent.
*/
protected UserAgent createUserAgent() {
return new BridgeUserAgent();
}
/**
* The user-agent wrapper, which call the methods in the event thread.
*/
protected static class BridgeUserAgentWrapper implements UserAgent {
/**
* The wrapped user agent.
*/
protected UserAgent userAgent;
/**
* Creates a new BridgeUserAgentWrapper.
*/
public BridgeUserAgentWrapper(UserAgent ua) {
userAgent = ua;
}
/**
* Returns the event dispatcher to use.
*/
public EventDispatcher getEventDispatcher() {
if (EventQueue.isDispatchThread()) {
return userAgent.getEventDispatcher();
} else {
class Query implements Runnable {
EventDispatcher result;
public void run() {
result = userAgent.getEventDispatcher();
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Returns the default size of the viewport.
*/
public Dimension2D getViewportSize() {
if (EventQueue.isDispatchThread()) {
return userAgent.getViewportSize();
} else {
class Query implements Runnable {
Dimension2D result;
public void run() {
result = userAgent.getViewportSize();
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Displays an error resulting from the specified Exception.
*/
public void displayError(final Exception ex) {
if (EventQueue.isDispatchThread()) {
userAgent.displayError(ex);
} else {
EventQueue.invokeLater(new Runnable() {
public void run() {
userAgent.displayError(ex);
}
});
}
}
/**
* Displays a message in the User Agent interface.
*/
public void displayMessage(final String message) {
if (EventQueue.isDispatchThread()) {
userAgent.displayMessage(message);
} else {
EventQueue.invokeLater(new Runnable() {
public void run() {
userAgent.displayMessage(message);
}
});
}
}
/**
* Shows an alert dialog box.
*/
public void showAlert(final String message) {
if (EventQueue.isDispatchThread()) {
userAgent.showAlert(message);
} else {
invokeAndWait(new Runnable() {
public void run() {
userAgent.showAlert(message);
}
});
}
}
/**
* Shows a prompt dialog box.
*/
public String showPrompt(final String message) {
if (EventQueue.isDispatchThread()) {
return userAgent.showPrompt(message);
} else {
class Query implements Runnable {
String result;
public void run() {
result = userAgent.showPrompt(message);
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Shows a prompt dialog box.
*/
public String showPrompt(final String message,
final String defaultValue) {
if (EventQueue.isDispatchThread()) {
return userAgent.showPrompt(message, defaultValue);
} else {
class Query implements Runnable {
String result;
public void run() {
result = userAgent.showPrompt(message, defaultValue);
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Shows a confirm dialog box.
*/
public boolean showConfirm(final String message) {
if (EventQueue.isDispatchThread()) {
return userAgent.showConfirm(message);
} else {
class Query implements Runnable {
boolean result;
public void run() {
result = userAgent.showConfirm(message);
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Returns the size of a px CSS unit in millimeters.
*/
public float getPixelUnitToMillimeter() {
if (EventQueue.isDispatchThread()) {
return userAgent.getPixelUnitToMillimeter();
} else {
class Query implements Runnable {
float result;
public void run() {
result = userAgent.getPixelUnitToMillimeter();
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Returns the size of a px CSS unit in millimeters.
* This will be removed after next release.
* @see #getPixelUnitToMillimeter()
*/
public float getPixelToMM() { return getPixelUnitToMillimeter(); }
/**
* Returns the default font family.
*/
public String getDefaultFontFamily() {
if (EventQueue.isDispatchThread()) {
return userAgent.getDefaultFontFamily();
} else {
class Query implements Runnable {
String result;
public void run() {
result = userAgent.getDefaultFontFamily();
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
public float getMediumFontSize() {
if (EventQueue.isDispatchThread()) {
return userAgent.getMediumFontSize();
} else {
class Query implements Runnable {
float result;
public void run() {
result = userAgent.getMediumFontSize();
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
public float getLighterFontWeight(float f) {
if (EventQueue.isDispatchThread()) {
return userAgent.getLighterFontWeight(f);
} else {
final float ff = f;
class Query implements Runnable {
float result;
public void run() {
result = userAgent.getLighterFontWeight(ff);
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
public float getBolderFontWeight(float f) {
if (EventQueue.isDispatchThread()) {
return userAgent.getBolderFontWeight(f);
} else {
final float ff = f;
class Query implements Runnable {
float result;
public void run() {
result = userAgent.getBolderFontWeight(ff);
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Returns the language settings.
*/
public String getLanguages() {
if (EventQueue.isDispatchThread()) {
return userAgent.getLanguages();
} else {
class Query implements Runnable {
String result;
public void run() {
result = userAgent.getLanguages();
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Returns the user stylesheet uri.
* @return null if no user style sheet was specified.
*/
public String getUserStyleSheetURI() {
if (EventQueue.isDispatchThread()) {
return userAgent.getUserStyleSheetURI();
} else {
class Query implements Runnable {
String result;
public void run() {
result = userAgent.getUserStyleSheetURI();
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Opens a link.
* @param elt The activated link element.
*/
public void openLink(final SVGAElement elt) {
if (EventQueue.isDispatchThread()) {
userAgent.openLink(elt);
} else {
EventQueue.invokeLater(new Runnable() {
public void run() {
userAgent.openLink(elt);
}
});
}
}
/**
* Informs the user agent to change the cursor.
* @param cursor the new cursor
*/
public void setSVGCursor(final Cursor cursor) {
if (EventQueue.isDispatchThread()) {
userAgent.setSVGCursor(cursor);
} else {
EventQueue.invokeLater(new Runnable() {
public void run() {
userAgent.setSVGCursor(cursor);
}
});
}
}
/**
* Returns the class name of the XML parser.
*/
public String getXMLParserClassName() {
if (EventQueue.isDispatchThread()) {
return userAgent.getXMLParserClassName();
} else {
class Query implements Runnable {
String result;
public void run() {
result = userAgent.getXMLParserClassName();
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Returns true if the XML parser must be in validation mode, false
* otherwise.
*/
public boolean isXMLParserValidating() {
if (EventQueue.isDispatchThread()) {
return userAgent.isXMLParserValidating();
} else {
class Query implements Runnable {
boolean result;
public void run() {
result = userAgent.isXMLParserValidating();
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Returns the <code>AffineTransform</code> currently
* applied to the drawing by the UserAgent.
*/
public AffineTransform getTransform() {
if (EventQueue.isDispatchThread()) {
return userAgent.getTransform();
} else {
class Query implements Runnable {
AffineTransform result;
public void run() {
result = userAgent.getTransform();
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Sets the <code>AffineTransform</code> to be
* applied to the drawing by the UserAgent.
*/
public void setTransform(AffineTransform at) {
if (EventQueue.isDispatchThread()) {
userAgent.setTransform(at);
} else {
final AffineTransform affine = at;
class Query implements Runnable {
public void run() {
userAgent.setTransform(affine);
}
}
Query q = new Query();
invokeAndWait(q);
}
}
/**
* Returns this user agent's CSS media.
*/
public String getMedia() {
if (EventQueue.isDispatchThread()) {
return userAgent.getMedia();
} else {
class Query implements Runnable {
String result;
public void run() {
result = userAgent.getMedia();
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Returns this user agent's alternate style-sheet title.
*/
public String getAlternateStyleSheet() {
if (EventQueue.isDispatchThread()) {
return userAgent.getAlternateStyleSheet();
} else {
class Query implements Runnable {
String result;
public void run() {
result = userAgent.getAlternateStyleSheet();
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Returns the location on the screen of the
* client area in the UserAgent.
*/
public Point getClientAreaLocationOnScreen() {
if (EventQueue.isDispatchThread()) {
return userAgent.getClientAreaLocationOnScreen();
} else {
class Query implements Runnable {
Point result;
public void run() {
result = userAgent.getClientAreaLocationOnScreen();
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Tells whether the given feature is supported by this
* user agent.
*/
public boolean hasFeature(final String s) {
if (EventQueue.isDispatchThread()) {
return userAgent.hasFeature(s);
} else {
class Query implements Runnable {
boolean result;
public void run() {
result = userAgent.hasFeature(s);
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Tells whether the given extension is supported by this
* user agent.
*/
public boolean supportExtension(final String s) {
if (EventQueue.isDispatchThread()) {
return userAgent.supportExtension(s);
} else {
class Query implements Runnable {
boolean result;
public void run() {
result = userAgent.supportExtension(s);
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* Lets the bridge tell the user agent that the following
* extension is supported by the bridge.
*/
public void registerExtension(final BridgeExtension ext) {
if (EventQueue.isDispatchThread()) {
userAgent.registerExtension(ext);
} else {
EventQueue.invokeLater(new Runnable() {
public void run() {
userAgent.registerExtension(ext);
}
});
}
}
/**
* Notifies the UserAgent that the input element
* has been found in the document. This is sometimes
* called, for example, to handle <a> or
* <title> elements in a UserAgent-dependant
* way.
*/
public void handleElement(final Element elt, final Object data) {
if (EventQueue.isDispatchThread()) {
userAgent.handleElement(elt, data);
} else {
EventQueue.invokeLater(new Runnable() {
public void run() {
userAgent.handleElement(elt, data);
}
});
}
}
/**
* Returns the security settings for the given script
* type, script url and document url
*
* @param scriptType type of script, as found in the
* type attribute of the <script> element.
* @param scriptURL url for the script, as defined in
* the script's xlink:href attribute. If that
* attribute was empty, then this parameter should
* be null
* @param docURL url for the document into which the
* script was found.
*/
public ScriptSecurity getScriptSecurity(String scriptType,
ParsedURL scriptPURL,
ParsedURL docPURL){
if (EventQueue.isDispatchThread()) {
return userAgent.getScriptSecurity(scriptType,
scriptPURL,
docPURL);
} else {
final String st = scriptType;
final ParsedURL sPURL= scriptPURL;
final ParsedURL dPURL= docPURL;
class Query implements Runnable {
ScriptSecurity result;
public void run() {
result = userAgent.getScriptSecurity(st, sPURL, dPURL);
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* This method throws a SecurityException if the script
* of given type, found at url and referenced from docURL
* should not be loaded.
*
* This is a convenience method to call checkLoadScript
* on the ScriptSecurity strategy returned by
* getScriptSecurity.
*
* @param scriptType type of script, as found in the
* type attribute of the <script> element.
* @param scriptURL url for the script, as defined in
* the script's xlink:href attribute. If that
* attribute was empty, then this parameter should
* be null
* @param docURL url for the document into which the
* script was found.
*/
public void checkLoadScript(String scriptType,
ParsedURL scriptPURL,
ParsedURL docPURL) throws SecurityException {
if (EventQueue.isDispatchThread()) {
userAgent.checkLoadScript(scriptType,
scriptPURL,
docPURL);
} else {
final String st = scriptType;
final ParsedURL sPURL= scriptPURL;
final ParsedURL dPURL= docPURL;
class Query implements Runnable {
SecurityException se = null;
public void run() {
try {
userAgent.checkLoadScript(st, sPURL, dPURL);
} catch (SecurityException se) {
this.se = se;
}
}
}
Query q = new Query();
invokeAndWait(q);
if (q.se != null) {
q.se.fillInStackTrace();
throw q.se;
}
}
}
/**
* Returns the security settings for the given resource
* url and document url
*
* @param resourceURL url for the resource, as defined in
* the resource's xlink:href attribute. If that
* attribute was empty, then this parameter should
* be null
* @param docURL url for the document into which the
* resource was found.
*/
public ExternalResourceSecurity
getExternalResourceSecurity(ParsedURL resourcePURL,
ParsedURL docPURL){
if (EventQueue.isDispatchThread()) {
return userAgent.getExternalResourceSecurity(resourcePURL,
docPURL);
} else {
final ParsedURL rPURL= resourcePURL;
final ParsedURL dPURL= docPURL;
class Query implements Runnable {
ExternalResourceSecurity result;
public void run() {
result = userAgent.getExternalResourceSecurity(rPURL, dPURL);
}
}
Query q = new Query();
invokeAndWait(q);
return q.result;
}
}
/**
* This method throws a SecurityException if the resource
* found at url and referenced from docURL
* should not be loaded.
*
* This is a convenience method to call checkLoadExternalResource
* on the ExternalResourceSecurity strategy returned by
* getExternalResourceSecurity.
*
* @param scriptURL url for the script, as defined in
* the script's xlink:href attribute. If that
* attribute was empty, then this parameter should
* be null
* @param docURL url for the document into which the
* script was found.
*/
public void
checkLoadExternalResource(ParsedURL resourceURL,
ParsedURL docURL) throws SecurityException {
if (EventQueue.isDispatchThread()) {
userAgent.checkLoadExternalResource(resourceURL,
docURL);
} else {
final ParsedURL rPURL= resourceURL;
final ParsedURL dPURL= docURL;
class Query implements Runnable {
SecurityException se;
public void run() {
try {
userAgent.checkLoadExternalResource(rPURL, dPURL);
} catch (SecurityException se) {
this.se = se;
}
}
}
Query q = new Query();
invokeAndWait(q);
if (q.se != null) {
q.se.fillInStackTrace();
throw q.se;
}
}
}
/**
* Invokes the given runnable from the event thread, and wait
* for the run method to terminate.
*/
protected void invokeAndWait(Runnable r) {
try {
EventQueue.invokeAndWait(r);
} catch (Exception e) {
}
}
}
/**
* To hide the user-agent methods.
*/
protected class BridgeUserAgent implements UserAgent {
/**
* Creates a new user agent.
*/
protected BridgeUserAgent() {
}
/**
* Returns the default size of the viewport of this user agent (0, 0).
*/
public Dimension2D getViewportSize() {
return getSize();
}
/**
* Returns the <code>EventDispatcher</code> used by the
* <code>UserAgent</code> to dispatch events on GVT.
*/
public EventDispatcher getEventDispatcher() {
return JSVGComponent.this.eventDispatcher;
}
/**
* Displays an error message in the User Agent interface.
*/
public void displayError(String message) {
if (svgUserAgent != null) {
svgUserAgent.displayError(message);
}
}
/**
* Displays an error resulting from the specified Exception.
*/
public void displayError(Exception ex) {
if (svgUserAgent != null) {
svgUserAgent.displayError(ex);
}
}
/**
* Displays a message in the User Agent interface.
*/
public void displayMessage(String message) {
if (svgUserAgent != null) {
svgUserAgent.displayMessage(message);
}
}
/**
* Shows an alert dialog box.
*/
public void showAlert(String message) {
if (svgUserAgent != null) {
svgUserAgent.showAlert(message);
return;
}
JSVGComponent.this.showAlert(message);
}
/**
* Shows a prompt dialog box.
*/
public String showPrompt(String message) {
if (svgUserAgent != null) {
return svgUserAgent.showPrompt(message);
}
return JSVGComponent.this.showPrompt(message);
}
/**
* Shows a prompt dialog box.
*/
public String showPrompt(String message, String defaultValue) {
if (svgUserAgent != null) {
return svgUserAgent.showPrompt(message, defaultValue);
}
return JSVGComponent.this.showPrompt(message, defaultValue);
}
/**
* Shows a confirm dialog box.
*/
public boolean showConfirm(String message) {
if (svgUserAgent != null) {
return svgUserAgent.showConfirm(message);
}
return JSVGComponent.this.showConfirm(message);
}
/**
* Returns the size of a px CSS unit in millimeters.
*/
public float getPixelUnitToMillimeter() {
if (svgUserAgent != null) {
return svgUserAgent.getPixelUnitToMillimeter();
}
return 0.264583333333333333333f; // 96 dpi
}
/**
* Returns the size of a px CSS unit in millimeters.
* This will be removed after next release.
* @see #getPixelUnitToMillimeter()
*/
public float getPixelToMM() { return getPixelUnitToMillimeter(); }
/**
* Returns the default font family.
*/
public String getDefaultFontFamily() {
if (svgUserAgent != null) {
return svgUserAgent.getDefaultFontFamily();
}
return "Arial, Helvetica, sans-serif";
}
/**
* Returns the medium font size.
*/
public float getMediumFontSize() {
if (svgUserAgent != null) {
return svgUserAgent.getMediumFontSize();
}
// 9pt (72pt = 1in)
return 9f * 25.4f / (72f * getPixelUnitToMillimeter());
}
/**
* Returns a lighter font-weight.
*/
public float getLighterFontWeight(float f) {
if (svgUserAgent != null) {
return svgUserAgent.getLighterFontWeight(f);
}
// Round f to nearest 100...
int weight = ((int)((f+50)/100))*100;
switch (weight) {
case 100: return 100;
case 200: return 100;
case 300: return 200;
case 400: return 300;
case 500: return 400;
case 600: return 400;
case 700: return 400;
case 800: return 400;
case 900: return 400;
default:
throw new IllegalArgumentException("Bad Font Weight: " + f);
}
}
/**
* Returns a bolder font-weight.
*/
public float getBolderFontWeight(float f) {
if (svgUserAgent != null) {
return svgUserAgent.getBolderFontWeight(f);
}
// Round f to nearest 100...
int weight = ((int)((f+50)/100))*100;
switch (weight) {
case 100: return 600;
case 200: return 600;
case 300: return 600;
case 400: return 600;
case 500: return 600;
case 600: return 700;
case 700: return 800;
case 800: return 900;
case 900: return 900;
default:
throw new IllegalArgumentException("Bad Font Weight: " + f);
}
}
/**
* Returns the language settings.
*/
public String getLanguages() {
if (svgUserAgent != null) {
return svgUserAgent.getLanguages();
}
return "en";
}
/**
* Returns the user stylesheet uri.
* @return null if no user style sheet was specified.
*/
public String getUserStyleSheetURI() {
if (svgUserAgent != null) {
return svgUserAgent.getUserStyleSheetURI();
}
return null;
}
/**
* Opens a link.
* @param elt The activated link element.
*/
public void openLink(SVGAElement elt) {
String show = XLinkSupport.getXLinkShow(elt);
String href = XLinkSupport.getXLinkHref(elt);
if (show.equals("new")) {
fireLinkActivatedEvent(elt, href);
if (svgUserAgent != null) {
String oldURI = svgDocument.getURL();
ParsedURL newURI = null;
// if the anchor element is in an external resource
if (elt.getOwnerDocument() != svgDocument) {
SVGDocument doc = (SVGDocument)elt.getOwnerDocument();
href = new ParsedURL(doc.getURL(), href).toString();
}
newURI = new ParsedURL(oldURI, href);
href = newURI.toString();
svgUserAgent.openLink(href, true);
} else {
JSVGComponent.this.loadSVGDocument(href);
}
return;
}
// Always use anchor element's document for base URI,
// for when it comes from an external resource.
ParsedURL newURI = new ParsedURL
(((SVGDocument)elt.getOwnerDocument()).getURL(), href);
// replace href with a fully resolved URI.
href = newURI.toString();
// Avoid reloading if possible.
if (svgDocument != null) {
ParsedURL oldURI = new ParsedURL(svgDocument.getURL());
// Check if they reference the same file.
if (newURI.sameFile(oldURI)) {
// They do, see if it's a new Fragment Ident.
String s = newURI.getRef();
if ((fragmentIdentifier != s) &&
((s == null) || (!s.equals(fragmentIdentifier)))) {
// It is, so update rendering transform.
fragmentIdentifier = s;
if (computeRenderingTransform())
scheduleGVTRendering();
}
// Let every one know the link fired (but don't
// load doc, it's already loaded.).
fireLinkActivatedEvent(elt, href);
return;
}
}
fireLinkActivatedEvent(elt, href);
if (svgUserAgent != null) {
svgUserAgent.openLink(href, false);
} else {
JSVGComponent.this.loadSVGDocument(href);
}
}
/**
* Fires a LinkActivatedEvent.
*/
protected void fireLinkActivatedEvent(SVGAElement elt, String href) {
Object[] ll = linkActivationListeners.toArray();
if (ll.length > 0) {
LinkActivationEvent ev;
ev = new LinkActivationEvent(JSVGComponent.this, elt, href);
for (int i = 0; i < ll.length; i++) {
LinkActivationListener l = (LinkActivationListener)ll[i];
l.linkActivated(ev);
}
}
}
/**
* Informs the user agent to change the cursor.
* @param cursor the new cursor
*/
public void setSVGCursor(Cursor cursor) {
if (cursor != JSVGComponent.this.getCursor())
JSVGComponent.this.setCursor(cursor);
}
/**
* Returns the class name of the XML parser.
*/
public String getXMLParserClassName() {
if (svgUserAgent != null) {
return svgUserAgent.getXMLParserClassName();
}
return XMLResourceDescriptor.getXMLParserClassName();
}
/**
* Returns true if the XML parser must be in validation mode, false
* otherwise depending on the SVGUserAgent.
*/
public boolean isXMLParserValidating() {
if (svgUserAgent != null) {
return svgUserAgent.isXMLParserValidating();
}
return false;
}
/**
* Returns the <code>AffineTransform</code> currently
* applied to the drawing by the UserAgent.
*/
public AffineTransform getTransform() {
return JSVGComponent.this.renderingTransform;
}
/**
* Sets the <code>AffineTransform</code> to be
* applied to the drawing by the UserAgent.
*/
public void setTransform(AffineTransform at) {
JSVGComponent.this.setRenderingTransform(at);
}
/**
* Returns this user agent's CSS media.
*/
public String getMedia() {
if (svgUserAgent != null) {
return svgUserAgent.getMedia();
}
return "screen";
}
/**
* Returns this user agent's alternate style-sheet title.
*/
public String getAlternateStyleSheet() {
if (svgUserAgent != null) {
return svgUserAgent.getAlternateStyleSheet();
}
return null;
}
/**
* Returns the location on the screen of the
* client area in the UserAgent.
*/
public Point getClientAreaLocationOnScreen() {
return getLocationOnScreen();
}
/**
* Tells whether the given feature is supported by this
* user agent.
*/
public boolean hasFeature(String s) {
return FEATURES.contains(s);
}
protected Map extensions = new HashMap();
/**
* Tells whether the given extension is supported by this
* user agent.
*/
public boolean supportExtension(String s) {
boolean ret = false;
if ((svgUserAgent != null) &&
(svgUserAgent.supportExtension(s)))
return true;
return extensions.containsKey(s);
}
/**
* Lets the bridge tell the user agent that the following
* extension is supported by the bridge.
*/
public void registerExtension(BridgeExtension ext) {
Iterator i = ext.getImplementedExtensions();
while (i.hasNext())
extensions.put(i.next(), ext);
}
/**
* Notifies the UserAgent that the input element
* has been found in the document. This is sometimes
* called, for example, to handle <a> or
* <title> elements in a UserAgent-dependant
* way.
*/
public void handleElement(Element elt, Object data) {
if (svgUserAgent != null) {
svgUserAgent.handleElement(elt, data);
}
}
/**
* Returns the security settings for the given script
* type, script url and document url
*
* @param scriptType type of script, as found in the
* type attribute of the <script> element.
* @param scriptURL url for the script, as defined in
* the script's xlink:href attribute. If that
* attribute was empty, then this parameter should
* be null
* @param docURL url for the document into which the
* script was found.
*/
public ScriptSecurity getScriptSecurity(String scriptType,
ParsedURL scriptURL,
ParsedURL docURL){
if (svgUserAgent != null){
return svgUserAgent.getScriptSecurity(scriptType,
scriptURL,
docURL);
} else {
return new DefaultScriptSecurity(scriptType,
scriptURL,
docURL);
}
}
/**
* This method throws a SecurityException if the script
* of given type, found at url and referenced from docURL
* should not be loaded.
*
* This is a convenience method to call checkLoadScript
* on the ScriptSecurity strategy returned by
* getScriptSecurity.
*
* @param scriptType type of script, as found in the
* type attribute of the <script> element.
* @param scriptURL url for the script, as defined in
* the script's xlink:href attribute. If that
* attribute was empty, then this parameter should
* be null
* @param docURL url for the document into which the
* script was found.
*/
public void checkLoadScript(String scriptType,
ParsedURL scriptURL,
ParsedURL docURL) throws SecurityException {
if (svgUserAgent != null) {
svgUserAgent.checkLoadScript(scriptType,
scriptURL,
docURL);
} else {
ScriptSecurity s = getScriptSecurity(scriptType,
scriptURL,
docURL);
if (s != null) {
s.checkLoadScript();
}
}
}
/**
* Returns the security settings for the given
* resource url and document url
*
* @param resourceURL url for the script, as defined in
* the resource's xlink:href attribute. If that
* attribute was empty, then this parameter should
* be null
* @param docURL url for the document into which the
* script was found.
*/
public ExternalResourceSecurity
getExternalResourceSecurity(ParsedURL resourceURL,
ParsedURL docURL){
if (svgUserAgent != null){
return svgUserAgent.getExternalResourceSecurity(resourceURL,
docURL);
} else {
return new RelaxedExternalResourceSecurity(resourceURL,
docURL);
}
}
/**
* This method throws a SecurityException if the resource
* found at url and referenced from docURL
* should not be loaded.
*
* This is a convenience method to call checkLoadExternalResource
* on the ExternalResourceSecurity strategy returned by
* getExternalResourceSecurity.
*
* @param scriptURL url for the script, as defined in
* the script's xlink:href attribute. If that
* attribute was empty, then this parameter should
* be null
* @param docURL url for the document into which the
* script was found.
*/
public void
checkLoadExternalResource(ParsedURL resourceURL,
ParsedURL docURL) throws SecurityException {
if (svgUserAgent != null) {
svgUserAgent.checkLoadExternalResource(resourceURL,
docURL);
} else {
ExternalResourceSecurity s
= getExternalResourceSecurity(resourceURL, docURL);
if (s != null) {
s.checkLoadExternalResource();
}
}
}
}
protected final static Set FEATURES = new HashSet();
static {
FEATURES.add(SVGConstants.SVG_ORG_W3C_SVG_FEATURE);
FEATURES.add(SVGConstants.SVG_ORG_W3C_SVG_LANG_FEATURE);
FEATURES.add(SVGConstants.SVG_ORG_W3C_SVG_STATIC_FEATURE);
}
private final static int SPLIT_THRESHOLD = 128;
/**
* Merges the given Rectangles.
*/
protected List mergeRectangles(List rects,
int x1, int y1, int x2, int y2) {
if (rects.size() <= 1) {
return rects;
}
int w = x2 - x1;
int h = y2 - y1;
if (w < SPLIT_THRESHOLD && h < SPLIT_THRESHOLD) {
// Merges all the rectangles
List result = new ArrayList();
Iterator it = rects.iterator();
Rectangle rect = (Rectangle)it.next();
while (it.hasNext()) {
Rectangle r = (Rectangle)it.next();
rect.add(r);
}
result.add(rect);
return result;
}
if (w < SPLIT_THRESHOLD) {
// Split horizontally
List h1 = new ArrayList();
List h2 = new ArrayList();
int dy = h / 2;
int av = y1 + dy;
Iterator it = rects.iterator();
while (it.hasNext()) {
Rectangle r = (Rectangle)it.next();
if (r.y < av) {
if (r.y + r.height > av) {
// The rectangle intersects the two regions
h2.add(new Rectangle(r.x, av, r.width,
(r.height + r.y) - av));
r = new Rectangle(r.x, r.y, r.width, av - r.y);
}
h1.add(r);
} else {
h2.add(r);
}
}
h1 = mergeRectangles(h1, x1, y1, x2, av - 1);
h2 = mergeRectangles(h2, x1, av, x2, y2);
h1.addAll(h2);
return h1;
}
if (h < SPLIT_THRESHOLD) {
// Split vertically
List w1 = new ArrayList();
List w2 = new ArrayList();
int dx = w / 2;
int av = x1 + dx;
Iterator it = rects.iterator();
while (it.hasNext()) {
Rectangle r = (Rectangle)it.next();
if (r.x < av) {
if (r.x + r.width > av) {
// The rectangle intersects the two regions
w2.add(new Rectangle(av, r.y,
(r.width + r.x) - av, r.height));
r = new Rectangle(r.x, r.y, av - r.x, r.height);
}
w1.add(r);
} else {
w2.add(r);
}
}
w1 = mergeRectangles(w1, x1, y1, av - 1, y2);
w2 = mergeRectangles(w2, av, y1, x2, y2);
w1.addAll(w2);
return w1;
}
// Split the region into four regions
List wh1 = new ArrayList();
List wh2 = new ArrayList();
List wh3 = new ArrayList();
List wh4 = new ArrayList();
int dx = w / 2;
int dy = h / 2;
int wav = x1 + dx;
int hav = y1 + dy;
Iterator it = rects.iterator();
while (it.hasNext()) {
Rectangle r = (Rectangle)it.next();
if (r.x < wav) {
if (r.y < hav) {
boolean c1 = r.x + r.width > wav;
boolean c2 = r.y + r.height > hav;
if (c1 && c2) {
// The rectangle intersects the four regions
wh2.add(new Rectangle(wav, r.y, (r.width + r.x) - wav,
hav - r.y));
wh3.add(new Rectangle(r.x, hav, wav - r.x,
(r.height + r.y) - hav));
wh4.add(new Rectangle(wav, hav,
(r.width + r.x) - wav,
(r.height + r.y) - hav));
r = new Rectangle(r.x, r.y, wav - r.x, hav - r.y);
} else if (c1) {
// The rectangle intersects two regions
wh2.add(new Rectangle(wav, r.y, (r.width + r.x) - wav,
r.height));
r = new Rectangle(r.x, r.y, wav - r.x, r.height);
} else if (c2) {
// The rectangle intersects two regions
wh3.add(new Rectangle(r.x, hav, r.width,
(r.height + r.y) - hav));
r = new Rectangle(r.x, r.y, r.width, hav - r.y);
}
wh1.add(r);
} else {
if (r.x + r.width > wav) {
// The rectangle intersects two regions
wh4.add(new Rectangle(wav, r.y, (r.width + r.x) - wav,
r.height));
r = new Rectangle(r.x, r.y, wav - r.x, r.height);
}
wh3.add(r);
}
} else {
if (r.y < hav) {
if (r.y + r.height > hav) {
// The rectangle intersects two regions
wh4.add(new Rectangle(r.x, hav, r.width,
(r.height + r.y) - hav));
r = new Rectangle(r.x, r.y, r.width, hav - r.y);
}
wh2.add(r);
} else {
wh4.add(r);
}
}
}
wh1 = mergeRectangles(wh1, x1, y1, wav - 1, hav - 1);
wh2 = mergeRectangles(wh2, wav, y1, x2, y2);
wh3 = mergeRectangles(wh3, x1, hav, wav - 1, y2);
wh4 = mergeRectangles(wh4, wav, hav, x2, y2);
wh1.addAll(wh2);
wh1.addAll(wh3);
wh1.addAll(wh4);
return wh1;
}
}