1 /*
2 * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
166 if (paused) {
167 final long deltaTime = now() - pauseTime;
168 startTime += deltaTime;
169 paused = false;
170 addPulseReceiver();
171 }
172 }
173
174 // package private only for the sake of testing
175 final PulseReceiver pulseReceiver = new PulseReceiver() {
176 @Override public void timePulse(long now) {
177 final long elapsedTime = now - startTime;
178 if (elapsedTime < 0) {
179 return;
180 }
181 if (accessCtrlCtx == null) {
182 throw new IllegalStateException("Error: AccessControlContext not captured");
183 }
184
185 AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
186 impl_timePulse(elapsedTime);
187 return null;
188 }, accessCtrlCtx);
189 }
190 };
191
192 private class CurrentRateProperty extends ReadOnlyDoublePropertyBase {
193 private double value;
194
195 @Override
196 public Object getBean() {
197 return Animation.this;
198 }
199
200 @Override
201 public String getName() {
202 return "currentRate";
203 }
204
205 @Override
206 public double get() {
294
295 public final DoubleProperty rateProperty() {
296 if (rate == null) {
297 rate = new DoublePropertyBase(DEFAULT_RATE) {
298
299 @Override
300 public void invalidated() {
301 final double newRate = getRate();
302 if (isRunningEmbedded()) {
303 if (isBound()) {
304 unbind();
305 }
306 set(oldRate);
307 throw new IllegalArgumentException("Cannot set rate of embedded animation while running.");
308 } else {
309 if (Math.abs(newRate) < EPSILON) {
310 if (getStatus() == Status.RUNNING) {
311 lastPlayedForward = (Math.abs(getCurrentRate()
312 - oldRate) < EPSILON);
313 }
314 setCurrentRate(0.0);
315 pauseReceiver();
316 } else {
317 if (getStatus() == Status.RUNNING) {
318 final double currentRate = getCurrentRate();
319 if (Math.abs(currentRate) < EPSILON) {
320 setCurrentRate(lastPlayedForward ? newRate : -newRate);
321 resumeReceiver();
322 } else {
323 final boolean playingForward = Math.abs(currentRate - oldRate) < EPSILON;
324 setCurrentRate(playingForward ? newRate : -newRate);
325 }
326 }
327 oldRate = newRate;
328 }
329 clipEnvelope.setRate(newRate);
330 }
331 }
332
333 @Override
334 public Object getBean() {
335 return Animation.this;
336 }
337
338 @Override
339 public String getName() {
340 return "rate";
341 }
342 };
343 }
344 return rate;
349 return false;
350 }
351 return parent.getStatus() != Status.STOPPED || parent.isRunningEmbedded();
352 }
353
354 private double oldRate = 1.0;
355 /**
356 * Read-only variable to indicate current direction/speed at which the
357 * {@code Animation} is being played.
358 * <p>
359 * {@code currentRate} is not necessary equal to {@code rate}.
360 * {@code currentRate} is set to {@code 0.0} when animation is paused or
361 * stopped. {@code currentRate} may also point to different direction during
362 * reverse cycles when {@code autoReverse} is {@code true}
363 *
364 * @defaultValue 0.0
365 */
366 private ReadOnlyDoubleProperty currentRate;
367 private static final double DEFAULT_CURRENT_RATE = 0.0;
368
369 private void setCurrentRate(double value) {
370 if ((currentRate != null) || (Math.abs(value - DEFAULT_CURRENT_RATE) > EPSILON)) {
371 ((CurrentRateProperty)currentRateProperty()).set(value);
372 }
373 }
374
375 public final double getCurrentRate() {
376 return (currentRate == null)? DEFAULT_CURRENT_RATE : currentRate.get();
377 }
378
379 public final ReadOnlyDoubleProperty currentRateProperty() {
380 if (currentRate == null) {
381 currentRate = new CurrentRateProperty();
382 }
383 return currentRate;
384 }
385
386 /**
387 * Read-only variable to indicate the duration of one cycle of this
388 * {@code Animation}: the time it takes to play from time 0 to the
389 * end of the Animation (at the default {@code rate} of
887 * <code>
888 * animation.setRate(negative rate);<br>
889 * animation.jumpTo(overall duration of animation);<br>
890 * animation.play();<br>
891 * </code>
892 * <p>
893 * Note: <ul>
894 * <li>{@code play()} is an asynchronous call, the {@code Animation} may not
895 * start immediately. </ul>
896 *
897 * @throws IllegalStateException
898 * if embedded in another animation,
899 * such as {@link SequentialTransition} or {@link ParallelTransition}
900 */
901 public void play() {
902 if (parent != null) {
903 throw new IllegalStateException("Cannot start when embedded in another animation");
904 }
905 switch (getStatus()) {
906 case STOPPED:
907 if (impl_startable(true)) {
908 final double rate = getRate();
909 if (lastPlayedFinished) {
910 jumpTo((rate < 0)? getTotalDuration() : Duration.ZERO);
911 }
912 impl_start(true);
913 startReceiver(TickCalculation.fromDuration(getDelay()));
914 if (Math.abs(rate) < EPSILON) {
915 pauseReceiver();
916 } else {
917
918 }
919 } else {
920 final EventHandler<ActionEvent> handler = getOnFinished();
921 if (handler != null) {
922 handler.handle(new ActionEvent(this, null));
923 }
924 }
925 break;
926 case PAUSED:
927 impl_resume();
928 if (Math.abs(getRate()) >= EPSILON) {
929 resumeReceiver();
930 }
931 break;
932 }
933 }
934
935 /**
936 * Plays an {@code Animation} from initial position in forward direction.
937 * <p>
938 * It is equivalent to
939 * <p>
940 * <code>
941 * animation.stop();<br>
942 * animation.setRate = setRate(Math.abs(animation.getRate())); </br>
943 * animation.jumpTo(Duration.ZERO);<br>
944 * animation.play();<br>
945 * </code>
946 *
947 * <p>
961 play();
962 }
963
964 /**
965 * Stops the animation and resets the play head to its initial position. If
966 * the animation is not currently running, this method has no effect.
967 * <p>
968 * Note: <ul>
969 * <li>{@code stop()} is an asynchronous call, the {@code Animation} may not stop
970 * immediately. </ul>
971 * @throws IllegalStateException
972 * if embedded in another animation,
973 * such as {@link SequentialTransition} or {@link ParallelTransition}
974 */
975 public void stop() {
976 if (parent != null) {
977 throw new IllegalStateException("Cannot stop when embedded in another animation");
978 }
979 if (getStatus() != Status.STOPPED) {
980 clipEnvelope.abortCurrentPulse();
981 impl_stop();
982 jumpTo(Duration.ZERO);
983 }
984 }
985
986 /**
987 * Pauses the animation. If the animation is not currently running, this
988 * method has no effect.
989 * <p>
990 * Note: <ul>
991 * <li>{@code pause()} is an asynchronous call, the {@code Animation} may not pause
992 * immediately. </ul>
993 * @throws IllegalStateException
994 * if embedded in another animation,
995 * such as {@link SequentialTransition} or {@link ParallelTransition}
996 */
997 public void pause() {
998 if (parent != null) {
999 throw new IllegalStateException("Cannot pause when embedded in another animation");
1000 }
1001 if (getStatus() == Status.RUNNING) {
1002 clipEnvelope.abortCurrentPulse();
1003 pauseReceiver();
1004 impl_pause();
1005 }
1006 }
1007
1008 /**
1009 * The constructor of {@code Animation}.
1010 *
1011 * This constructor allows to define a target framerate.
1012 *
1013 * @param targetFramerate
1014 * The custom target frame rate for this {@code Animation}
1015 * @see #getTargetFramerate()
1016 */
1017 protected Animation(double targetFramerate) {
1018 this.targetFramerate = targetFramerate;
1019 this.resolution = (int) Math.max(1, Math.round(TickCalculation.TICKS_PER_SECOND / targetFramerate));
1020 this.clipEnvelope = ClipEnvelope.create(this);
1021 this.timer = Toolkit.getToolkit().getMasterTimer();
1022 }
1023
1024 /**
1030 this.clipEnvelope = ClipEnvelope.create(this);
1031 this.timer = Toolkit.getToolkit().getMasterTimer();
1032 }
1033
1034 // These constructors are only for testing purposes
1035 Animation(AbstractMasterTimer timer) {
1036 this.resolution = 1;
1037 this.targetFramerate = TickCalculation.TICKS_PER_SECOND / timer.getDefaultResolution();
1038 this.clipEnvelope = ClipEnvelope.create(this);
1039 this.timer = timer;
1040 }
1041
1042 // These constructors are only for testing purposes
1043 Animation(AbstractMasterTimer timer, ClipEnvelope clipEnvelope, int resolution) {
1044 this.resolution = resolution;
1045 this.targetFramerate = TickCalculation.TICKS_PER_SECOND / resolution;
1046 this.clipEnvelope = clipEnvelope;
1047 this.timer = timer;
1048 }
1049
1050 boolean impl_startable(boolean forceSync) {
1051 return (fromDuration(getCycleDuration()) > 0L)
1052 || (!forceSync && clipEnvelope.wasSynched());
1053 }
1054
1055 void impl_sync(boolean forceSync) {
1056 if (forceSync || !clipEnvelope.wasSynched()) {
1057 syncClipEnvelope();
1058 }
1059 }
1060
1061 private void syncClipEnvelope() {
1062 final int publicCycleCount = getCycleCount();
1063 final int internalCycleCount = (publicCycleCount <= 0)
1064 && (publicCycleCount != INDEFINITE) ? 1 : publicCycleCount;
1065 clipEnvelope = clipEnvelope.setCycleCount(internalCycleCount);
1066 clipEnvelope.setCycleDuration(getCycleDuration());
1067 clipEnvelope.setAutoReverse(isAutoReverse());
1068 }
1069
1070 void impl_start(boolean forceSync) {
1071 impl_sync(forceSync);
1072 setStatus(Status.RUNNING);
1073 clipEnvelope.start();
1074 setCurrentRate(clipEnvelope.getCurrentRate());
1075 lastPulse = 0;
1076 }
1077
1078 void impl_pause() {
1079 final double currentRate = getCurrentRate();
1080 if (Math.abs(currentRate) >= EPSILON) {
1081 lastPlayedForward = Math.abs(getCurrentRate() - getRate()) < EPSILON;
1082 }
1083 setCurrentRate(0.0);
1084 setStatus(Status.PAUSED);
1085 }
1086
1087 void impl_resume() {
1088 setStatus(Status.RUNNING);
1089 setCurrentRate(lastPlayedForward ? getRate() : -getRate());
1090 }
1091
1092 void impl_stop() {
1093 if (!paused) {
1094 timer.removePulseReceiver(pulseReceiver);
1095 }
1096 setStatus(Status.STOPPED);
1097 setCurrentRate(0.0);
1098 }
1099
1100 void impl_timePulse(long elapsedTime) {
1101 if (resolution == 1) { // fullspeed
1102 clipEnvelope.timePulse(elapsedTime);
1103 } else if (elapsedTime - lastPulse >= resolution) {
1104 lastPulse = (elapsedTime / resolution) * resolution;
1105 clipEnvelope.timePulse(elapsedTime);
1106 }
1107 }
1108
1109 abstract void impl_playTo(long currentTicks, long cycleTicks);
1110
1111 abstract void impl_jumpTo(long currentTicks, long cycleTicks, boolean forceJump);
1112
1113 void impl_setCurrentTicks(long ticks) {
1114 currentTicks = ticks;
1115 if (currentTime != null) {
1116 currentTime.fireValueChangedEvent();
1117 }
1118 }
1119
1120 void impl_setCurrentRate(double currentRate) {
1121 // if (getStatus() == Status.RUNNING) {
1122 setCurrentRate(currentRate);
1123 // }
1124 }
1125
1126 final void impl_finished() {
1127 lastPlayedFinished = true;
1128 impl_stop();
1129 final EventHandler<ActionEvent> handler = getOnFinished();
1130 if (handler != null) {
1131 try {
1132 handler.handle(new ActionEvent(this, null));
1133 } catch (Exception ex) {
1134 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), ex);
1135 }
1136 }
1137 }
1138 }
|
1 /*
2 * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
166 if (paused) {
167 final long deltaTime = now() - pauseTime;
168 startTime += deltaTime;
169 paused = false;
170 addPulseReceiver();
171 }
172 }
173
174 // package private only for the sake of testing
175 final PulseReceiver pulseReceiver = new PulseReceiver() {
176 @Override public void timePulse(long now) {
177 final long elapsedTime = now - startTime;
178 if (elapsedTime < 0) {
179 return;
180 }
181 if (accessCtrlCtx == null) {
182 throw new IllegalStateException("Error: AccessControlContext not captured");
183 }
184
185 AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
186 doTimePulse(elapsedTime);
187 return null;
188 }, accessCtrlCtx);
189 }
190 };
191
192 private class CurrentRateProperty extends ReadOnlyDoublePropertyBase {
193 private double value;
194
195 @Override
196 public Object getBean() {
197 return Animation.this;
198 }
199
200 @Override
201 public String getName() {
202 return "currentRate";
203 }
204
205 @Override
206 public double get() {
294
295 public final DoubleProperty rateProperty() {
296 if (rate == null) {
297 rate = new DoublePropertyBase(DEFAULT_RATE) {
298
299 @Override
300 public void invalidated() {
301 final double newRate = getRate();
302 if (isRunningEmbedded()) {
303 if (isBound()) {
304 unbind();
305 }
306 set(oldRate);
307 throw new IllegalArgumentException("Cannot set rate of embedded animation while running.");
308 } else {
309 if (Math.abs(newRate) < EPSILON) {
310 if (getStatus() == Status.RUNNING) {
311 lastPlayedForward = (Math.abs(getCurrentRate()
312 - oldRate) < EPSILON);
313 }
314 doSetCurrentRate(0.0);
315 pauseReceiver();
316 } else {
317 if (getStatus() == Status.RUNNING) {
318 final double currentRate = getCurrentRate();
319 if (Math.abs(currentRate) < EPSILON) {
320 doSetCurrentRate(lastPlayedForward ? newRate : -newRate);
321 resumeReceiver();
322 } else {
323 final boolean playingForward = Math.abs(currentRate - oldRate) < EPSILON;
324 doSetCurrentRate(playingForward ? newRate : -newRate);
325 }
326 }
327 oldRate = newRate;
328 }
329 clipEnvelope.setRate(newRate);
330 }
331 }
332
333 @Override
334 public Object getBean() {
335 return Animation.this;
336 }
337
338 @Override
339 public String getName() {
340 return "rate";
341 }
342 };
343 }
344 return rate;
349 return false;
350 }
351 return parent.getStatus() != Status.STOPPED || parent.isRunningEmbedded();
352 }
353
354 private double oldRate = 1.0;
355 /**
356 * Read-only variable to indicate current direction/speed at which the
357 * {@code Animation} is being played.
358 * <p>
359 * {@code currentRate} is not necessary equal to {@code rate}.
360 * {@code currentRate} is set to {@code 0.0} when animation is paused or
361 * stopped. {@code currentRate} may also point to different direction during
362 * reverse cycles when {@code autoReverse} is {@code true}
363 *
364 * @defaultValue 0.0
365 */
366 private ReadOnlyDoubleProperty currentRate;
367 private static final double DEFAULT_CURRENT_RATE = 0.0;
368
369 private void doSetCurrentRate(double value) {
370 if ((currentRate != null) || (Math.abs(value - DEFAULT_CURRENT_RATE) > EPSILON)) {
371 ((CurrentRateProperty)currentRateProperty()).set(value);
372 }
373 }
374
375 public final double getCurrentRate() {
376 return (currentRate == null)? DEFAULT_CURRENT_RATE : currentRate.get();
377 }
378
379 public final ReadOnlyDoubleProperty currentRateProperty() {
380 if (currentRate == null) {
381 currentRate = new CurrentRateProperty();
382 }
383 return currentRate;
384 }
385
386 /**
387 * Read-only variable to indicate the duration of one cycle of this
388 * {@code Animation}: the time it takes to play from time 0 to the
389 * end of the Animation (at the default {@code rate} of
887 * <code>
888 * animation.setRate(negative rate);<br>
889 * animation.jumpTo(overall duration of animation);<br>
890 * animation.play();<br>
891 * </code>
892 * <p>
893 * Note: <ul>
894 * <li>{@code play()} is an asynchronous call, the {@code Animation} may not
895 * start immediately. </ul>
896 *
897 * @throws IllegalStateException
898 * if embedded in another animation,
899 * such as {@link SequentialTransition} or {@link ParallelTransition}
900 */
901 public void play() {
902 if (parent != null) {
903 throw new IllegalStateException("Cannot start when embedded in another animation");
904 }
905 switch (getStatus()) {
906 case STOPPED:
907 if (startable(true)) {
908 final double rate = getRate();
909 if (lastPlayedFinished) {
910 jumpTo((rate < 0)? getTotalDuration() : Duration.ZERO);
911 }
912 doStart(true);
913 startReceiver(TickCalculation.fromDuration(getDelay()));
914 if (Math.abs(rate) < EPSILON) {
915 pauseReceiver();
916 } else {
917
918 }
919 } else {
920 final EventHandler<ActionEvent> handler = getOnFinished();
921 if (handler != null) {
922 handler.handle(new ActionEvent(this, null));
923 }
924 }
925 break;
926 case PAUSED:
927 doResume();
928 if (Math.abs(getRate()) >= EPSILON) {
929 resumeReceiver();
930 }
931 break;
932 }
933 }
934
935 /**
936 * Plays an {@code Animation} from initial position in forward direction.
937 * <p>
938 * It is equivalent to
939 * <p>
940 * <code>
941 * animation.stop();<br>
942 * animation.setRate = setRate(Math.abs(animation.getRate())); </br>
943 * animation.jumpTo(Duration.ZERO);<br>
944 * animation.play();<br>
945 * </code>
946 *
947 * <p>
961 play();
962 }
963
964 /**
965 * Stops the animation and resets the play head to its initial position. If
966 * the animation is not currently running, this method has no effect.
967 * <p>
968 * Note: <ul>
969 * <li>{@code stop()} is an asynchronous call, the {@code Animation} may not stop
970 * immediately. </ul>
971 * @throws IllegalStateException
972 * if embedded in another animation,
973 * such as {@link SequentialTransition} or {@link ParallelTransition}
974 */
975 public void stop() {
976 if (parent != null) {
977 throw new IllegalStateException("Cannot stop when embedded in another animation");
978 }
979 if (getStatus() != Status.STOPPED) {
980 clipEnvelope.abortCurrentPulse();
981 doStop();
982 jumpTo(Duration.ZERO);
983 }
984 }
985
986 /**
987 * Pauses the animation. If the animation is not currently running, this
988 * method has no effect.
989 * <p>
990 * Note: <ul>
991 * <li>{@code pause()} is an asynchronous call, the {@code Animation} may not pause
992 * immediately. </ul>
993 * @throws IllegalStateException
994 * if embedded in another animation,
995 * such as {@link SequentialTransition} or {@link ParallelTransition}
996 */
997 public void pause() {
998 if (parent != null) {
999 throw new IllegalStateException("Cannot pause when embedded in another animation");
1000 }
1001 if (getStatus() == Status.RUNNING) {
1002 clipEnvelope.abortCurrentPulse();
1003 pauseReceiver();
1004 doPause();
1005 }
1006 }
1007
1008 /**
1009 * The constructor of {@code Animation}.
1010 *
1011 * This constructor allows to define a target framerate.
1012 *
1013 * @param targetFramerate
1014 * The custom target frame rate for this {@code Animation}
1015 * @see #getTargetFramerate()
1016 */
1017 protected Animation(double targetFramerate) {
1018 this.targetFramerate = targetFramerate;
1019 this.resolution = (int) Math.max(1, Math.round(TickCalculation.TICKS_PER_SECOND / targetFramerate));
1020 this.clipEnvelope = ClipEnvelope.create(this);
1021 this.timer = Toolkit.getToolkit().getMasterTimer();
1022 }
1023
1024 /**
1030 this.clipEnvelope = ClipEnvelope.create(this);
1031 this.timer = Toolkit.getToolkit().getMasterTimer();
1032 }
1033
1034 // These constructors are only for testing purposes
1035 Animation(AbstractMasterTimer timer) {
1036 this.resolution = 1;
1037 this.targetFramerate = TickCalculation.TICKS_PER_SECOND / timer.getDefaultResolution();
1038 this.clipEnvelope = ClipEnvelope.create(this);
1039 this.timer = timer;
1040 }
1041
1042 // These constructors are only for testing purposes
1043 Animation(AbstractMasterTimer timer, ClipEnvelope clipEnvelope, int resolution) {
1044 this.resolution = resolution;
1045 this.targetFramerate = TickCalculation.TICKS_PER_SECOND / resolution;
1046 this.clipEnvelope = clipEnvelope;
1047 this.timer = timer;
1048 }
1049
1050 boolean startable(boolean forceSync) {
1051 return (fromDuration(getCycleDuration()) > 0L)
1052 || (!forceSync && clipEnvelope.wasSynched());
1053 }
1054
1055 void sync(boolean forceSync) {
1056 if (forceSync || !clipEnvelope.wasSynched()) {
1057 syncClipEnvelope();
1058 }
1059 }
1060
1061 private void syncClipEnvelope() {
1062 final int publicCycleCount = getCycleCount();
1063 final int internalCycleCount = (publicCycleCount <= 0)
1064 && (publicCycleCount != INDEFINITE) ? 1 : publicCycleCount;
1065 clipEnvelope = clipEnvelope.setCycleCount(internalCycleCount);
1066 clipEnvelope.setCycleDuration(getCycleDuration());
1067 clipEnvelope.setAutoReverse(isAutoReverse());
1068 }
1069
1070 void doStart(boolean forceSync) {
1071 sync(forceSync);
1072 setStatus(Status.RUNNING);
1073 clipEnvelope.start();
1074 doSetCurrentRate(clipEnvelope.getCurrentRate());
1075 lastPulse = 0;
1076 }
1077
1078 void doPause() {
1079 final double currentRate = getCurrentRate();
1080 if (Math.abs(currentRate) >= EPSILON) {
1081 lastPlayedForward = Math.abs(getCurrentRate() - getRate()) < EPSILON;
1082 }
1083 doSetCurrentRate(0.0);
1084 setStatus(Status.PAUSED);
1085 }
1086
1087 void doResume() {
1088 setStatus(Status.RUNNING);
1089 doSetCurrentRate(lastPlayedForward ? getRate() : -getRate());
1090 }
1091
1092 void doStop() {
1093 if (!paused) {
1094 timer.removePulseReceiver(pulseReceiver);
1095 }
1096 setStatus(Status.STOPPED);
1097 doSetCurrentRate(0.0);
1098 }
1099
1100 void doTimePulse(long elapsedTime) {
1101 if (resolution == 1) { // fullspeed
1102 clipEnvelope.timePulse(elapsedTime);
1103 } else if (elapsedTime - lastPulse >= resolution) {
1104 lastPulse = (elapsedTime / resolution) * resolution;
1105 clipEnvelope.timePulse(elapsedTime);
1106 }
1107 }
1108
1109 abstract void doPlayTo(long currentTicks, long cycleTicks);
1110
1111 abstract void doJumpTo(long currentTicks, long cycleTicks, boolean forceJump);
1112
1113 void setCurrentTicks(long ticks) {
1114 currentTicks = ticks;
1115 if (currentTime != null) {
1116 currentTime.fireValueChangedEvent();
1117 }
1118 }
1119
1120 void setCurrentRate(double currentRate) {
1121 // if (getStatus() == Status.RUNNING) {
1122 doSetCurrentRate(currentRate);
1123 // }
1124 }
1125
1126 final void finished() {
1127 lastPlayedFinished = true;
1128 doStop();
1129 final EventHandler<ActionEvent> handler = getOnFinished();
1130 if (handler != null) {
1131 try {
1132 handler.handle(new ActionEvent(this, null));
1133 } catch (Exception ex) {
1134 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), ex);
1135 }
1136 }
1137 }
1138 }
|