254 lines
7.1 KiB
Java
254 lines
7.1 KiB
Java
/*
|
|
ZZUpdateManager.java
|
|
*
|
|
* Copyright (c) 2000, Ted Nelson and Tuomas Lukka
|
|
*
|
|
* You may use and distribute under the terms of either the GNU Lesser
|
|
* General Public License, either version 2 of the license or,
|
|
* at your choice, any later version. Alternatively, you may use and
|
|
* distribute under the terms of the XPL.
|
|
*
|
|
* See the LICENSE.lgpl and LICENSE.xpl files for the specific terms of
|
|
* the licenses.
|
|
*
|
|
* This software is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the README
|
|
* file for more details.
|
|
*
|
|
*/
|
|
/*
|
|
* Written by Tuomas Lukka
|
|
*/
|
|
package org.gzigzag;
|
|
import java.util.*;
|
|
|
|
/** A single global instance to manage the updating of views.
|
|
* The views are set in a priority order so that even if the user is
|
|
* moving fast, he will get an immediate response through the less important
|
|
* views not being updated at each step.
|
|
*/
|
|
|
|
public class ZZUpdateManager implements Runnable {
|
|
public static final String rcsid = "$Id: ZZUpdateManager.java,v 1.29 2001/02/27 08:36:15 raulir Exp $";
|
|
public static boolean dbg = false;
|
|
private static void p(String s) { if(dbg) ZZLogger.log(s); }
|
|
private static void pa(String s) { ZZLogger.log(s); }
|
|
|
|
/** The order of views, from the most important to the least.
|
|
*/
|
|
private static Vector ordering = new Vector();
|
|
|
|
/** Whether an update is currently in progress, through
|
|
* a view being painted.
|
|
*/
|
|
private static boolean updating;
|
|
private static int disabled;
|
|
private static boolean noanimation;
|
|
/** Whether the currently ongoing update should stop immediately
|
|
* and restart from the beginning
|
|
*/
|
|
private static boolean restartUpd = false;
|
|
|
|
public static void addView(ZZView v) { ordering.addElement(v); }
|
|
public static void rmView(ZZView v) { ordering.removeElement(v); }
|
|
|
|
/** Set the view that has the privilege of animating the next
|
|
* update.
|
|
* Setting this to null will make all views update fast.
|
|
*/
|
|
public static void setFast(ZZView v) {
|
|
p("Setfast: "+v + " cur: "+ordering.elementAt(0));
|
|
if(v == null) {
|
|
noanimation = true;
|
|
return;
|
|
}
|
|
if(ordering.elementAt(0)!=v) {
|
|
ordering.removeElement(v);
|
|
ordering.insertElementAt(v, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/* Called by a space to inform that some cells have been
|
|
* changed
|
|
*/
|
|
public static void chg() {
|
|
p("UPDMANAGER CHG");
|
|
if(restartUpd) return;
|
|
synchronized(ordering) {
|
|
restartUpd = true;
|
|
ordering.notifyAll();
|
|
}
|
|
p("UPDMANAGER CHGOUT");
|
|
}
|
|
|
|
/** Disable all updating of views.
|
|
* XXX While frozen, might consider updating e.g. once every
|
|
* second...
|
|
*/
|
|
public static void freeze() {
|
|
p("ZZUpdatemanager freeze");
|
|
synchronized(ordering) { disabled++; }
|
|
p("ZZUpdatemanager frozen "+disabled);
|
|
}
|
|
/** Enable again the updating of views. */
|
|
public static void thaw() {
|
|
p("ZZUpdatemanager thaw");
|
|
synchronized(ordering) {
|
|
if(disabled<=0)
|
|
throw new ZZError("thaw() without matching freeze()!");
|
|
if(--disabled==0) ordering.notifyAll();
|
|
}
|
|
p("ZZUpdatemanager thawed "+disabled);
|
|
}
|
|
|
|
static ZZUpdateManager m = new ZZUpdateManager();
|
|
static Thread t= new Thread(m);
|
|
static {
|
|
p("STARTORDTHREAD");
|
|
t.start();
|
|
}
|
|
|
|
public void run() {
|
|
try {
|
|
int ind = -1;
|
|
boolean reras = true;
|
|
boolean anim = false;
|
|
Vector curUpdVec = new Vector();
|
|
|
|
while(true) {
|
|
p("STARTORD");
|
|
synchronized(ordering) {
|
|
p("STARTORD SYNCHED");
|
|
if(!anim) ind++;
|
|
updating = false;
|
|
try {
|
|
while(disabled>0 || (ind>=curUpdVec.size() && !restartUpd)) {
|
|
p("STARTORD "+disabled+" "+ind+" "+restartUpd);
|
|
ordering.wait();
|
|
}
|
|
} catch(Exception e) {
|
|
ZZLogger.exc(e);
|
|
}
|
|
p("STARTORD WAITED");
|
|
updating = true;
|
|
if(restartUpd) {
|
|
restartUpd = false;
|
|
curUpdVec = (Vector)ordering.clone();
|
|
ind = 0;
|
|
reras = true;
|
|
// Try to make the system stop less at
|
|
// inconvenient, unequal times.
|
|
|
|
// System.gc(); // doesn't seem to work.
|
|
}
|
|
if(curUpdVec.size()==0) continue;
|
|
}
|
|
try {
|
|
p("STARTORD DOING " + ind + " "+ curUpdVec+" "+reras);
|
|
ZZView v = (ZZView)curUpdVec.elementAt(ind);
|
|
if(ind==0) { // animate if need be
|
|
if(reras) {
|
|
timeNewCycle();
|
|
|
|
anim = v.reraster();
|
|
reras = false;
|
|
}
|
|
float fract = timeAnimFract();
|
|
|
|
if(fract >= 1 || !anim || noanimation) {
|
|
fract = 1;
|
|
anim = false;
|
|
noanimation = false;
|
|
}
|
|
p("PN 0: "+fract+" "+anim+" "+lastStart+" "+millis);
|
|
// XXX paintNow() is not really safe here, because ZZ code
|
|
// in general isn't thread-safe. If Kaffe someday provides
|
|
// EventQueue.invokeAndWait(), this could be fixed.
|
|
boolean care = ZZDrawing.instance.enableQuality(false);
|
|
v.paintNow((float)fract);
|
|
if (fract == 1 && care) {
|
|
ZZDrawing.instance.enableQuality(true);
|
|
v.paintNow(1.0f);
|
|
}
|
|
} else {
|
|
p("PN: "+ind);
|
|
v.reraster();
|
|
// XXX Ditto.
|
|
boolean care = ZZDrawing.instance.enableQuality(false);
|
|
v.paintNow(1.0f);
|
|
if (care) {
|
|
ZZDrawing.instance.enableQuality(true);
|
|
v.paintNow(1.0f);
|
|
}
|
|
}
|
|
((java.awt.Component)v).getToolkit().sync();
|
|
} catch(ZZError e) {
|
|
ZZLogger.exc(e);
|
|
System.out.println("EXCEPTION WHILE UPDATING!");
|
|
}
|
|
p("STARTORD DONE: anim "+anim);
|
|
}
|
|
} finally {
|
|
System.out.println("HELP! UPDATE LOOP STOPPED. XXX");
|
|
}
|
|
}
|
|
|
|
static final long defmillis = 800;
|
|
/** The time before the next user event
|
|
* to try to be ready.
|
|
*/
|
|
static final long trybefore = 150;
|
|
/** The time to increment the interval with.
|
|
*/
|
|
static final long inctime = 80;
|
|
private static long lastStart = 0;
|
|
/** The interval between start and end of animation.
|
|
*/
|
|
private static long millis = defmillis;
|
|
|
|
/** Called when we begin a new animation. */
|
|
private void timeNewCycle() {
|
|
long cur = System.currentTimeMillis();
|
|
// Interval between start of last animation and start of the new one.
|
|
long sinceLast = cur-lastStart;
|
|
|
|
// sinceLast - millis = interval between end of last animation and
|
|
// start of the new one. We try to end the last animation
|
|
// trybefore ticks before the start of the new one, so if
|
|
// sinceLast - millis < trybefore, the animation takes too long.
|
|
if(sinceLast - millis < trybefore)
|
|
millis = sinceLast-trybefore; // Shorten the animation time.
|
|
|
|
// If the animation didn't take too long, and if it's at least a unit
|
|
// (inctime) shorter than it was by default, gradually lengthen it.
|
|
else if(millis < defmillis - inctime)
|
|
millis += inctime;
|
|
|
|
lastStart = cur;
|
|
}
|
|
|
|
private float timeAnimFract() {
|
|
long cur = System.currentTimeMillis();
|
|
|
|
double fract = 1.0;
|
|
|
|
// If we are animating at all (20 milliseconds is
|
|
// minimum time we try to use animation with),
|
|
// calculate the fraction of the animation we are at.
|
|
if(millis > 20)
|
|
fract = (cur-lastStart) / (double)millis;
|
|
|
|
// Test for a really weird JVM problem.
|
|
if(cur - lastStart >= millis && fract < 1) {
|
|
System.out.println("AAAARRRRRGGGHHH!!! Division problem "+ cur + " "+lastStart+" "+millis);
|
|
}
|
|
|
|
return (float)fract;
|
|
}
|
|
|
|
}
|
|
|
|
|