3 import java.awt.Component;
4 import java.awt.Dimension;
5 import java.awt.Graphics;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.io.BufferedInputStream;
10 import java.io.BufferedReader;
11 import java.io.ByteArrayInputStream;
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.io.InputStreamReader;
15 import java.util.stream.Collectors;
17 import javax.imageio.ImageIO;
18 import javax.swing.Timer;
21 * A set of generally useful SwingJS-related methods. Includes:
23 * alternatives to using getCodeBase() for loading resources, due to issues in
24 * Eclipse setting that incorrectly (but no problem in JavaScript)
31 public class SwingJSUtils {
33 * Set the dimension for the applet prior to j2sApplet's call to
34 * run the applet. Must be used to create a static field:
37 * private static Dimension dim =
41 * Then, if it is desired also to have Java also set this, add
43 * if (dim != null) setSize(dim);
45 * to the applet's init() method.
49 * @return the Dimension
53 public static Dimension setDim(int w, int h) {
54 String baseURI = (/** @j2sNative document.body.baseURI || */
56 boolean isTest = (baseURI == null || baseURI.indexOf("_applet.html") >= 0);
62 * J2S.thisApplet.__Info.width = w; J2S.thisApplet.__Info.height = h;
64 return new Dimension(w, h);
68 * Reliably load a resource of a specific type from the code directory
70 * adaptable - here we are returning an image or a string
72 * @param cl the classname of the object to return (Image.class,
73 * String.class) null for InputStream
79 public static Object getResource(Class<?> baseClass, String filename, Class<?> cl) {
80 System.out.println("mpUtils.SwingJSUtils.getResource " + baseClass.getCanonicalName() + " " + filename);
81 InputStream is = baseClass.getResourceAsStream(filename);
82 if (cl == Image.class) {
84 return ImageIO.read(is);
85 } catch (IOException e) {
88 } else if (cl == String.class) {
89 return new BufferedReader(new InputStreamReader(is)).lines().collect(Collectors.joining("\n"));
95 * Pre-fetch images during the static entry of the class. This should provide
96 * plenty of clock ticks, since the file transfer is synchronous, and all we are
97 * waiting for is the DOM image object to initialize.
105 public static void loadImagesStatic(Class<?> cl, Image[] images, String root, String ext, int nImages) {
106 for (int i = nImages; --i >= 0;) {
108 // Bild laden und beim MediaTracker registrieren
109 // MediaTracker ladekontrolle = new MediaTracker(this);
111 // BH SwingJS -- adding generally useful method for loading data
112 // avoiding the use of getCodeBase(), which for some reason does not work in
115 images[i] = (Image) getResource(cl, root + i + "." + ext, Image.class);
118 // * $("body").append(images[i]._imgNode);
121 // ladekontrolle.addImage(scharf[i],i);
122 // Warten , bis Bild ganz geladen ist
124 // try {ladekontrolle.waitForID(i);}
125 // catch (InterruptedException e)
131 * Fill an array with images based on a String[] listing
132 * @param cl reference class
133 * @param root optional root path, ending in "/"
134 * @param names source file names
135 * @param images array to fill
137 public static void loadImagesStatic(Class<?> cl, String root, String[] names, Image[] images) {
138 for (int i = names.length; --i >= 0;) {
139 images[i] = (Image) getResource(cl, root + names[i], Image.class);
144 * Eclipse-friendly image getting
150 public static Image getImage(Component c, String fileName) {
151 return getImage(c.getClass(), fileName);
155 * Eclipse-friendly image getting
161 public static Image getImage(Class<?> c, String fileName) {
162 return (Image) getResource(c, fileName, Image.class);
166 * Clear the component graphic. BH added this for JavaScript because changing
167 * the browser zoom can change the size of the canvas for unknown reasons.
171 public static void clearComponent(Component c) {
172 Graphics gc = c.getGraphics();
173 gc.clearRect(0, 0, c.getWidth(), c.getHeight());
179 * A simple interface to the machine loop, generally of the form
181 * public boolean stateLoop() {
182 * while (stateHepler.isAlive()) {
183 * switch (stateHelper.getState()) {
186 * return stateHelper.delayState(100,STATE_YYY);
189 * stateHelper.setState(STATE_ZZZ);
193 * return stateHelper.delayAction(100, MY_ID, "myCommand", myListener, STATE_XXX); *
196 * stateHelper.interrupt();
207 public interface StateMachine {
209 public boolean stateLoop();
213 * StateHelper is a class that facilitates moving from an asychronous multithreaded model to a state-oriented model of programming
214 * for SwingJS animations and other asynchronous business.
219 public static class StateHelper {
221 public static final int UNCHANGED = Integer.MIN_VALUE;
223 private StateMachine machine;
227 private boolean interrupted;
230 public StateHelper(StateMachine machine) {
231 this.machine = machine;
234 public void interrupt() {
238 public boolean isInterrupted() {
242 public boolean isAlive() {
246 public void restart() {
250 public void setState(int state) {
251 this.state = this.stateNext = state;
254 public int getState() {
258 public void setLevel(int level) {
259 this.level = this.levelNext = level;
262 public int getLevel() {
266 public void setNextState(int next) {
270 public int getNextState() {
274 public int getNextLevel() {
278 public void setNextLevel(int next) {
284 * NOTE: this method must remain private; it is accessed via p$1
288 private boolean nextState() {
289 return next(stateNext, levelNext);
292 * Set the state and run machine.stateLoop().
294 * @param state something meaningful to the machine
296 * @return not interrupted
298 * @author Bob Hanson hansonr@stolaf.edu
300 public boolean next(int state) {
301 return next(state, 0);
305 * Set the state and level, and then run machine.stateLoop(). Driven directly or via delayedState or delayedAction
307 * @param state something meaningful to the machine
308 * @param level something meaningful to the machine
310 * @return not interrupted
312 * @author Bob Hanson hansonr@stolaf.edu
314 public boolean next(int state, int level) {
315 return nextStatePriv(this, state, level);
318 private static boolean nextStatePriv(Object oThis, int state, int level) {
319 StateHelper me = (StateHelper) oThis;
322 if (level != UNCHANGED)
324 if (state != UNCHANGED)
326 return me.machine.stateLoop();
330 * After the given number of milliseseconds, set the new state and run the machines stateLoop with unchanged level
332 * @param ms the number of milliseconds to delay; 0 to execute synchronously *
333 * @param stateNext the next state to run
334 * @return not interrupted
336 * @author Bob Hanson hansonr@stolaf.edu
338 public boolean delayedState(int ms, int stateNext) {
339 return delayedState(ms, stateNext, level);
342 private Timer stateTimer;
344 private int stateNext;
345 private int levelNext;
348 * After the given number of milliseseconds, set the new state and level, and
349 * run the machines stateLoop
351 * @param ms the number of milliseconds to delay; 0 to execute
353 * @param stateNext the next state
354 * @param levelNext the next level
355 * @return not interrupted
357 * @author Bob Hanson hansonr@stolaf.edu
360 public boolean delayedState(int ms, int stateNext, int levelNext) {
364 return next(stateNext, levelNext);
365 if (stateNext != UNCHANGED)
366 this.stateNext = stateNext;
367 if (levelNext != UNCHANGED)
368 this.levelNext = levelNext;
373 * setTimeout(function(){
374 * p$1.nextState.apply(me, []);
380 if (stateTimer == null) {
381 stateTimer = new Timer(ms, new ActionListener() {
383 public void actionPerformed(ActionEvent e) {
388 stateTimer.setRepeats(false);
391 stateTimer.restart();
398 * Fire an actionPerformed event after a given number of milliseconds
400 * @param ms delay milliseconds. if 0, then this action will be called
402 * @param id id for this event, possibly ACTION_PERFORMED (1001), but not
404 * @param command key for ActionEvent.getCommand()
405 * @param listener ActionListener to be called.
406 * @return not interrupted
408 * @author Bob Hanson hansonr@stolaf.edu
410 public boolean delayedAction(int ms, int id, String command, ActionListener listener) {
411 return delayedAction(ms, id, command, listener, UNCHANGED, UNCHANGED);
415 * Fire an actionPerformed event after a given number of milliseconds
417 * @param ms delay milliseconds. if 0, then this action will be called
419 * @param id id for this event, possibly ACTION_PERFORMED (1001), but not
421 * @param command key for ActionEvent.getCommand()
422 * @param listener ActionListener to be called.
424 * @param state the next state to go to after this listener is called; UNCHANGED to let the listener take care of this.
426 * @return not interrupted
428 * @author Bob Hanson hansonr@stolaf.edu
430 public boolean delayedAction(int ms, int id, String command, ActionListener listener, int state) {
431 return delayedAction(ms, id, command, listener, state, UNCHANGED);
435 * Fire an actionPerformed event after a given number of milliseconds. Setting BOTH stateNext and levelNext to UNCHANGED (Integer.MIN_VALUE)
436 * allows the listener to handle continuing the loop.
438 * @param ms delay milliseconds. if 0, then this action will be called
440 * @param id id for this event, possibly ACTION_PERFORMED (1001), but not
442 * @param command key for ActionEvent.getCommand()
443 * @param listener ActionListener to be called.
444 * @param stateNext state to run after the event is processed by the listener, or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle this.
445 * @param levelNext level to run after the event is processed by the listener, or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle this.
446 * @return not interrupted
448 * @author Bob Hanson hansonr@stolaf.edu
450 public boolean delayedAction(int ms, int id, String command, ActionListener listener, int stateNext, int levelNext) {
453 ActionEvent event = new ActionEvent(this, id, command);
455 listener.actionPerformed(event);
456 return (stateNext == UNCHANGED && levelNext == UNCHANGED || nextStatePriv(this, stateNext == UNCHANGED ? state : stateNext, levelNext == UNCHANGED ? level : levelNext));
459 StateHelper me = this;
461 Timer timer = new Timer(ms, id == ActionEvent.ACTION_PERFORMED ? listener : new ActionListener() {
463 public void actionPerformed(ActionEvent e) {
465 listener.actionPerformed(event);
466 if (!interrupted && (stateNext != UNCHANGED || levelNext != UNCHANGED))
467 nextStatePriv(me, stateNext == UNCHANGED ? state : stateNext, levelNext == UNCHANGED ? level : levelNext);
471 timer.setRepeats(false);
476 public static void delayedRun(int ms, Runnable runnable) {
477 new StateHelper(null).delayedRun(ms, runnable, UNCHANGED, UNCHANGED);
482 * Fire an actionPerformed event after a given number of milliseconds. Setting
483 * BOTH stateNext and levelNext to UNCHANGED (Integer.MIN_VALUE) allows the
484 * listener to handle continuing the loop.
486 * @param ms delay milliseconds. if 0, then this action will be called
488 * @param id id for this event, possibly ACTION_PERFORMED (1001), but not
490 * @param command key for ActionEvent.getCommand()
491 * @param listener ActionListener to be called.
492 * @param stateNext state to run after the event is processed by the listener,
493 * or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle
495 * @param levelNext level to run after the event is processed by the listener,
496 * or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle
498 * @return not interrupted
500 * @author Bob Hanson hansonr@stolaf.edu
502 public boolean delayedRun(int ms, Runnable runnable, int stateNext, int levelNext) {
506 return (stateNext == UNCHANGED && levelNext == UNCHANGED || nextStateIfUnchanged(this, runnable,
507 stateNext == UNCHANGED ? state : stateNext, levelNext == UNCHANGED ? level : levelNext));
509 StateHelper me = this;
513 * setTimeout(function() {
515 * me.nextStateIfUnchanged$O$O$I$I.apply(me, [me, runnable, stateNext, levelNext]);
520 Timer timer = new Timer(ms, new ActionListener() {
522 public void actionPerformed(ActionEvent e) {
523 nextStateIfUnchanged(me, runnable, stateNext, levelNext);
527 timer.setRepeats(false);
533 protected boolean nextStateIfUnchanged(Object oThis, Object runnable, int stateNext, int levelNext) {
534 StateHelper me = (StateHelper)(oThis);
536 ((Runnable) runnable).run();
537 if (!me.interrupted && (stateNext != UNCHANGED || levelNext != UNCHANGED))
538 nextStatePriv(oThis, stateNext == UNCHANGED ? me.state : stateNext,
539 levelNext == UNCHANGED ? me.level : levelNext);
544 * sleep and then execute the next state
547 public void sleep(int ms) {
548 int next = stateNext;
549 delayedState(ms, next);
554 * open a "url-like" input stream
559 public static BufferedInputStream openStream(Class<?> base, String fileName) {
560 String s = (String) getResource(base, fileName, String.class);
561 return new BufferedInputStream(new ByteArrayInputStream(s.getBytes()));
565 public static class Performance {
567 public final static int TIME_RESET = 0;
569 public final static int TIME_MARK = 1;
571 public static final int TIME_SET = 2;
573 public static final int TIME_GET = 3;
575 public static long time, mark, set, duration;
580 * Performance.timeCheck(null, Platform.TIME_MARK);
584 * Performance.timeCheck("some message", Platform.TIME_MARK);
586 * reset...[set/mark]n...get (total time) (time spent between set and mark)
588 * set...get (total time) (time spent between set and get)
590 * long t0 = now(0); ........ ; dt = now(t0); (time since t0)e
595 public static void timeCheck(String msg, int mode) {
596 msg = timeCheckStr(msg, mode);
598 System.err.println(msg);
601 public static long now(long t) {
602 return System.currentTimeMillis() - t;
605 public static String timeCheckStr(String msg, int mode) {
606 long t = System.currentTimeMillis();
612 return ("Platform: timer reset\t\t\t" + msg);
622 // total time between set/mark points
623 duration += (t - set);
631 return ("Platform: timer mark\t" + ((t - time) / 1000f) + "\t" + ((t - m0) / 1000f) + "\t"
641 return ("Platform: timer get\t" + ((t - time) / 1000f) + "\t" + ((duration) / 1000f) + "\t" + msg);