| /* |
| |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| package org.apache.batik.anim.timing; |
| |
| import java.util.Calendar; |
| import java.util.LinkedList; |
| |
| import org.apache.batik.util.DoublyIndexedSet; |
| |
| /** |
| * An abstract base class for the root time container element |
| * for a document. |
| * |
| * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a> |
| * @version $Id$ |
| */ |
| public abstract class TimedDocumentRoot extends TimeContainer { |
| |
| /** |
| * The wallclock time that the document began. |
| */ |
| protected Calendar documentBeginTime; |
| |
| /** |
| * Allows the use of accessKey() timing specifiers with a single |
| * character, as specified in SVG 1.1. |
| */ |
| protected boolean useSVG11AccessKeys; |
| |
| /** |
| * Allows the use of accessKey() timing specifiers with a DOM 3 |
| * key name, as specified in SVG 1.2. |
| */ |
| protected boolean useSVG12AccessKeys; |
| |
| /** |
| * A set to determine when propagation of new Instance times should |
| * be stopped. |
| */ |
| protected DoublyIndexedSet propagationFlags = new DoublyIndexedSet(); |
| |
| /** |
| * List of {link TimegraphListener}s to be notified of changes to the |
| * timed elements in this document. |
| */ |
| protected LinkedList listeners = new LinkedList(); |
| |
| /** |
| * Whether the document is currently being sampled. |
| */ |
| protected boolean isSampling; |
| |
| /** |
| * Whether the document is currently being sampled for a hyperlink. |
| */ |
| protected boolean isHyperlinking; |
| |
| /** |
| * Creates a new TimedDocumentRoot. |
| * @param useSVG11AccessKeys allows the use of accessKey() timing |
| * specifiers with a single character |
| * @param useSVG12AccessKeys allows the use of accessKey() with a |
| * DOM 3 key name |
| */ |
| public TimedDocumentRoot(boolean useSVG11AccessKeys, |
| boolean useSVG12AccessKeys) { |
| root = this; |
| this.useSVG11AccessKeys = useSVG11AccessKeys; |
| this.useSVG12AccessKeys = useSVG12AccessKeys; |
| } |
| |
| /** |
| * Returns the implicit duration of the element. The document root |
| * has an {@link #INDEFINITE} implicit duration. |
| */ |
| protected float getImplicitDur() { |
| return INDEFINITE; |
| } |
| |
| /** |
| * Returns the default begin time for the given child |
| * timed element. In SVG, this is always 0, since the |
| * only time container is the root SVG element, which acts |
| * like a 'par'. |
| */ |
| public float getDefaultBegin(TimedElement child) { |
| return 0.0f; |
| } |
| |
| /** |
| * Returns the last sampled document time. |
| */ |
| public float getCurrentTime() { |
| return lastSampleTime; |
| } |
| |
| /** |
| * Returns whether the document is currently being sampled. |
| */ |
| public boolean isSampling() { |
| return isSampling; |
| } |
| |
| /** |
| * Returns whether the document is currently being sampled for a hyperlink. |
| */ |
| public boolean isHyperlinking() { |
| return isHyperlinking; |
| } |
| |
| /** |
| * Samples the entire timegraph at the given time. |
| */ |
| public float seekTo(float time, boolean hyperlinking) { |
| // Trace.enter(this, "seekTo", new Object[] { Float.valueOf(time) } ); try { |
| isSampling = true; |
| lastSampleTime = time; |
| isHyperlinking = hyperlinking; |
| propagationFlags.clear(); |
| // No time containers in SVG, so we don't have to worry |
| // about a partial ordering of timed elements to sample. |
| float mint = Float.POSITIVE_INFINITY; |
| TimedElement[] es = getChildren(); |
| for (TimedElement e1 : es) { |
| float t = e1.sampleAt(time, hyperlinking); |
| if (t < mint) { |
| mint = t; |
| } |
| } |
| boolean needsUpdates; |
| do { |
| needsUpdates = false; |
| for (TimedElement e : es) { |
| if (e.shouldUpdateCurrentInterval) { |
| needsUpdates = true; |
| // System.err.print("{" + ((Test.AnimateElement) es[i]).id + "} "); |
| float t = e.sampleAt(time, hyperlinking); |
| if (t < mint) { |
| mint = t; |
| } |
| } |
| } |
| } while (needsUpdates); |
| isSampling = false; |
| if (hyperlinking) { |
| root.currentIntervalWillUpdate(); |
| } |
| return mint; |
| // } finally { Trace.exit(); } |
| } |
| |
| /** |
| * Resets the entire timegraph. |
| */ |
| public void resetDocument(Calendar documentBeginTime) { |
| if (documentBeginTime == null) { |
| this.documentBeginTime = Calendar.getInstance(); |
| } else { |
| this.documentBeginTime = documentBeginTime; |
| } |
| reset(true); |
| } |
| |
| /** |
| * Returns the wallclock time that the document began. |
| */ |
| public Calendar getDocumentBeginTime() { |
| return documentBeginTime; |
| } |
| |
| /** |
| * Converts an epoch time to document time. |
| */ |
| public float convertEpochTime(long t) { |
| long begin = documentBeginTime.getTime().getTime(); |
| return (t - begin) / 1000f; |
| } |
| |
| /** |
| * Converts a wallclock time to document time. |
| */ |
| public float convertWallclockTime(Calendar time) { |
| long begin = documentBeginTime.getTime().getTime(); |
| long t = time.getTime().getTime(); |
| return (t - begin) / 1000f; |
| } |
| |
| /** |
| * Adds a {@link TimegraphListener} to the document. |
| */ |
| public void addTimegraphListener(TimegraphListener l) { |
| listeners.add(l); |
| } |
| |
| /** |
| * Removes a {@link TimegraphListener} from the document. |
| */ |
| public void removeTimegraphListener(TimegraphListener l) { |
| listeners.remove(l); |
| } |
| |
| /** |
| * Fires an {@link TimegraphListener#elementAdded} event on all |
| * timegraph listeners. |
| */ |
| void fireElementAdded(TimedElement e) { |
| for (Object listener : listeners) { |
| ((TimegraphListener) listener).elementAdded(e); |
| } |
| } |
| |
| /** |
| * Fires an {@link TimegraphListener#elementRemoved} event on all |
| * timegraph listeners. |
| */ |
| void fireElementRemoved(TimedElement e) { |
| for (Object listener : listeners) { |
| ((TimegraphListener) listener).elementRemoved(e); |
| } |
| } |
| |
| // XXX Add fire* methods for the other events in TimegraphListener, and make |
| // TimedElement fire them. |
| |
| /** |
| * Returns whether the specified newly created {@link Interval} should |
| * propagate its times to the given {@link TimingSpecifier}. |
| * @param i the Interval that has just been created |
| * @param ts the TimingSpecifier that is a dependent of the Interval |
| * @param isBegin whether the dependency is on the begin or end time of |
| * the Interval |
| */ |
| boolean shouldPropagate(Interval i, TimingSpecifier ts, boolean isBegin) { |
| InstanceTime it = isBegin ? i.getBeginInstanceTime() |
| : i.getEndInstanceTime(); |
| if (propagationFlags.contains(it, ts)) { |
| return false; |
| } |
| propagationFlags.add(it, ts); |
| return true; |
| } |
| |
| /** |
| * Invoked by timed elements in this document to indicate that the current |
| * interval will be re-evaluated at the next sample. This should be |
| * overridden in a concrete class so that ticks can be scheduled immediately |
| * if they are currently paused due to no animations being active. |
| */ |
| protected void currentIntervalWillUpdate() { |
| } |
| |
| /** |
| * Returns the namespace URI of the event that corresponds to the given |
| * animation event name. |
| */ |
| protected abstract String getEventNamespaceURI(String eventName); |
| |
| /** |
| * Returns the type of the event that corresponds to the given |
| * animation event name. |
| */ |
| protected abstract String getEventType(String eventName); |
| |
| /** |
| * Returns the name of the repeat event. |
| * @return either "repeat" or "repeatEvent" |
| */ |
| protected abstract String getRepeatEventName(); |
| } |