001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */package org.netbeans.modules.mobility.svgcore.composer;
041:
042: import com.sun.perseus.awt.SVGAnimatorImpl;
043: import com.sun.perseus.builder.ModelBuilder;
044: import com.sun.perseus.j2d.Point;
045: import com.sun.perseus.j2d.Transform;
046: import com.sun.perseus.model.AbstractAnimate;
047: import com.sun.perseus.model.CompositeGraphicsNode;
048: import com.sun.perseus.model.DocumentNode;
049: import com.sun.perseus.model.ModelNode;
050: import com.sun.perseus.model.SVG;
051: import com.sun.perseus.model.SVGImageImpl;
052: import com.sun.perseus.model.Time;
053: import com.sun.perseus.model.UpdateAdapter;
054: import com.sun.perseus.util.SVGConstants;
055: import java.awt.AWTEvent;
056: import java.awt.AWTEvent;
057: import java.io.IOException;
058: import java.io.InputStream;
059: import java.util.ArrayList;
060: import java.util.HashSet;
061: import java.util.List;
062: import java.util.Set;
063: import java.util.logging.Level;
064: import javax.microedition.m2g.ExternalResourceHandler;
065: import javax.microedition.m2g.SVGAnimator;
066: import javax.microedition.m2g.SVGImage;
067: import javax.swing.JComponent;
068: import org.netbeans.modules.mobility.svgcore.composer.prototypes.PatchedAnimationElement;
069: import org.netbeans.modules.mobility.svgcore.composer.prototypes.PatchedElement;
070: import org.netbeans.modules.mobility.svgcore.composer.prototypes.PatchedGroup;
071: import org.netbeans.modules.mobility.svgcore.composer.prototypes.PatchedTransformableElement;
072: import org.netbeans.modules.mobility.svgcore.composer.prototypes.SVGComposerPrototypeFactory;
073: import org.w3c.dom.Document;
074: import org.w3c.dom.Element;
075: import org.w3c.dom.Node;
076: import org.w3c.dom.svg.SVGAnimationElement;
077: import org.w3c.dom.svg.SVGElement;
078: import org.w3c.dom.svg.SVGLocatableElement;
079: import org.w3c.dom.svg.SVGMatrix;
080: import org.w3c.dom.svg.SVGPoint;
081: import org.w3c.dom.svg.SVGRect;
082: import org.w3c.dom.svg.SVGSVGElement;
083:
084: /**
085: *
086: * @author Pavel Benes
087: */
088: public final class PerseusController {
089: public static final int ANIMATION_NOT_AVAILABLE = 0;
090: public static final int ANIMATION_NOT_RUNNING = 1;
091: public static final int ANIMATION_RUNNING = 2;
092: public static final int ANIMATION_PAUSED = 3;
093:
094: public static final float ANIMATION_DEFAULT_DURATION = 30.0f;
095: public static final float ANIMATION_DEFAULT_STEP = 0.1f;
096: public static final String ID_VIEWBOX_MARKER = "$VIEWBOX$"; //NOI18N
097: public static final String ID_BBOX_MARKER = "$BBOX$"; //NOI18N
098:
099: private static final String[] ANIM_PATTERNS = new String[] {
100: "." + SVGConstants.SVG_DOMFOCUSIN_EVENT_TYPE, //NOI18N
101: "." + SVGConstants.SVG_DOMFOCUSOUT_EVENT_TYPE + //NOI18N
102: "." + SVGConstants.SVG_DOMACTIVATE_EVENT_TYPE }; //NOI18N
103: private static final SVGMatrix IDENTITY_TRANSFORM = new Transform(
104: null);
105:
106: private static int s_instanceCounter = 0;
107:
108: protected final SceneManager m_sceneMgr;
109: protected final String m_id;
110: protected SVGAnimatorImpl m_animator;
111: protected SVGImage m_svgImage;
112: protected DocumentNode m_svgDoc;
113: protected SVGLocatableElement m_viewBoxMarker;
114: protected int m_animationState = ANIMATION_NOT_AVAILABLE;
115: protected float m_currentTime = 0.0f;
116:
117: PerseusController(SceneManager sceneMgr) {
118: m_sceneMgr = sceneMgr;
119: m_id = "PC-" + (s_instanceCounter++) + "-" + sceneMgr;
120: }
121:
122: void initialize() {
123: m_svgImage = m_sceneMgr.getSVGImage();
124: m_svgDoc = (DocumentNode) m_svgImage.getDocument();
125: m_animator = (SVGAnimatorImpl) SVGAnimator.createAnimator(
126: m_svgImage, "javax.swing.JComponent"); //NOI18N
127: m_animationState = containsAnimation(m_svgDoc) ? ANIMATION_NOT_RUNNING
128: : ANIMATION_NOT_AVAILABLE;
129:
130: SVGSVGElement svg = getSVGRootElement();
131: SVGRect rect = svg
132: .getRectTrait(SVGConstants.SVG_VIEW_BOX_ATTRIBUTE);
133:
134: if (rect != null) {
135: m_viewBoxMarker = (SVGLocatableElement) m_svgDoc
136: .createElementNS(SVGConstants.SVG_NAMESPACE_URI,
137: SVGConstants.SVG_RECT_TAG);
138: m_viewBoxMarker.setId(ID_VIEWBOX_MARKER);
139: m_viewBoxMarker.setTrait(SVGConstants.SVG_FILL_ATTRIBUTE,
140: "none"); //NOI18N
141: m_viewBoxMarker.setTrait(SVGConstants.SVG_STROKE_ATTRIBUTE,
142: "none"); //NOI18N
143: m_viewBoxMarker.setFloatTrait(SVGConstants.SVG_X_ATTRIBUTE,
144: rect.getX());
145: m_viewBoxMarker.setFloatTrait(SVGConstants.SVG_Y_ATTRIBUTE,
146: rect.getY());
147: m_viewBoxMarker.setFloatTrait(
148: SVGConstants.SVG_WIDTH_ATTRIBUTE, rect.getWidth());
149: m_viewBoxMarker
150: .setFloatTrait(SVGConstants.SVG_HEIGHT_ATTRIBUTE,
151: rect.getHeight());
152:
153: svg.appendChild(m_viewBoxMarker);
154: } else {
155: m_viewBoxMarker = null;
156: }
157:
158: // we need to get the animator into the 'paused' state so that
159: // all changes are immediately visible
160: m_animator.play();
161: m_animator.pause();
162: SceneManager.log(Level.INFO, toString() + " initialized."); //NOI18N
163: }
164:
165: public SVGPoint convertCoords(float x, float y) {
166: SVGMatrix m = getSVGRootElement().getScreenCTM().inverse();
167: return new Point(m.getComponent(0) * x + m.getComponent(2) * y
168: + m.getComponent(4), m.getComponent(1) * x
169: + m.getComponent(3) * y + m.getComponent(5));
170: }
171:
172: public SVGLocatableElement getViewBoxMarker() {
173: return m_viewBoxMarker;
174: }
175:
176: public JComponent getAnimatorGUI() {
177: return (JComponent) m_animator.getTargetComponent();
178: }
179:
180: public void execute(Runnable command) {
181: try {
182: m_animator.invokeAndWait(command);
183: } catch (Exception ex) {
184: SceneManager.error("Command execution failed.", ex); //NOI18N
185: }
186: }
187:
188: public DocumentNode getSVGDocument() {
189: return m_svgDoc;
190: }
191:
192: public SVGSVGElement getSVGRootElement() {
193: return (SVGSVGElement) m_svgDoc.getDocumentElement();
194: }
195:
196: public SVGObject getObjectById(String id) {
197: SVGElement elem = getElementById(id);
198: if (elem != null && elem instanceof SVGLocatableElement) {
199: SVGLocatableElement locElem = (SVGLocatableElement) elem;
200: if (getSafeScreenBBox(locElem) != null) {
201: return getSVGObject(locElem);
202: }
203: }
204: SceneManager.log(Level.INFO, "No object found for id " + id
205: + "(" + toString() + ")"); //NOI18N
206: return null;
207: }
208:
209: public SVGElement getElementById(String id) {
210: SVGElement elem = getElementById(
211: (ModelNode) getSVGRootElement(), id);
212: return elem;
213: }
214:
215: public SVGObject[] getObjectsAt(int x, int y) {
216: SVGLocatableElement elem = findElementAt(x, y);
217:
218: if (elem != null) {
219: //try to find an parent envelope node if any
220: Node node = elem;
221: while (node != null) {
222: if (PatchedGroup.isWrapper(node)) {
223: elem = (SVGLocatableElement) node;
224: break;
225: }
226: node = node.getParentNode();
227: }
228:
229: SVGObject obj = getSVGObject(elem);
230: if (obj != null) {
231: return new SVGObject[] { obj };
232: }
233: }
234: return null;
235: }
236:
237: public void delete(final SVGElement elem) {
238: execute(new Runnable() {
239: public void run() {
240: Node parent = elem.getParentNode();
241: // HACK - clear all elements' ids so that the element removal is possible
242: setNullIds(elem, true);
243: try {
244: parent.removeChild(elem);
245: } finally {
246: setNullIds(elem, false);
247: }
248: }
249: });
250: }
251:
252: public void moveToBottom(final SVGLocatableElement elem) {
253: final Node parent = elem.getParentNode();
254: final ModelNode firstChild = ((ModelNode) parent)
255: .getFirstChildNode();
256:
257: if (firstChild != elem) {
258: execute(new Runnable() {
259: public void run() {
260: // HACK - clear all elements' ids so that the element removal is possible
261: setNullIds(elem, true);
262: try {
263: parent.removeChild(elem);
264: parent.insertBefore(elem, (Node) firstChild);
265: } finally {
266: setNullIds(elem, false);
267: }
268: }
269: });
270: }
271: }
272:
273: public void moveToTop(final SVGLocatableElement elem) {
274: final Node parent = elem.getParentNode();
275: final ModelNode lastChild = ((ModelNode) parent)
276: .getLastChildNode();
277:
278: if (lastChild != elem) {
279: execute(new Runnable() {
280: public void run() {
281: // HACK - clear all elements' ids so that the element removal is possible
282: setNullIds(elem, true);
283: try {
284: parent.removeChild(elem);
285: parent.appendChild(elem);
286: } finally {
287: setNullIds(elem, false);
288: }
289: }
290: });
291: }
292: }
293:
294: public void moveBackward(final SVGLocatableElement elem) {
295: final Node parent = elem.getParentNode();
296: final ModelNode firstChild = ((ModelNode) parent)
297: .getFirstChildNode();
298:
299: if (firstChild != elem) {
300: execute(new Runnable() {
301: public void run() {
302: // HACK - clear all elements' ids so that the element removal is possible
303: setNullIds(elem, true);
304: try {
305: ModelNode previousChild = ((ModelNode) elem)
306: .getPreviousSiblingNode();
307: assert previousChild != null;
308: parent.removeChild(elem);
309:
310: parent.insertBefore(elem, (Node) previousChild);
311: } finally {
312: setNullIds(elem, false);
313: }
314: }
315: });
316: }
317: }
318:
319: public void moveForward(final SVGLocatableElement elem) {
320: final Node parent = elem.getParentNode();
321: final ModelNode lastChild = ((ModelNode) parent)
322: .getLastChildNode();
323:
324: if (lastChild != elem) {
325: execute(new Runnable() {
326: public void run() {
327: // HACK - clear all elements' ids so that the element removal is possible
328: setNullIds(elem, true);
329: try {
330: ModelNode nextChild = ((ModelNode) elem)
331: .getNextSiblingNode();
332: assert nextChild != null;
333: parent.removeChild(elem);
334: if (nextChild == lastChild) {
335: parent.appendChild(elem);
336: } else {
337: parent.insertBefore(elem, (Node) nextChild
338: .getNextSiblingNode());
339: }
340: } finally {
341: setNullIds(elem, false);
342: }
343: }
344: });
345: }
346: }
347:
348: public SVGLocatableElement findElementAt(int x, int y) {
349: float[] pt = { x, y };
350: ModelNode target = m_svgDoc.nodeHitAt(pt);
351: SVGLocatableElement elt = null;
352: if (target != null) {
353: while (elt == null && target != null) {
354: if (target instanceof SVGLocatableElement) {
355: elt = (SVGLocatableElement) target;
356: } else {
357: target = target.getParent();
358: }
359: }
360: }
361: return elt;
362: }
363:
364: public static SVGElement hideAllButSubtree(ModelNode node, String id) {
365: if (node instanceof SVGElement) {
366: SVGElement elem = (SVGElement) node;
367: if (id.equals(elem.getId())) {
368: return elem;
369: }
370: }
371:
372: ModelNode child = node.getFirstChildNode();
373: SVGElement visibleChild = null;
374:
375: while (child != null) {
376: SVGElement e;
377: if ((e = hideAllButSubtree(child, id)) != null) {
378: visibleChild = e;
379: }
380: child = child.getNextSiblingNode();
381: }
382: if (node instanceof CompositeGraphicsNode) {
383: CompositeGraphicsNode elem = (CompositeGraphicsNode) node;
384: if (visibleChild == null) {
385: elem.setVisibility(false);
386: elem.setTrait(SVGConstants.SVG_VISIBILITY_ATTRIBUTE,
387: "hidden"); //NOI18N
388: }
389: }
390: return visibleChild;
391: }
392:
393: public static SVGElement findElementById(SVGSVGElement root,
394: String id) {
395: SVGElement elem = getElementById((ModelNode) root, id);
396: return elem;
397: }
398:
399: protected static SVGElement getElementById(ModelNode node, String id) {
400: if (node instanceof SVGElement) {
401: SVGElement svgElem = (SVGElement) node;
402: if (id.equals(svgElem.getId())) {
403: return svgElem;
404: }
405: }
406:
407: ModelNode child = node.getFirstChildNode();
408: while (child != null) {
409: SVGElement res = getElementById(child, id);
410: if (res != null) {
411: return res;
412: }
413: child = child.getNextSiblingNode();
414: }
415: return null;
416: }
417:
418: public int getAnimatorState() {
419: return m_animationState;
420: }
421:
422: public boolean isAnimatorStarted() {
423: return m_animationState == ANIMATION_RUNNING
424: || m_animationState == ANIMATION_PAUSED;
425: }
426:
427: public void startAnimator() {
428: if (m_animationState == ANIMATION_NOT_RUNNING
429: || m_animationState == ANIMATION_PAUSED) {
430: if (m_animator.getState() != SVGAnimatorImpl.STATE_PLAYING) {
431: m_animator.play();
432: m_sceneMgr.processEvent(new AWTEvent(this ,
433: SceneManager.EVENT_ANIM_STARTED) {
434: });
435: }
436: m_animationState = ANIMATION_RUNNING;
437: m_sceneMgr.getScreenManager().repaint();
438: }
439: }
440:
441: public void getFocusableTargets(List<String> focusableTargets) {
442: SVGElement root = getSVGRootElement();
443: Set<String> ids = new HashSet<String>();
444: collectFocusableElements(root, ids);
445: orderFocusableElements(root, ids, focusableTargets);
446: focusableTargets.add(0, null);
447: }
448:
449: public void pauseAnimator() {
450: if (m_animationState == ANIMATION_RUNNING) {
451: if (m_animator.getState() == SVGAnimatorImpl.STATE_PLAYING) {
452: m_animator.pause();
453: }
454: m_animationState = ANIMATION_PAUSED;
455: }
456: }
457:
458: public void stopAnimator() {
459: if (m_animationState == ANIMATION_RUNNING
460: || m_animationState == ANIMATION_PAUSED) {
461: m_animator.stop();
462: m_animator.play();
463: m_animator.pause();
464: setAnimatorTime(0);
465: m_animationState = ANIMATION_NOT_RUNNING;
466: m_sceneMgr.processEvent(new AWTEvent(this ,
467: SceneManager.EVENT_ANIM_STOPPED) {
468: });
469: m_sceneMgr.getScreenManager().repaint();
470: }
471: }
472:
473: public float getAnimatorTime() {
474: try {
475: if (m_animator.getState() != SVGAnimatorImpl.STATE_STOPPED) {
476: m_animator.invokeAndWait(new Runnable() {
477: public void run() {
478: m_currentTime = getSVGRootElement()
479: .getCurrentTime();
480: m_sceneMgr
481: .updateAnimationDuration(m_currentTime);
482: }
483: });
484: } else {
485: m_currentTime = 0;
486: }
487: } catch (InterruptedException ex) {
488: SceneManager.error(toString() + " - Wait interrupted.", ex); //NOI18N
489: }
490: return m_currentTime;
491: }
492:
493: public void setAnimatorTime(float time) {
494: if (m_animator != null) {
495: m_currentTime = time;
496: m_sceneMgr.updateAnimationDuration(m_currentTime);
497: if (m_animator.getState() != SVGAnimatorImpl.STATE_STOPPED) {
498: m_animator.invokeLater(new Runnable() {
499: public void run() {
500: getSVGRootElement().setCurrentTime(
501: m_currentTime);
502: }
503: });
504: } else {
505: SceneManager.log(Level.SEVERE, toString()
506: + " - Animator is stopped."); //NOI18N
507: }
508: }
509: }
510:
511: public void startAnimation(String id) {
512: SVGElement elem = getElementById(id);
513: if (elem != null && elem instanceof SVGAnimationElement) {
514: ((SVGAnimationElement) elem).beginElementAt(0);
515: } else {
516: SceneManager.log(Level.SEVERE, toString()
517: + " - Animation element not found: " + id); //NOI18N
518: }
519: }
520:
521: public void stopAnimation(String id) {
522: SVGElement elem = getElementById(id);
523: if (elem != null && elem instanceof SVGAnimationElement) {
524: ((SVGAnimationElement) elem).endElementAt(0);
525: } else {
526: SceneManager.log(Level.SEVERE, toString()
527: + " - Animation element not found: " + id); //NOI18N
528: }
529: }
530:
531: public String toString() {
532: return m_id;
533: }
534:
535: private synchronized SVGObject getSVGObject(SVGLocatableElement elem) {
536: assert elem != null : "Element must not be null"; //NOI18N
537: if (elem instanceof PatchedElement) {
538: PatchedElement pelem = (PatchedElement) elem;
539: SVGObject obj = pelem.getSVGObject();
540: if (obj == null) {
541: obj = new SVGObject(m_sceneMgr, elem);
542: pelem.attachSVGObject(obj);
543: }
544: return obj;
545: } else {
546: SceneManager.log(Level.SEVERE, toString()
547: + " - PatchedElement must be used instead of "
548: + elem.getClass().getName()); //NOI18N
549: return null;
550: }
551: }
552:
553: public static ModelNode getRootElement(ModelNode node) {
554: ModelNode parent;
555:
556: while ((parent = node.getParent()) != null) {
557: node = parent;
558: }
559: return node;
560: }
561:
562: public static SVGImage createImage(InputStream stream)
563: throws IOException {
564: if (stream == null) {
565: throw new NullPointerException();
566: }
567:
568: SVGImage img = loadDocument(stream, null);
569:
570: // Now, get image width/height from <svg> element and set it in
571: // DocumentNode
572: DocumentNode docNode = (DocumentNode) img.getDocument();
573: Element root = docNode.getDocumentElement();
574: if (!(root instanceof SVG)) {
575: SceneManager.log(Level.SEVERE, "Missing SVG root element"); //NOI18N
576: throw new IOException("Missing SVG root element"); //NOI18N
577: }
578:
579: SVG svg = (SVG) root;
580: int width = (int) svg.getWidth();
581: int height = (int) svg.getHeight();
582: docNode.setSize(width, height);
583:
584: return img;
585: }
586:
587: protected static SVGImage loadDocument(final InputStream is,
588: final ExternalResourceHandler handler) throws IOException {
589:
590: DocumentNode documentNode = new DocumentNode();
591: UpdateAdapter updateAdapter = new UpdateAdapter();
592: documentNode.setUpdateListener(updateAdapter);
593:
594: //long t = System.currentTimeMillis();
595: ModelBuilder
596: .loadDocument(is, documentNode,
597: SVGComposerPrototypeFactory
598: .getPrototypes(documentNode));
599: //System.out.println("Load document took " + (System.currentTimeMillis() - t) + "[ms]");
600:
601: if (updateAdapter.hasLoadingFailed()) {
602: if (updateAdapter.getLoadingFailedException() != null) {
603: throw new IOException(updateAdapter
604: .getLoadingFailedException().getMessage());
605: }
606: throw new IOException("Loading of SVG document failed.");
607: }
608:
609: SVGImageImpl img = new SVGImageImpl(documentNode, null);
610:
611: // Now, initialize the timing engine and sample at zero.
612: documentNode.initializeTimingEngine();
613: documentNode.sample(new Time(0));
614:
615: return img;
616: }
617:
618: protected static int getChildrenCount(ModelNode node) {
619: int count = 0;
620: ModelNode child = node.getFirstChildNode();
621: while (child != null) {
622: count++;
623: child = child.getNextSiblingNode();
624: }
625: return count;
626: }
627:
628: protected static void transferChildren(SVGElement target,
629: SVGElement source) {
630: ModelNode child = ((ModelNode) source).getFirstChildNode();
631: while (child != null) {
632: if (child instanceof PatchedElement) {
633: PatchedElement pe = (PatchedElement) child;
634: child = child.getNextSiblingNode();
635: setNullIds((SVGElement) pe, true);
636: source.removeChild((Node) pe);
637: target.appendChild((Node) pe);
638: setNullIds((SVGElement) pe, false);
639: } else {
640: SceneManager.log(Level.SEVERE,
641: "PatchedElement must be used instead of "
642: + child.getClass().getName()); //NOI18N
643: }
644: }
645: }
646:
647: public static void printTree(ModelNode node, int level) {
648: for (int i = 0; i < level; i++) {
649: System.out.print(" "); //NOI18N
650: }
651: System.out.println(node.getClass());
652: ModelNode child = node.getFirstChildNode();
653: while (child != null) {
654: printTree(child, level + 1);
655: child = child.getNextSiblingNode();
656: }
657: }
658:
659: /*
660: * Collect all element ids referenced by animation begin trait.
661: */
662: private void collectFocusableElements(SVGElement elem,
663: Set<String> elemIds) {
664: if (elem instanceof PatchedAnimationElement) {
665: String beginTrait = elem
666: .getTrait(SVGConstants.SVG_BEGIN_ATTRIBUTE);
667: for (String pattern : ANIM_PATTERNS) {
668: int p = 0;
669: while ((p = beginTrait.indexOf(pattern, p)) != -1) {
670: int i = p - 1;
671: while (i >= 0
672: && isElementIdChar(beginTrait.charAt(i))) {
673: i--;
674: }
675: String id = beginTrait.substring(i + 1, p);
676: if (getElementById(id) != null) {
677: elemIds.add(id);
678: }
679:
680: p += pattern.length();
681: }
682: }
683: }
684:
685: SVGElement child = (SVGElement) elem.getFirstElementChild();
686: while (child != null) {
687: collectFocusableElements(child, elemIds);
688: child = (SVGElement) child.getNextElementSibling();
689: }
690: }
691:
692: /*
693: * Order element ids by their occurence in SVG document.
694: */
695: private boolean orderFocusableElements(SVGElement elem,
696: Set<String> ids, List<String> orderedIds) {
697: String id = elem.getId();
698: if (id != null) {
699: if (ids.remove(id)) {
700: orderedIds.add(id);
701: if (ids.isEmpty()) {
702: return true;
703: }
704: }
705: }
706: SVGElement child = (SVGElement) elem.getFirstElementChild();
707: while (child != null) {
708: if (orderFocusableElements(child, ids, orderedIds)) {
709: return true;
710: }
711: child = (SVGElement) child.getNextElementSibling();
712: }
713: return false;
714: }
715:
716: private boolean containsAnimation(ModelNode node) {
717: if (node instanceof AbstractAnimate) {
718: return true;
719: }
720:
721: ModelNode child = node.getFirstChildNode();
722: while (child != null) {
723: if (containsAnimation(child)) {
724: return true;
725: } else {
726: child = child.getNextSiblingNode();
727: }
728: }
729: return false;
730: }
731:
732: public static boolean isElementIdChar(char c) {
733: return Character.isLetter(c) || Character.isDigit(c)
734: || c == '.' || c == '_' || c == '-' || c == ':';
735: }
736:
737: public static void setNullIds(SVGElement elem, boolean isNull) {
738: if (elem instanceof PatchedElement) {
739: ((PatchedElement) elem).setNullId(isNull);
740: } else if (elem.getId() != null) {
741: SceneManager.log(Level.SEVERE,
742: "PatchedElement must be used instead of "
743: + elem.getClass().getName()); //NOI18N
744: }
745:
746: SVGElement child = (SVGElement) elem.getFirstElementChild();
747: while (child != null) {
748: setNullIds(child, isNull);
749: child = (SVGElement) child.getNextElementSibling();
750: }
751: }
752:
753: public static boolean isViewBoxMarker(ModelNode node) {
754: return node instanceof SVGElement
755: && ID_VIEWBOX_MARKER
756: .equals(((SVGElement) node).getId());
757: }
758:
759: public static SVGRect getSafeScreenBBox(SVGLocatableElement elem) {
760: SVGRect bBox = elem.getScreenBBox();
761: if (bBox == null) {
762: //TODO solve the issue with null bounding box
763: SceneManager.log(Level.SEVERE,
764: "Null screen BBox for element:" + elem); //NOI18N
765: ModelNode child = ((ModelNode) elem).getFirstChildNode();
766: if (child != null && child instanceof SVGLocatableElement) {
767: bBox = ((SVGLocatableElement) child).getScreenBBox();
768: }
769: }
770: return bBox;
771: }
772:
773: public static SVGRect getSafeBBox(SVGLocatableElement elem) {
774: SVGRect bBox = elem.getBBox();
775: if (bBox == null) {
776: //TODO solve the issue with null bounding box
777: SceneManager.log(Level.SEVERE, "Null BBox for element:"
778: + elem); //NOI18N
779: ModelNode child = ((ModelNode) elem).getFirstChildNode();
780: if (child != null && child instanceof SVGLocatableElement) {
781: bBox = ((SVGLocatableElement) child).getBBox();
782: }
783: }
784: return bBox;
785: }
786:
787: protected static Document getOwnerDocument(Node elem) {
788: Node parent;
789:
790: while ((parent = elem.getParentNode()) != null) {
791: elem = parent;
792: }
793: return (Document) elem;
794: }
795:
796: public static SVGMatrix getParentTransformation(Node node) {
797: List<SVGMatrix> transforms = null;
798:
799: while (node != null) {
800: if ((node instanceof PatchedTransformableElement)) {
801: Transform temp = ((PatchedTransformableElement) node)
802: .getTransform();
803: if (temp != null) {
804: if (transforms == null) {
805: transforms = new ArrayList<SVGMatrix>();
806: }
807: transforms.add(new Transform(temp));
808: }
809: }
810: node = node.getParentNode();
811: }
812: SVGMatrix total = null;
813:
814: if (transforms != null) {
815: total = transforms.get(0);
816: for (int i = 1; i < transforms.size(); i++) {
817: total = total.mMultiply(transforms.get(i));
818: }
819: }
820:
821: return total;
822: }
823:
824: public static final boolean isIdentityTransform(SVGMatrix matrix,
825: boolean ignoreTranslate) {
826: int length = ignoreTranslate ? 4 : 6;
827: for (int i = 0; i < length; i++) {
828: if (Math.abs(IDENTITY_TRANSFORM.getComponent(i)
829: - matrix.getComponent(i)) > 0.001) {
830: return false;
831: }
832: }
833: return true;
834: }
835: }
|