001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.bridge;
020:
021: import java.awt.Dimension;
022: import java.awt.RenderingHints;
023: import java.awt.Shape;
024: import java.awt.geom.AffineTransform;
025: import java.awt.geom.NoninvertibleTransformException;
026: import java.awt.geom.Rectangle2D;
027: import java.util.ArrayList;
028: import java.util.HashSet;
029: import java.util.List;
030: import java.util.Set;
031:
032: import org.apache.batik.dom.svg.AnimatedLiveAttributeValue;
033: import org.apache.batik.dom.svg.LiveAttributeException;
034: import org.apache.batik.dom.svg.SVGContext;
035: import org.apache.batik.dom.svg.SVGOMElement;
036: import org.apache.batik.dom.svg.SVGOMSVGElement;
037: import org.apache.batik.dom.svg.SVGSVGContext;
038: import org.apache.batik.ext.awt.image.renderable.ClipRable8Bit;
039: import org.apache.batik.ext.awt.image.renderable.Filter;
040: import org.apache.batik.gvt.CanvasGraphicsNode;
041: import org.apache.batik.gvt.CompositeGraphicsNode;
042: import org.apache.batik.gvt.GraphicsNode;
043: import org.apache.batik.gvt.ShapeNode;
044: import org.apache.batik.gvt.TextNode;
045:
046: import org.w3c.dom.Element;
047: import org.w3c.dom.Node;
048: import org.w3c.dom.events.MutationEvent;
049: import org.w3c.dom.svg.SVGDocument;
050: import org.w3c.dom.svg.SVGRect;
051:
052: /**
053: * Bridge class for the <svg> element.
054: *
055: * @author <a href="mailto:tkormann@apache.org">Thierry Kormann</a>
056: * @version $Id: SVGSVGElementBridge.java 475477 2006-11-15 22:44:28Z cam $
057: */
058: public class SVGSVGElementBridge extends SVGGElementBridge implements
059: SVGSVGContext {
060:
061: /**
062: * Constructs a new bridge for the <svg> element.
063: */
064: public SVGSVGElementBridge() {
065: }
066:
067: /**
068: * Returns 'svg'.
069: */
070: public String getLocalName() {
071: return SVG_SVG_TAG;
072: }
073:
074: /**
075: * Returns a new instance of this bridge.
076: */
077: public Bridge getInstance() {
078: return new SVGSVGElementBridge();
079: }
080:
081: /**
082: * Creates a <tt>CompositeGraphicsNode</tt>.
083: */
084: protected GraphicsNode instantiateGraphicsNode() {
085: return new CanvasGraphicsNode();
086: }
087:
088: /**
089: * Creates a <tt>GraphicsNode</tt> according to the specified parameters.
090: *
091: * @param ctx the bridge context to use
092: * @param e the element that describes the graphics node to build
093: * @return a graphics node that represents the specified element
094: */
095: public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
096: // 'requiredFeatures', 'requiredExtensions' and 'systemLanguage'
097: if (!SVGUtilities.matchUserAgent(e, ctx.getUserAgent())) {
098: return null;
099: }
100:
101: CanvasGraphicsNode cgn;
102: cgn = (CanvasGraphicsNode) instantiateGraphicsNode();
103:
104: associateSVGContext(ctx, e, cgn);
105:
106: try {
107: // In some cases we converted document fragments which didn't
108: // have a parent SVG element, this check makes sure only the
109: // real root of the SVG Document tries to do negotiation with
110: // the UA.
111: SVGDocument doc = (SVGDocument) e.getOwnerDocument();
112: SVGOMSVGElement se = (SVGOMSVGElement) e;
113: boolean isOutermost = (doc.getRootElement() == e);
114: float x = 0;
115: float y = 0;
116: // x and y have no meaning on the outermost 'svg' element
117: if (!isOutermost) {
118: // 'x' attribute - default is 0
119: x = se.getX().getAnimVal().getValue();
120:
121: // 'y' attribute - default is 0
122: y = se.getY().getAnimVal().getValue();
123: }
124:
125: // 'width' attribute - default is 100%
126: float w = se.getWidth().getAnimVal().getValue();
127:
128: // 'height' attribute - default is 100%
129: float h = se.getHeight().getAnimVal().getValue();
130:
131: // 'visibility'
132: cgn.setVisible(CSSUtilities.convertVisibility(e));
133:
134: // 'viewBox' and "preserveAspectRatio' attributes
135: AffineTransform viewingTransform = ViewBox
136: .getPreserveAspectRatioTransform(e, w, h, ctx);
137:
138: float actualWidth = w;
139: float actualHeight = h;
140: try {
141: AffineTransform vtInv = viewingTransform
142: .createInverse();
143: actualWidth = (float) (w * vtInv.getScaleX());
144: actualHeight = (float) (h * vtInv.getScaleY());
145: } catch (NoninvertibleTransformException ex) {
146: }
147:
148: AffineTransform positionTransform = AffineTransform
149: .getTranslateInstance(x, y);
150: // The outermost preserveAspectRatio matrix is set by the user
151: // agent, so we don't need to set the transform for outermost svg
152: if (!isOutermost) {
153: // X & Y are ignored on outermost SVG.
154: cgn.setPositionTransform(positionTransform);
155: } else if (doc == ctx.getDocument()) {
156: // <!> FIXME: hack to compute the original document's size
157: ctx.setDocumentSize(new Dimension((int) (w + 0.5f),
158: (int) (h + 0.5f)));
159: }
160: // Set the viewing transform, this is often updated when the
161: // component prepares for rendering.
162: cgn.setViewingTransform(viewingTransform);
163:
164: // 'overflow' and 'clip'
165: Shape clip = null;
166: if (CSSUtilities.convertOverflow(e)) { // overflow:hidden
167: float[] offsets = CSSUtilities.convertClip(e);
168: if (offsets == null) { // clip:auto
169: clip = new Rectangle2D.Float(x, y, w, h);
170: } else { // clip:rect(<x> <y> <w> <h>)
171: // offsets[0] = top
172: // offsets[1] = right
173: // offsets[2] = bottom
174: // offsets[3] = left
175: clip = new Rectangle2D.Float(x + offsets[3], y
176: + offsets[0], w - offsets[1] - offsets[3],
177: h - offsets[2] - offsets[0]);
178: }
179: }
180:
181: if (clip != null) {
182: try {
183: AffineTransform at = new AffineTransform(
184: positionTransform);
185: at.concatenate(viewingTransform);
186: at = at.createInverse(); // clip in user space
187: clip = at.createTransformedShape(clip);
188: Filter filter = cgn.getGraphicsNodeRable(true);
189: cgn.setClip(new ClipRable8Bit(filter, clip));
190: } catch (NoninvertibleTransformException ex) {
191: }
192: }
193: RenderingHints hints = null;
194: hints = CSSUtilities.convertColorRendering(e, hints);
195: if (hints != null)
196: cgn.setRenderingHints(hints);
197:
198: // 'enable-background'
199: Rectangle2D r = CSSUtilities.convertEnableBackground(e);
200: if (r != null) {
201: cgn.setBackgroundEnable(r);
202: }
203:
204: ctx.openViewport(e, new SVGSVGElementViewport(actualWidth,
205: actualHeight));
206: return cgn;
207: } catch (LiveAttributeException ex) {
208: throw new BridgeException(ctx, ex);
209: }
210: }
211:
212: /**
213: * Builds using the specified BridgeContext and element, the
214: * specified graphics node.
215: *
216: * @param ctx the bridge context to use
217: * @param e the element that describes the graphics node to build
218: * @param node the graphics node to build
219: */
220: public void buildGraphicsNode(BridgeContext ctx, Element e,
221: GraphicsNode node) {
222:
223: // 'opacity'
224: node.setComposite(CSSUtilities.convertOpacity(e));
225: // 'filter'
226: node.setFilter(CSSUtilities.convertFilter(e, node, ctx));
227: // 'mask'
228: node.setMask(CSSUtilities.convertMask(e, node, ctx));
229: // 'pointer-events'
230: node.setPointerEventType(CSSUtilities.convertPointerEvents(e));
231:
232: initializeDynamicSupport(ctx, e, node);
233:
234: ctx.closeViewport(e);
235: }
236:
237: // BridgeUpdateHandler implementation //////////////////////////////////
238:
239: /**
240: * Disposes this BridgeUpdateHandler and releases all resources.
241: */
242: public void dispose() {
243: ctx.removeViewport(e);
244: super .dispose();
245: }
246:
247: /**
248: * Invoked when the animated value of an animatable attribute has changed.
249: */
250: public void handleAnimatedAttributeChanged(
251: AnimatedLiveAttributeValue alav) {
252: try {
253: boolean rebuild = false;
254: if (alav.getNamespaceURI() == null) {
255: String ln = alav.getLocalName();
256: if (ln.equals(SVG_WIDTH_ATTRIBUTE)
257: || ln.equals(SVG_HEIGHT_ATTRIBUTE)) {
258: rebuild = true;
259: } else if (ln.equals(SVG_X_ATTRIBUTE)
260: || ln.equals(SVG_Y_ATTRIBUTE)) {
261: SVGDocument doc = (SVGDocument) e
262: .getOwnerDocument();
263: SVGOMSVGElement se = (SVGOMSVGElement) e;
264: // X & Y are ignored on outermost SVG.
265: boolean isOutermost = doc.getRootElement() == e;
266: if (!isOutermost) {
267: // 'x' attribute - default is 0
268: float x = se.getX().getAnimVal().getValue();
269:
270: // 'y' attribute - default is 0
271: float y = se.getY().getAnimVal().getValue();
272:
273: AffineTransform positionTransform = AffineTransform
274: .getTranslateInstance(x, y);
275: CanvasGraphicsNode cgn;
276: cgn = (CanvasGraphicsNode) node;
277:
278: cgn.setPositionTransform(positionTransform);
279: return;
280: }
281: }
282:
283: if (rebuild) {
284: CompositeGraphicsNode gn = node.getParent();
285: gn.remove(node);
286: disposeTree(e, false);
287:
288: handleElementAdded(gn, e.getParentNode(), e);
289: return;
290: }
291: }
292: } catch (LiveAttributeException ex) {
293: throw new BridgeException(ctx, ex);
294: }
295: super .handleAnimatedAttributeChanged(alav);
296: }
297:
298: /**
299: * Invoked when an MutationEvent of type 'DOMAttrModified' is fired.
300: */
301: public void handleDOMAttrModifiedEvent(MutationEvent evt) {
302: try {
303: // Don't call 'super' because there is no 'transform'
304: // attribute on <svg>
305: String attrName = evt.getAttrName();
306: boolean rebuild = false;
307: if (attrName.equals(SVG_VIEW_BOX_ATTRIBUTE)
308: || attrName
309: .equals(SVG_PRESERVE_ASPECT_RATIO_ATTRIBUTE)) {
310: SVGDocument doc = (SVGDocument) e.getOwnerDocument();
311: SVGOMSVGElement se = (SVGOMSVGElement) e;
312: boolean isOutermost = doc.getRootElement() == e;
313:
314: // X & Y are ignored on outermost SVG.
315: float x = 0;
316: float y = 0;
317: if (!isOutermost) {
318: // 'x' attribute - default is 0
319: x = se.getX().getAnimVal().getValue();
320:
321: // 'y' attribute - default is 0
322: y = se.getY().getAnimVal().getValue();
323: }
324:
325: // 'width' attribute - default is 100%
326: float w = se.getWidth().getAnimVal().getValue();
327:
328: // 'height' attribute - default is 100%
329: float h = se.getHeight().getAnimVal().getValue();
330:
331: CanvasGraphicsNode cgn;
332: cgn = (CanvasGraphicsNode) node;
333:
334: // 'viewBox' and "preserveAspectRatio' attributes
335: AffineTransform newVT = ViewBox
336: .getPreserveAspectRatioTransform(e, w, h, ctx);
337: AffineTransform oldVT = cgn.getViewingTransform();
338: if ((newVT.getScaleX() != oldVT.getScaleX())
339: || (newVT.getScaleY() != oldVT.getScaleY())
340: || (newVT.getShearX() != oldVT.getShearX())
341: || (newVT.getShearY() != oldVT.getShearY()))
342: rebuild = true;
343: else {
344: // Only differs in translate.
345: cgn.setViewingTransform(newVT);
346:
347: // 'overflow' and 'clip'
348: Shape clip = null;
349: if (CSSUtilities.convertOverflow(e)) { // overflow:hidden
350: float[] offsets = CSSUtilities.convertClip(e);
351: if (offsets == null) { // clip:auto
352: clip = new Rectangle2D.Float(x, y, w, h);
353: } else { // clip:rect(<x> <y> <w> <h>)
354: // offsets[0] = top
355: // offsets[1] = right
356: // offsets[2] = bottom
357: // offsets[3] = left
358: clip = new Rectangle2D.Float(
359: x + offsets[3], y + offsets[0], w
360: - offsets[1] - offsets[3],
361: h - offsets[2] - offsets[0]);
362: }
363: }
364:
365: if (clip != null) {
366: try {
367: AffineTransform at;
368: at = cgn.getPositionTransform();
369: if (at == null)
370: at = new AffineTransform();
371: else
372: at = new AffineTransform(at);
373: at.concatenate(newVT);
374: at = at.createInverse(); // clip in user space
375: clip = at.createTransformedShape(clip);
376: Filter filter = cgn
377: .getGraphicsNodeRable(true);
378: cgn
379: .setClip(new ClipRable8Bit(filter,
380: clip));
381: } catch (NoninvertibleTransformException ex) {
382: }
383: }
384: }
385: }
386:
387: if (rebuild) {
388: CompositeGraphicsNode gn = node.getParent();
389: gn.remove(node);
390: disposeTree(e, false);
391:
392: handleElementAdded(gn, e.getParentNode(), e);
393: }
394: } catch (LiveAttributeException ex) {
395: throw new BridgeException(ctx, ex);
396: }
397: }
398:
399: /**
400: * A viewport defined an <svg> element.
401: */
402: public static class SVGSVGElementViewport implements Viewport {
403: private float width;
404: private float height;
405:
406: /**
407: * Constructs a new viewport with the specified <tt>SVGSVGElement</tt>.
408: * @param w the width of the viewport
409: * @param h the height of the viewport
410: */
411: public SVGSVGElementViewport(float w, float h) {
412: this .width = w;
413: this .height = h;
414: }
415:
416: /**
417: * Returns the width of this viewport.
418: */
419: public float getWidth() {
420: return width;
421: }
422:
423: /**
424: * Returns the height of this viewport.
425: */
426: public float getHeight() {
427: return height;
428: }
429: }
430:
431: public List getIntersectionList(SVGRect svgRect, Element end) {
432: List ret = new ArrayList();
433: Rectangle2D rect = new Rectangle2D.Float(svgRect.getX(),
434: svgRect.getY(), svgRect.getWidth(), svgRect.getHeight());
435:
436: GraphicsNode svgGN = ctx.getGraphicsNode(e);
437: if (svgGN == null)
438: return ret;
439:
440: Rectangle2D svgBounds = svgGN.getSensitiveBounds();
441: if (svgBounds == null)
442: return ret;
443:
444: // If the svg elem doesn't intersect none of the children
445: // will.
446: if (!rect.intersects(svgBounds))
447: return ret;
448:
449: Element base = e;
450: AffineTransform ati = svgGN.getGlobalTransform();
451: try {
452: ati = ati.createInverse();
453: } catch (NoninvertibleTransformException e) {
454: }
455:
456: Element curr;
457: Node next = base.getFirstChild();
458: while (next != null) {
459: if (next instanceof Element)
460: break;
461: next = next.getNextSibling();
462: }
463: if (next == null)
464: return ret;
465: curr = (Element) next;
466:
467: Set ancestors = null;
468: if (end != null) {
469: ancestors = getAncestors(end, base);
470: if (ancestors == null)
471: end = null;
472: }
473: while (curr != null) {
474: String nsURI = curr.getNamespaceURI();
475: String tag = curr.getLocalName();
476: boolean isGroup;
477: isGroup = SVG_NAMESPACE_URI.equals(nsURI)
478: && (SVG_G_TAG.equals(tag)
479: || SVG_SVG_TAG.equals(tag) || SVG_A_TAG
480: .equals(tag));
481:
482: GraphicsNode gn = ctx.getGraphicsNode(curr);
483: if (gn == null) {
484: // No graphics node but check if curr is an
485: // ancestor of end.
486: if ((ancestors != null) && (ancestors.contains(curr)))
487: break;
488: curr = getNext(curr, base, end);
489: continue;
490: }
491:
492: AffineTransform at = gn.getGlobalTransform();
493: Rectangle2D gnBounds = gn.getSensitiveBounds();
494: at.preConcatenate(ati);
495: if (gnBounds != null)
496: gnBounds = at.createTransformedShape(gnBounds)
497: .getBounds2D();
498:
499: if ((gnBounds == null) || (!rect.intersects(gnBounds))) {
500: // Graphics node does not intersect check if curr is
501: // an ancestor of end.
502: if ((ancestors != null) && (ancestors.contains(curr)))
503: break;
504: curr = getNext(curr, base, end);
505: continue;
506: }
507:
508: // Check if it is an SVG 'g', or 'svg' element in
509: // which case don't add this node but do check it's
510: // children.
511: if (isGroup) {
512: // Check children.
513: next = curr.getFirstChild();
514: while (next != null) {
515: if (next instanceof Element)
516: break;
517: next = next.getNextSibling();
518: }
519: if (next != null) {
520: curr = (Element) next;
521: continue;
522: }
523: } else {
524: if (curr == end)
525: break;
526: // Otherwise check this node for intersection more
527: // carefully and if it still intersects add it.
528: if (SVG_NAMESPACE_URI.equals(nsURI)
529: && SVG_USE_TAG.equals(tag)) {
530: // FIXX: This really isn't right we need to
531: // Add the proxy children.
532: if (rect.contains(gnBounds))
533: ret.add(curr);
534: }
535: if (gn instanceof ShapeNode) {
536: ShapeNode sn = (ShapeNode) gn;
537: Shape sensitive = sn.getSensitiveArea();
538: if (sensitive != null) {
539: sensitive = at
540: .createTransformedShape(sensitive);
541: if (sensitive.intersects(rect))
542: ret.add(curr);
543: }
544: } else if (gn instanceof TextNode) {
545: SVGOMElement svgElem = (SVGOMElement) curr;
546: SVGTextElementBridge txtBridge;
547: txtBridge = (SVGTextElementBridge) svgElem
548: .getSVGContext();
549: Set elems = txtBridge.getTextIntersectionSet(at,
550: rect);
551:
552: // filter elems based on who is before end as
553: // children of curr, if needed.
554: if ((ancestors != null) && ancestors.contains(curr))
555: filterChildren(curr, end, elems, ret);
556: else
557: ret.addAll(elems);
558:
559: } else {
560: ret.add(curr);
561: }
562: }
563:
564: curr = getNext(curr, base, end);
565: }
566:
567: return ret;
568: }
569:
570: public List getEnclosureList(SVGRect svgRect, Element end) {
571: List ret = new ArrayList();
572: Rectangle2D rect = new Rectangle2D.Float(svgRect.getX(),
573: svgRect.getY(), svgRect.getWidth(), svgRect.getHeight());
574: GraphicsNode svgGN = ctx.getGraphicsNode(e);
575: if (svgGN == null)
576: return ret;
577:
578: Rectangle2D svgBounds = svgGN.getSensitiveBounds();
579: if (svgBounds == null)
580: return ret;
581:
582: // If the svg elem doesn't at least intersect none of the
583: // children will be enclosed.
584: if (!rect.intersects(svgBounds))
585: return ret;
586:
587: Element base = e;
588: AffineTransform ati = svgGN.getGlobalTransform();
589: try {
590: ati = ati.createInverse();
591: } catch (NoninvertibleTransformException e) {
592: }
593:
594: Element curr;
595: Node next = base.getFirstChild();
596: while (next != null) {
597: if (next instanceof Element)
598: break;
599: next = next.getNextSibling();
600: }
601:
602: if (next == null)
603: return ret;
604: curr = (Element) next;
605:
606: Set ancestors = null;
607: if (end != null) {
608: ancestors = getAncestors(end, base);
609: if (ancestors == null)
610: end = null;
611: }
612:
613: while (curr != null) {
614: String nsURI = curr.getNamespaceURI();
615: String tag = curr.getLocalName();
616: boolean isGroup;
617: isGroup = SVG_NAMESPACE_URI.equals(nsURI)
618: && (SVG_G_TAG.equals(tag)
619: || SVG_SVG_TAG.equals(tag) || SVG_A_TAG
620: .equals(tag));
621:
622: GraphicsNode gn = ctx.getGraphicsNode(curr);
623: if (gn == null) {
624: // No graphics node but check if curr is an
625: // ancestor of end.
626: if ((ancestors != null) && (ancestors.contains(curr)))
627: break;
628: curr = getNext(curr, base, end);
629: continue;
630: }
631:
632: AffineTransform at = gn.getGlobalTransform();
633: Rectangle2D gnBounds = gn.getSensitiveBounds();
634: at.preConcatenate(ati);
635: if (gnBounds != null)
636: gnBounds = at.createTransformedShape(gnBounds)
637: .getBounds2D();
638:
639: if ((gnBounds == null) || (!rect.intersects(gnBounds))) {
640: // Graphics node does not intersect check if curr is
641: // an ancestor of end.
642: if ((ancestors != null) && (ancestors.contains(curr)))
643: break;
644: curr = getNext(curr, base, end);
645: continue;
646: }
647:
648: // If it is a group then don't add this node but do check
649: // it's children.
650: if (isGroup) {
651: // Check children.
652: next = curr.getFirstChild();
653: while (next != null) {
654: if (next instanceof Element)
655: break;
656: next = next.getNextSibling();
657: }
658: if (next != null) {
659: curr = (Element) next;
660: continue;
661: }
662: } else {
663: if (curr == end)
664: break;
665: if (SVG_NAMESPACE_URI.equals(nsURI)
666: && SVG_USE_TAG.equals(tag)) {
667: // FIXX: This really isn't right we need to
668: // Add the proxy children.
669: if (rect.contains(gnBounds))
670: ret.add(curr);
671: } else if (gn instanceof TextNode) {
672: // If gnBounds is contained in rect then just add
673: // all the children
674: SVGOMElement svgElem = (SVGOMElement) curr;
675: SVGTextElementBridge txtBridge;
676: txtBridge = (SVGTextElementBridge) svgElem
677: .getSVGContext();
678: Set elems = txtBridge.getTextEnclosureSet(at, rect);
679:
680: // filter elems based on who is before end as
681: // children of curr if needed.
682: if ((ancestors != null) && ancestors.contains(curr))
683: filterChildren(curr, end, elems, ret);
684: else
685: ret.addAll(elems);
686: } else if (rect.contains(gnBounds)) {
687: // shape nodes
688: ret.add(curr);
689: }
690: }
691:
692: curr = getNext(curr, base, end);
693: }
694: return ret;
695: }
696:
697: public boolean checkIntersection(Element element, SVGRect svgRect) {
698:
699: GraphicsNode svgGN = ctx.getGraphicsNode(e);
700: if (svgGN == null)
701: return false; // not in tree?
702:
703: Rectangle2D rect = new Rectangle2D.Float(svgRect.getX(),
704: svgRect.getY(), svgRect.getWidth(), svgRect.getHeight());
705: AffineTransform ati = svgGN.getGlobalTransform();
706:
707: try {
708: ati = ati.createInverse();
709: } catch (NoninvertibleTransformException e) {
710: }
711:
712: SVGContext svgctx = null;
713: if (element instanceof SVGOMElement) {
714: svgctx = ((SVGOMElement) element).getSVGContext();
715: if ((svgctx instanceof SVGTextElementBridge)
716: || (svgctx instanceof SVGTextElementBridge.AbstractTextChildSVGContext)) {
717: return SVGTextElementBridge.getTextIntersection(ctx,
718: element, ati, rect, true);
719: }
720: }
721:
722: Rectangle2D gnBounds = null;
723: GraphicsNode gn = ctx.getGraphicsNode(element);
724: if (gn != null)
725: gnBounds = gn.getSensitiveBounds();
726:
727: if (gnBounds == null)
728: return false;
729:
730: AffineTransform at = gn.getGlobalTransform();
731: at.preConcatenate(ati);
732:
733: gnBounds = at.createTransformedShape(gnBounds).getBounds2D();
734: if (!rect.intersects(gnBounds))
735: return false;
736:
737: // Check GN more closely
738: if (!(gn instanceof ShapeNode))
739: return true;
740:
741: ShapeNode sn = (ShapeNode) gn;
742: Shape sensitive = sn.getSensitiveArea();
743: if (sensitive == null)
744: return false;
745:
746: sensitive = at.createTransformedShape(sensitive);
747: if (sensitive.intersects(rect))
748: return true;
749:
750: return false;
751: }
752:
753: public boolean checkEnclosure(Element element, SVGRect svgRect) {
754: GraphicsNode gn = ctx.getGraphicsNode(element);
755: Rectangle2D gnBounds = null;
756: SVGContext svgctx = null;
757: if (element instanceof SVGOMElement) {
758: svgctx = ((SVGOMElement) element).getSVGContext();
759: if ((svgctx instanceof SVGTextElementBridge)
760: || (svgctx instanceof SVGTextElementBridge.AbstractTextChildSVGContext)) {
761: gnBounds = SVGTextElementBridge.getTextBounds(ctx,
762: element, true);
763: Element p = (Element) element.getParentNode();
764: // Get GN for text children so we can get transform.
765: while ((p != null) && (gn == null)) {
766: gn = ctx.getGraphicsNode(p);
767: p = (Element) p.getParentNode();
768: }
769: } else if (gn != null)
770: gnBounds = gn.getSensitiveBounds();
771: } else if (gn != null)
772: gnBounds = gn.getSensitiveBounds();
773:
774: if (gnBounds == null)
775: return false;
776:
777: GraphicsNode svgGN = ctx.getGraphicsNode(e);
778: if (svgGN == null)
779: return false; // not in tree?
780:
781: Rectangle2D rect = new Rectangle2D.Float(svgRect.getX(),
782: svgRect.getY(), svgRect.getWidth(), svgRect.getHeight());
783: AffineTransform ati = svgGN.getGlobalTransform();
784: try {
785: ati = ati.createInverse();
786: } catch (NoninvertibleTransformException e) {
787: }
788:
789: AffineTransform at = gn.getGlobalTransform();
790: at.preConcatenate(ati);
791:
792: gnBounds = at.createTransformedShape(gnBounds).getBounds2D();
793:
794: return rect.contains(gnBounds);
795: }
796:
797: public boolean filterChildren(Element curr, Element end, Set elems,
798: List ret) {
799: Node child = curr.getFirstChild();
800: while (child != null) {
801: if ((child instanceof Element)
802: && filterChildren((Element) child, end, elems, ret))
803: return true;
804: child = child.getNextSibling();
805: }
806:
807: if (curr == end)
808: return true;
809:
810: if (elems.contains(curr))
811: ret.add(curr);
812:
813: return false;
814: }
815:
816: protected Set getAncestors(Element end, Element base) {
817: Set ret = new HashSet();
818: Element p = end;
819: do {
820: ret.add(p);
821: p = (Element) p.getParentNode();
822: } while ((p != null) && (p != base));
823:
824: if (p == null) // 'end' is not a child of 'base'.
825: return null;
826:
827: return ret;
828: }
829:
830: protected Element getNext(Element curr, Element base, Element end) {
831: Node next;
832: // Check the next element.
833: next = curr.getNextSibling();
834: while (next != null) {
835: if (next instanceof Element)
836: break;
837: next = next.getNextSibling();
838: }
839: while (next == null) {
840: // No sibling so check parent's siblings...
841: curr = (Element) curr.getParentNode();
842: if ((curr == end) || (curr == base)) {
843: next = null; // signal we are done!
844: break;
845: }
846: next = curr.getNextSibling();
847: while (next != null) {
848: if (next instanceof Element)
849: break;
850: next = next.getNextSibling();
851: }
852: }
853:
854: return (Element) next;
855: }
856:
857: public void deselectAll() {
858: ctx.getUserAgent().deselectAll();
859: }
860:
861: public int suspendRedraw(int max_wait_milliseconds) {
862: UpdateManager um = ctx.getUpdateManager();
863: if (um != null)
864: return um.addRedrawSuspension(max_wait_milliseconds);
865: return -1;
866: }
867:
868: public boolean unsuspendRedraw(int suspend_handle_id) {
869: UpdateManager um = ctx.getUpdateManager();
870: if (um != null)
871: return um.releaseRedrawSuspension(suspend_handle_id);
872: return false; // no UM so couldn't have issued an id...
873: }
874:
875: public void unsuspendRedrawAll() {
876: UpdateManager um = ctx.getUpdateManager();
877: if (um != null)
878: um.releaseAllRedrawSuspension();
879: }
880:
881: public void forceRedraw() {
882: UpdateManager um = ctx.getUpdateManager();
883: if (um != null)
884: um.forceRepaint();
885: }
886:
887: /**
888: * Pauses animations in the document.
889: */
890: public void pauseAnimations() {
891: ctx.getAnimationEngine().pause();
892: }
893:
894: /**
895: * Unpauses animations in the document.
896: */
897: public void unpauseAnimations() {
898: ctx.getAnimationEngine().unpause();
899: }
900:
901: /**
902: * Returns whether animations are currently paused.
903: */
904: public boolean animationsPaused() {
905: return ctx.getAnimationEngine().isPaused();
906: }
907:
908: /**
909: * Returns the current document time.
910: */
911: public float getCurrentTime() {
912: return ctx.getAnimationEngine().getCurrentTime();
913: }
914:
915: /**
916: * Sets the current document time.
917: */
918: public void setCurrentTime(float t) {
919: ctx.getAnimationEngine().setCurrentTime(t);
920: }
921: }
|