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.Shape;
022: import java.awt.geom.AffineTransform;
023: import java.util.Collection;
024: import java.util.ArrayList;
025: import java.util.Collections;
026: import java.util.Iterator;
027: import java.util.LinkedList;
028: import java.util.List;
029: import java.util.Timer;
030: import java.util.TimerTask;
031:
032: import org.apache.batik.bridge.svg12.DefaultXBLManager;
033: import org.apache.batik.bridge.svg12.SVG12BridgeContext;
034: import org.apache.batik.bridge.svg12.SVG12ScriptingEnvironment;
035: import org.apache.batik.dom.events.AbstractEvent;
036: import org.apache.batik.dom.svg.SVGOMDocument;
037: import org.apache.batik.gvt.GraphicsNode;
038: import org.apache.batik.gvt.RootGraphicsNode;
039: import org.apache.batik.gvt.UpdateTracker;
040: import org.apache.batik.gvt.renderer.ImageRenderer;
041: import org.apache.batik.util.EventDispatcher;
042: import org.apache.batik.util.XMLConstants;
043: import org.apache.batik.util.EventDispatcher.Dispatcher;
044: import org.apache.batik.util.RunnableQueue;
045: import org.w3c.dom.Document;
046: import org.w3c.dom.events.DocumentEvent;
047: import org.w3c.dom.events.EventTarget;
048:
049: /**
050: * This class provides features to manage the update of an SVG document.
051: *
052: * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
053: * @version $Id: UpdateManager.java 501843 2007-01-31 13:52:12Z deweese $
054: */
055: public class UpdateManager {
056:
057: static final int MIN_REPAINT_TIME;
058: static {
059: int value = 20;
060: try {
061: String s = System.getProperty(
062: "org.apache.batik.min_repaint_time", "20");
063: value = Integer.parseInt(s);
064: } catch (SecurityException se) {
065: } catch (NumberFormatException nfe) {
066: } finally {
067: MIN_REPAINT_TIME = value;
068: }
069: }
070:
071: /**
072: * The bridge context.
073: */
074: protected BridgeContext bridgeContext;
075:
076: /**
077: * The document to manage.
078: */
079: protected Document document;
080:
081: /**
082: * The update RunnableQueue.
083: */
084: protected RunnableQueue updateRunnableQueue;
085:
086: /**
087: * The RunHandler for the RunnableQueue.
088: */
089: protected RunnableQueue.RunHandler runHandler;
090:
091: /**
092: * Whether the update manager is running.
093: */
094: protected volatile boolean running;
095:
096: /**
097: * Whether the suspend() method was called.
098: */
099: protected volatile boolean suspendCalled;
100:
101: /**
102: * The listeners.
103: */
104: protected List listeners = Collections
105: .synchronizedList(new LinkedList());
106:
107: /**
108: * The scripting environment.
109: */
110: protected ScriptingEnvironment scriptingEnvironment;
111:
112: /**
113: * The repaint manager.
114: */
115: protected RepaintManager repaintManager;
116:
117: /**
118: * The update tracker.
119: */
120: protected UpdateTracker updateTracker;
121:
122: /**
123: * The GraphicsNode whose updates are to be tracked.
124: */
125: protected GraphicsNode graphicsNode;
126:
127: /**
128: * Whether the manager was started.
129: */
130: protected boolean started;
131:
132: /**
133: * Array of resource documents' BridgeContexts.
134: */
135: protected BridgeContext[] secondaryBridgeContexts;
136:
137: /**
138: * Array of resource documents' ScriptingEnvironments that should
139: * have their SVGLoad event dispatched.
140: */
141: protected ScriptingEnvironment[] secondaryScriptingEnvironments;
142:
143: /**
144: * The current minRepaintTime
145: */
146: protected int minRepaintTime;
147:
148: /**
149: * Creates a new update manager.
150: * @param ctx The bridge context.
151: * @param gn GraphicsNode whose updates are to be tracked.
152: * @param doc The document to manage.
153: */
154: public UpdateManager(BridgeContext ctx, GraphicsNode gn,
155: Document doc) {
156: bridgeContext = ctx;
157: bridgeContext.setUpdateManager(this );
158:
159: document = doc;
160:
161: updateRunnableQueue = RunnableQueue.createRunnableQueue();
162: runHandler = createRunHandler();
163: updateRunnableQueue.setRunHandler(runHandler);
164:
165: graphicsNode = gn;
166:
167: scriptingEnvironment = initializeScriptingEnvironment(bridgeContext);
168:
169: // Any BridgeContexts for resource documents that exist
170: // when initializing the scripting environment for the
171: // primary document also need to have their scripting
172: // environments initialized.
173: secondaryBridgeContexts = (BridgeContext[]) ctx
174: .getChildContexts().clone();
175: secondaryScriptingEnvironments = new ScriptingEnvironment[secondaryBridgeContexts.length];
176: for (int i = 0; i < secondaryBridgeContexts.length; i++) {
177: BridgeContext resCtx = secondaryBridgeContexts[i];
178: if (!((SVGOMDocument) resCtx.getDocument()).isSVG12()) {
179: continue;
180: }
181: resCtx.setUpdateManager(this );
182: ScriptingEnvironment se = initializeScriptingEnvironment(resCtx);
183: secondaryScriptingEnvironments[i] = se;
184: }
185: minRepaintTime = MIN_REPAINT_TIME;
186: }
187:
188: public int getMinRepaintTime() {
189: return minRepaintTime;
190: }
191:
192: public void setMinRepaintTime(int minRepaintTime) {
193: this .minRepaintTime = minRepaintTime;
194: }
195:
196: /**
197: * Creates an appropriate ScriptingEnvironment and XBL manager for
198: * the given document.
199: */
200: protected ScriptingEnvironment initializeScriptingEnvironment(
201: BridgeContext ctx) {
202: SVGOMDocument d = (SVGOMDocument) ctx.getDocument();
203: ScriptingEnvironment se;
204: if (d.isSVG12()) {
205: se = new SVG12ScriptingEnvironment(ctx);
206: ctx.xblManager = new DefaultXBLManager(d, ctx);
207: d.setXBLManager(ctx.xblManager);
208: } else {
209: se = new ScriptingEnvironment(ctx);
210: }
211: return se;
212: }
213:
214: /**
215: * Dispatches an 'SVGLoad' event to the document.
216: */
217: public synchronized void dispatchSVGLoadEvent()
218: throws InterruptedException {
219: dispatchSVGLoadEvent(bridgeContext, scriptingEnvironment);
220: for (int i = 0; i < secondaryScriptingEnvironments.length; i++) {
221: BridgeContext ctx = secondaryBridgeContexts[i];
222: if (!((SVGOMDocument) ctx.getDocument()).isSVG12()) {
223: continue;
224: }
225: ScriptingEnvironment se = secondaryScriptingEnvironments[i];
226: dispatchSVGLoadEvent(ctx, se);
227: }
228: secondaryBridgeContexts = null;
229: secondaryScriptingEnvironments = null;
230: }
231:
232: /**
233: * Dispatches an 'SVGLoad' event to the document.
234: */
235: protected void dispatchSVGLoadEvent(BridgeContext ctx,
236: ScriptingEnvironment se) {
237: se.loadScripts();
238: se.dispatchSVGLoadEvent();
239: if (ctx.isSVG12() && ctx.xblManager != null) {
240: SVG12BridgeContext ctx12 = (SVG12BridgeContext) ctx;
241: ctx12.addBindingListener();
242: ctx12.xblManager.startProcessing();
243: }
244: }
245:
246: /**
247: * Dispatches an "SVGZoom" event to the document.
248: */
249: public void dispatchSVGZoomEvent() throws InterruptedException {
250: scriptingEnvironment.dispatchSVGZoomEvent();
251: }
252:
253: /**
254: * Dispatches an "SVGZoom" event to the document.
255: */
256: public void dispatchSVGScrollEvent() throws InterruptedException {
257: scriptingEnvironment.dispatchSVGScrollEvent();
258: }
259:
260: /**
261: * Dispatches an "SVGZoom" event to the document.
262: */
263: public void dispatchSVGResizeEvent() throws InterruptedException {
264: scriptingEnvironment.dispatchSVGResizeEvent();
265: }
266:
267: /**
268: * Finishes the UpdateManager initialization.
269: */
270: public void manageUpdates(final ImageRenderer r) {
271: updateRunnableQueue.preemptLater(new Runnable() {
272: public void run() {
273: synchronized (UpdateManager.this ) {
274: running = true;
275:
276: updateTracker = new UpdateTracker();
277: RootGraphicsNode root = graphicsNode.getRoot();
278: if (root != null) {
279: root
280: .addTreeGraphicsNodeChangeListener(updateTracker);
281: }
282:
283: repaintManager = new RepaintManager(r);
284:
285: // Send the UpdateManagerStarted event.
286: UpdateManagerEvent ev = new UpdateManagerEvent(
287: UpdateManager.this , null, null);
288: fireEvent(startedDispatcher, ev);
289: started = true;
290: }
291: }
292: });
293: resume();
294: }
295:
296: /**
297: * Returns the bridge context.
298: */
299: public BridgeContext getBridgeContext() {
300: return bridgeContext;
301: }
302:
303: /**
304: * Returns the update RunnableQueue.
305: */
306: public RunnableQueue getUpdateRunnableQueue() {
307: return updateRunnableQueue;
308: }
309:
310: /**
311: * Returns the repaint manager.
312: */
313: public RepaintManager getRepaintManager() {
314: return repaintManager;
315: }
316:
317: /**
318: * Returns the GVT update tracker.
319: */
320: public UpdateTracker getUpdateTracker() {
321: return updateTracker;
322: }
323:
324: /**
325: * Returns the current Document.
326: */
327: public Document getDocument() {
328: return document;
329: }
330:
331: /**
332: * Returns the scripting environment.
333: */
334: public ScriptingEnvironment getScriptingEnvironment() {
335: return scriptingEnvironment;
336: }
337:
338: /**
339: * Tells whether the update manager is currently running.
340: */
341: public synchronized boolean isRunning() {
342: return running;
343: }
344:
345: /**
346: * Suspends the update manager.
347: */
348: public synchronized void suspend() {
349: // System.err.println("Suspend: " + suspendCalled + " : " + running);
350: if (updateRunnableQueue.getQueueState() == RunnableQueue.RUNNING) {
351: updateRunnableQueue.suspendExecution(false);
352: }
353: suspendCalled = true;
354: }
355:
356: /**
357: * Resumes the update manager.
358: */
359: public synchronized void resume() {
360: // System.err.println("Resume: " + suspendCalled + " : " + running);
361:
362: // if (suspendCalled) {
363: // UpdateManagerEvent ev = new UpdateManagerEvent
364: // (this, null, null);
365: // // FIXX: Must happen in a different thread!
366: // fireEvent(suspendedDispatcher, ev);
367: // fireEvent(resumedDispatcher, ev);
368: // }
369: if (updateRunnableQueue.getQueueState() != RunnableQueue.RUNNING) {
370: updateRunnableQueue.resumeExecution();
371: }
372: }
373:
374: /**
375: * Interrupts the manager tasks.
376: */
377: public void interrupt() {
378: Runnable r = new Runnable() {
379: public void run() {
380: synchronized (UpdateManager.this ) {
381: if (started) {
382: dispatchSVGUnLoadEvent();
383: } else {
384: running = false;
385: scriptingEnvironment.interrupt();
386: updateRunnableQueue.getThread().halt();
387: }
388: }
389: }
390: };
391: try {
392: // Preempt to cancel the pending tasks
393: updateRunnableQueue.preemptLater(r);
394: updateRunnableQueue.resumeExecution(); // ensure runnable runs...
395: } catch (IllegalStateException ise) {
396: // Not running, which is probably ok since that's what we
397: // wanted. Might be an issue if SVGUnload wasn't issued...
398: }
399: }
400:
401: /**
402: * Dispatches an 'SVGUnLoad' event to the document.
403: * This method interrupts the update manager threads.
404: * NOTE: this method must be called outside the update thread.
405: */
406: public void dispatchSVGUnLoadEvent() {
407: if (!started) {
408: throw new IllegalStateException(
409: "UpdateManager not started.");
410: }
411:
412: // Invoke first to cancel the pending tasks
413: updateRunnableQueue.preemptLater(new Runnable() {
414: public void run() {
415: synchronized (UpdateManager.this ) {
416: AbstractEvent evt = (AbstractEvent) ((DocumentEvent) document)
417: .createEvent("SVGEvents");
418: String type;
419: if (bridgeContext.isSVG12()) {
420: type = "unload";
421: } else {
422: type = "SVGUnload";
423: }
424: evt.initEventNS(
425: XMLConstants.XML_EVENTS_NAMESPACE_URI,
426: type, false, // canBubbleArg
427: false); // cancelableArg
428: ((EventTarget) (document.getDocumentElement()))
429: .dispatchEvent(evt);
430: running = false;
431:
432: // Now shut everything down and disconnect
433: // everything before we send the
434: // UpdateMangerStopped event.
435: scriptingEnvironment.interrupt();
436: updateRunnableQueue.getThread().halt();
437: bridgeContext.dispose();
438:
439: // Send the UpdateManagerStopped event.
440: UpdateManagerEvent ev = new UpdateManagerEvent(
441: UpdateManager.this , null, null);
442: fireEvent(stoppedDispatcher, ev);
443: }
444: }
445: });
446: resume();
447: }
448:
449: /**
450: * Updates the rendering buffer. Only to be called from the
451: * update thread.
452: * @param u2d The user to device transform.
453: * @param dbr Whether the double buffering should be used.
454: * @param aoi The area of interest in the renderer space units.
455: * @param width The offscreen buffer width.
456: * @param height The offscreen buffer height.
457: */
458: public void updateRendering(AffineTransform u2d, boolean dbr,
459: Shape aoi, int width, int height) {
460: repaintManager.setupRenderer(u2d, dbr, aoi, width, height);
461: List l = new ArrayList(1);
462: l.add(aoi);
463: updateRendering(l, false);
464: }
465:
466: /**
467: * Updates the rendering buffer. Only to be called from the
468: * update thread.
469: * @param u2d The user to device transform.
470: * @param dbr Whether the double buffering should be used.
471: * @param cpt If the canvas painting transform should be cleared
472: * when the update complets
473: * @param aoi The area of interest in the renderer space units.
474: * @param width The offscreen buffer width.
475: * @param height The offscreen buffer height.
476: */
477: public void updateRendering(AffineTransform u2d, boolean dbr,
478: boolean cpt, Shape aoi, int width, int height) {
479: repaintManager.setupRenderer(u2d, dbr, aoi, width, height);
480: List l = new ArrayList(1);
481: l.add(aoi);
482: updateRendering(l, cpt);
483: }
484:
485: /**
486: * Updates the rendering buffer.
487: * @param areas List of areas of interest in rederer space units.
488: * @param clearPaintingTransform Indicates if the painting transform
489: * should be cleared as a result of this update.
490: */
491: protected void updateRendering(List areas,
492: boolean clearPaintingTransform) {
493: try {
494: UpdateManagerEvent ev = new UpdateManagerEvent(this ,
495: repaintManager.getOffScreen(), null);
496: fireEvent(updateStartedDispatcher, ev);
497:
498: Collection c = repaintManager.updateRendering(areas);
499: List l = new ArrayList(c);
500:
501: ev = new UpdateManagerEvent(this , repaintManager
502: .getOffScreen(), l, clearPaintingTransform);
503: fireEvent(updateCompletedDispatcher, ev);
504: } catch (ThreadDeath td) {
505: UpdateManagerEvent ev = new UpdateManagerEvent(this , null,
506: null);
507: fireEvent(updateFailedDispatcher, ev);
508: throw td;
509: } catch (Throwable t) {
510: UpdateManagerEvent ev = new UpdateManagerEvent(this , null,
511: null);
512: fireEvent(updateFailedDispatcher, ev);
513: }
514: }
515:
516: /**
517: * This tracks when the rendering first got 'out of date'
518: * with respect to the document.
519: */
520: long outOfDateTime = 0;
521:
522: /**
523: * Repaints the dirty areas, if needed.
524: */
525: protected void repaint() {
526: if (!updateTracker.hasChanged()) {
527: // No changes, nothing to repaint.
528: outOfDateTime = 0;
529: return;
530: }
531:
532: long ctime = System.currentTimeMillis();
533: if (ctime < allResumeTime) {
534: createRepaintTimer();
535: return;
536: }
537: if (allResumeTime > 0) {
538: // All suspendRedraw requests have expired.
539: releaseAllRedrawSuspension();
540: }
541:
542: if (ctime - outOfDateTime < minRepaintTime) {
543: // We very recently did a repaint check if other
544: // repaint runnables are pending.
545: synchronized (updateRunnableQueue.getIteratorLock()) {
546: Iterator i = updateRunnableQueue.iterator();
547: while (i.hasNext())
548: if (!(i.next() instanceof NoRepaintRunnable))
549: // have a pending repaint runnable so we
550: // will skip this repaint and we will let
551: // the next one pick it up.
552: return;
553:
554: }
555: }
556:
557: List dirtyAreas = updateTracker.getDirtyAreas();
558: updateTracker.clear();
559: if (dirtyAreas != null) {
560: updateRendering(dirtyAreas, false);
561: }
562: outOfDateTime = 0;
563: }
564:
565: /**
566: * Users of Batik should essentially never call
567: * this directly from Java. If the Canvas is not
568: * updating when you change the SVG Document it is almost
569: * certainly because you are not making your changes
570: * in the RunnableQueue (getUpdateRunnableQueue()).
571: * You will have problems if you are not making all
572: * changes to the document in the UpdateManager's
573: * RunnableQueue.
574: *
575: * This method exists to implement the
576: * 'SVGSVGElement.forceRedraw()' method.
577: */
578: public void forceRepaint() {
579: if (!updateTracker.hasChanged()) {
580: // No changes, nothing to repaint.
581: outOfDateTime = 0;
582: return;
583: }
584:
585: List dirtyAreas = updateTracker.getDirtyAreas();
586: updateTracker.clear();
587: if (dirtyAreas != null) {
588: updateRendering(dirtyAreas, false);
589: }
590: outOfDateTime = 0;
591: }
592:
593: protected class SuspensionInfo {
594: /**
595: * The index of this redraw suspension
596: */
597: int index;
598: /**
599: * The system time in millisec that this suspension
600: * will expire and redraws can resume (at least for
601: * this suspension.
602: */
603: long resumeMilli;
604:
605: public SuspensionInfo(int index, long resumeMilli) {
606: this .index = index;
607: this .resumeMilli = resumeMilli;
608: }
609:
610: public int getIndex() {
611: return index;
612: }
613:
614: public long getResumeMilli() {
615: return resumeMilli;
616: }
617: }
618:
619: protected class RepaintTimerTask extends TimerTask {
620: UpdateManager um;
621:
622: RepaintTimerTask(UpdateManager um) {
623: this .um = um;
624: }
625:
626: public void run() {
627: RunnableQueue rq = um.getUpdateRunnableQueue();
628: if (rq == null)
629: return;
630: rq.invokeLater(new Runnable() {
631: public void run() {
632: }
633: });
634: }
635: }
636:
637: List suspensionList = new ArrayList();
638: int nextSuspensionIndex = 1;
639: long allResumeTime = -1;
640: Timer repaintTriggerTimer = null;
641: TimerTask repaintTimerTask = null;
642:
643: void createRepaintTimer() {
644: if (repaintTimerTask != null)
645: return;
646: if (allResumeTime < 0)
647: return;
648: if (repaintTriggerTimer == null)
649: repaintTriggerTimer = new Timer(true);
650:
651: long delay = allResumeTime - System.currentTimeMillis();
652: if (delay < 0)
653: delay = 0;
654: repaintTimerTask = new RepaintTimerTask(this );
655: repaintTriggerTimer.schedule(repaintTimerTask, delay);
656: // System.err.println("CTimer delay: " + delay);
657: }
658:
659: /**
660: * Sets up a timer that will trigger a repaint
661: * when it fires.
662: * If create is true it will construct a timer even
663: * if one
664: */
665: void resetRepaintTimer() {
666: if (repaintTimerTask == null)
667: return;
668: if (allResumeTime < 0)
669: return;
670: if (repaintTriggerTimer == null)
671: repaintTriggerTimer = new Timer(true);
672:
673: long delay = allResumeTime - System.currentTimeMillis();
674: if (delay < 0)
675: delay = 0;
676: repaintTimerTask = new RepaintTimerTask(this );
677: repaintTriggerTimer.schedule(repaintTimerTask, delay);
678: // System.err.println("Timer delay: " + delay);
679: }
680:
681: int addRedrawSuspension(int max_wait_milliseconds) {
682: long resumeTime = System.currentTimeMillis()
683: + max_wait_milliseconds;
684: SuspensionInfo si = new SuspensionInfo(nextSuspensionIndex++,
685: resumeTime);
686: if (resumeTime > allResumeTime) {
687: allResumeTime = resumeTime;
688: // System.err.println("Added AllRes Time: " + allResumeTime);
689: resetRepaintTimer();
690: }
691: suspensionList.add(si);
692: return si.getIndex();
693: }
694:
695: void releaseAllRedrawSuspension() {
696: suspensionList.clear();
697: allResumeTime = -1;
698: resetRepaintTimer();
699: }
700:
701: boolean releaseRedrawSuspension(int index) {
702: if (index > nextSuspensionIndex)
703: return false;
704: if (suspensionList.size() == 0)
705: return true;
706:
707: int lo = 0, hi = suspensionList.size() - 1;
708: while (lo < hi) {
709: int mid = (lo + hi) >> 1;
710: SuspensionInfo si = (SuspensionInfo) suspensionList
711: .get(mid);
712: int idx = si.getIndex();
713: if (idx == index) {
714: lo = hi = mid;
715: } else if (idx < index) {
716: lo = mid + 1;
717: } else {
718: hi = mid - 1;
719: }
720: }
721:
722: SuspensionInfo si = (SuspensionInfo) suspensionList.get(lo);
723: int idx = si.getIndex();
724: if (idx != index)
725: return true; // currently not in list but was at some point...
726:
727: suspensionList.remove(lo);
728: if (suspensionList.size() == 0) {
729: // No more active suspensions
730: allResumeTime = -1;
731: resetRepaintTimer();
732: } else {
733: // Check if we need to find a new 'bounding' suspension.
734: long resumeTime = si.getResumeMilli();
735: if (resumeTime == allResumeTime) {
736: allResumeTime = findNewAllResumeTime();
737: // System.err.println("New AllRes Time: " + allResumeTime);
738: resetRepaintTimer();
739: }
740: }
741: return true;
742: }
743:
744: long findNewAllResumeTime() {
745: long ret = -1;
746: Iterator i = suspensionList.iterator();
747: while (i.hasNext()) {
748: SuspensionInfo si = (SuspensionInfo) i.next();
749: long t = si.getResumeMilli();
750: if (t > ret)
751: ret = t;
752: }
753: return ret;
754: }
755:
756: /**
757: * Adds a UpdateManagerListener to this UpdateManager.
758: */
759: public void addUpdateManagerListener(UpdateManagerListener l) {
760: listeners.add(l);
761: }
762:
763: /**
764: * Removes a UpdateManagerListener from this UpdateManager.
765: */
766: public void removeUpdateManagerListener(UpdateManagerListener l) {
767: listeners.remove(l);
768: }
769:
770: protected void fireEvent(Dispatcher dispatcher, Object event) {
771: EventDispatcher.fireEvent(dispatcher, listeners, event, false);
772: }
773:
774: /**
775: * Dispatches a UpdateManagerEvent to notify that the manager was
776: * started
777: */
778: static Dispatcher startedDispatcher = new Dispatcher() {
779: public void dispatch(Object listener, Object event) {
780: ((UpdateManagerListener) listener)
781: .managerStarted((UpdateManagerEvent) event);
782: }
783: };
784:
785: /**
786: * Dispatches a UpdateManagerEvent to notify that the manager was
787: * stopped.
788: */
789: static Dispatcher stoppedDispatcher = new Dispatcher() {
790: public void dispatch(Object listener, Object event) {
791: ((UpdateManagerListener) listener)
792: .managerStopped((UpdateManagerEvent) event);
793: }
794: };
795:
796: /**
797: * Dispatches a UpdateManagerEvent to notify that the manager was
798: * suspended.
799: */
800: static Dispatcher suspendedDispatcher = new Dispatcher() {
801: public void dispatch(Object listener, Object event) {
802: ((UpdateManagerListener) listener)
803: .managerSuspended((UpdateManagerEvent) event);
804: }
805: };
806:
807: /**
808: * Dispatches a UpdateManagerEvent to notify that the manager was
809: * resumed.
810: */
811: static Dispatcher resumedDispatcher = new Dispatcher() {
812: public void dispatch(Object listener, Object event) {
813: ((UpdateManagerListener) listener)
814: .managerResumed((UpdateManagerEvent) event);
815: }
816: };
817:
818: /**
819: * Dispatches a UpdateManagerEvent to notify that an update
820: * started
821: */
822: static Dispatcher updateStartedDispatcher = new Dispatcher() {
823: public void dispatch(Object listener, Object event) {
824: ((UpdateManagerListener) listener)
825: .updateStarted((UpdateManagerEvent) event);
826: }
827: };
828:
829: /**
830: * Dispatches a UpdateManagerEvent to notify that an update
831: * completed
832: */
833: static Dispatcher updateCompletedDispatcher = new Dispatcher() {
834: public void dispatch(Object listener, Object event) {
835: ((UpdateManagerListener) listener)
836: .updateCompleted((UpdateManagerEvent) event);
837: }
838: };
839:
840: /**
841: * Dispatches a UpdateManagerEvent to notify that an update
842: * failed
843: */
844: static Dispatcher updateFailedDispatcher = new Dispatcher() {
845: public void dispatch(Object listener, Object event) {
846: ((UpdateManagerListener) listener)
847: .updateFailed((UpdateManagerEvent) event);
848: }
849: };
850:
851: // RunnableQueue.RunHandler /////////////////////////////////////////
852: protected RunnableQueue.RunHandler createRunHandler() {
853: return new UpdateManagerRunHander();
854: }
855:
856: protected class UpdateManagerRunHander extends
857: RunnableQueue.RunHandlerAdapter {
858:
859: public void runnableStart(RunnableQueue rq, Runnable r) {
860: if (running && !(r instanceof NoRepaintRunnable)) {
861: // Mark the document as updated when the
862: // runnable starts.
863: if (outOfDateTime == 0)
864: outOfDateTime = System.currentTimeMillis();
865: }
866: }
867:
868: /**
869: * Called when the given Runnable has just been invoked and
870: * has returned.
871: */
872: public void runnableInvoked(RunnableQueue rq, Runnable r) {
873: if (running && !(r instanceof NoRepaintRunnable)) {
874: repaint();
875: }
876: }
877:
878: /**
879: * Called when the execution of the queue has been suspended.
880: */
881: public void executionSuspended(RunnableQueue rq) {
882: synchronized (UpdateManager.this ) {
883: // System.err.println("Suspended: " + suspendCalled);
884: if (suspendCalled) {
885: running = false;
886: UpdateManagerEvent ev = new UpdateManagerEvent(
887: this , null, null);
888: fireEvent(suspendedDispatcher, ev);
889: }
890: }
891: }
892:
893: /**
894: * Called when the execution of the queue has been resumed.
895: */
896: public void executionResumed(RunnableQueue rq) {
897: synchronized (UpdateManager.this ) {
898: // System.err.println("Resumed: " + suspendCalled +
899: // " : " + running);
900: if (suspendCalled && !running) {
901: running = true;
902: suspendCalled = false;
903:
904: UpdateManagerEvent ev = new UpdateManagerEvent(
905: this, null, null);
906: fireEvent(resumedDispatcher, ev);
907: }
908: }
909: }
910: }
911: }
|