1 package org.opentrafficsim.gui;
2
3 import java.awt.Container;
4 import java.awt.Dimension;
5 import java.awt.FlowLayout;
6 import java.awt.Font;
7 import java.awt.Rectangle;
8 import java.awt.event.ActionEvent;
9 import java.awt.event.ActionListener;
10 import java.awt.event.WindowEvent;
11 import java.awt.event.WindowListener;
12 import java.beans.PropertyChangeEvent;
13 import java.beans.PropertyChangeListener;
14 import java.io.Serializable;
15 import java.rmi.RemoteException;
16 import java.text.DecimalFormat;
17 import java.text.NumberFormat;
18 import java.text.ParseException;
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.Hashtable;
22 import java.util.Map;
23 import java.util.Timer;
24 import java.util.TimerTask;
25 import java.util.logging.Level;
26 import java.util.logging.Logger;
27 import java.util.regex.Matcher;
28 import java.util.regex.Pattern;
29
30 import javax.swing.BoxLayout;
31 import javax.swing.ImageIcon;
32 import javax.swing.JButton;
33 import javax.swing.JFormattedTextField;
34 import javax.swing.JFrame;
35 import javax.swing.JLabel;
36 import javax.swing.JPanel;
37 import javax.swing.JSlider;
38 import javax.swing.SwingConstants;
39 import javax.swing.SwingUtilities;
40 import javax.swing.WindowConstants;
41 import javax.swing.event.ChangeEvent;
42 import javax.swing.event.ChangeListener;
43 import javax.swing.text.DefaultFormatter;
44 import javax.swing.text.MaskFormatter;
45
46 import org.djunits.unit.TimeUnit;
47 import org.djunits.value.vdouble.scalar.Duration;
48 import org.djunits.value.vdouble.scalar.Time;
49 import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
50 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
51 import org.opentrafficsim.simulationengine.WrappableAnimation;
52
53 import nl.tudelft.simulation.dsol.SimRuntimeException;
54 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
55 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
56 import nl.tudelft.simulation.dsol.simulators.DEVSRealTimeClock;
57 import nl.tudelft.simulation.dsol.simulators.DEVSSimulator;
58 import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
59 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
60 import nl.tudelft.simulation.event.EventInterface;
61 import nl.tudelft.simulation.event.EventListenerInterface;
62 import nl.tudelft.simulation.language.io.URLResource;
63
64
65
66
67
68
69
70
71
72
73
74
75 public class OTSControlPanel extends JPanel implements ActionListener, PropertyChangeListener, WindowListener,
76 EventListenerInterface
77 {
78
79 private static final long serialVersionUID = 20150617L;
80
81
82 private OTSDEVSSimulatorInterface simulator;
83
84
85 private final WrappableAnimation wrappableAnimation;
86
87
88 private final Logger logger;
89
90
91 private final ClockPanel clockPanel;
92
93
94 private final TimeWarpPanel timeWarpPanel;
95
96
97 private final ArrayList<JButton> buttons = new ArrayList<JButton>();
98
99
100 private final Font timeFont = new Font("SansSerif", Font.BOLD, 18);
101
102
103 private final TimeEdit timeEdit;
104
105
106 private SimEvent<OTSSimTimeDouble> stopAtEvent = null;
107
108
109 protected boolean closeHandlerRegistered = false;
110
111
112 private boolean isCleanUp = false;
113
114
115
116
117
118
119
120 public OTSControlPanel(final OTSDEVSSimulatorInterface simulator, final WrappableAnimation wrappableAnimation)
121 throws RemoteException
122 {
123 this.simulator = simulator;
124 this.wrappableAnimation = wrappableAnimation;
125 this.logger = Logger.getLogger("nl.tudelft.opentrafficsim");
126
127 this.setLayout(new FlowLayout(FlowLayout.LEFT));
128 JPanel buttonPanel = new JPanel();
129 buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
130 buttonPanel.add(makeButton("stepButton", "/Last_recor.png", "Step", "Execute one event", true));
131 buttonPanel.add(makeButton("nextTimeButton", "/NextTrack.png", "NextTime",
132 "Execute all events scheduled for the current time", true));
133 buttonPanel.add(makeButton("runPauseButton", "/Play.png", "RunPause", "XXX", true));
134 this.timeWarpPanel = new TimeWarpPanel(0.1, 1000, 1, 3, simulator);
135 buttonPanel.add(this.timeWarpPanel);
136 buttonPanel.add(makeButton("resetButton", "/Undo.png", "Reset", "Reset the simulation", false));
137 this.clockPanel = new ClockPanel();
138 buttonPanel.add(this.clockPanel);
139 this.timeEdit = new TimeEdit(new Time(0, TimeUnit.SECOND));
140 this.timeEdit.addPropertyChangeListener("value", this);
141 buttonPanel.add(this.timeEdit);
142 this.add(buttonPanel);
143 fixButtons();
144 installWindowCloseHandler();
145 this.simulator.addListener(this, SimulatorInterface.END_OF_REPLICATION_EVENT);
146 this.simulator.addListener(this, SimulatorInterface.START_EVENT);
147 this.simulator.addListener(this, SimulatorInterface.STOP_EVENT);
148 this.simulator.addListener(this, DEVSRealTimeClock.CHANGE_SPEED_FACTOR_EVENT);
149 }
150
151
152
153
154
155
156
157
158
159
160 private JButton makeButton(final String name, final String iconPath, final String actionCommand, final String toolTipText,
161 final boolean enabled)
162 {
163
164 JButton result = new JButton(new ImageIcon(URLResource.getResource(iconPath)));
165 result.setName(name);
166 result.setEnabled(enabled);
167 result.setActionCommand(actionCommand);
168 result.setToolTipText(toolTipText);
169 result.addActionListener(this);
170 this.buttons.add(result);
171 return result;
172 }
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188 private SimEvent<OTSSimTimeDouble> scheduleEvent(final Time executionTime, final short priority, final Object source,
189 final Object eventTarget, final String method, final Object[] args) throws SimRuntimeException
190 {
191 SimEvent<OTSSimTimeDouble> simEvent =
192 new SimEvent<OTSSimTimeDouble>(new OTSSimTimeDouble(new Time(executionTime.getSI(), TimeUnit.SECOND)),
193 priority, source, eventTarget, method, args);
194 this.simulator.scheduleEvent(simEvent);
195 return simEvent;
196 }
197
198
199
200
201 public final void installWindowCloseHandler()
202 {
203 if (this.closeHandlerRegistered)
204 {
205 return;
206 }
207
208
209 new DisposeOnCloseThread(this).start();
210 }
211
212
213 protected class DisposeOnCloseThread extends Thread
214 {
215
216 private OTSControlPanel panel;
217
218
219
220
221 public DisposeOnCloseThread(final OTSControlPanel panel)
222 {
223 super();
224 this.panel = panel;
225 }
226
227
228 @Override
229 public final void run()
230 {
231 Container root = this.panel;
232 while (!(root instanceof JFrame))
233 {
234 try
235 {
236 Thread.sleep(10);
237 }
238 catch (InterruptedException exception)
239 {
240
241 }
242
243
244 root = this.panel;
245 while (null != root.getParent() && !(root instanceof JFrame))
246 {
247 root = root.getParent();
248 }
249 }
250 JFrame frame = (JFrame) root;
251 frame.addWindowListener(this.panel);
252 this.panel.closeHandlerRegistered = true;
253
254 }
255
256
257 @Override
258 public final String toString()
259 {
260 return "DisposeOnCloseThread [panel=" + this.panel + "]";
261 }
262 }
263
264
265 @Override
266 public final void actionPerformed(final ActionEvent actionEvent)
267 {
268 String actionCommand = actionEvent.getActionCommand();
269
270 try
271 {
272 if (actionCommand.equals("Step"))
273 {
274 if (getSimulator().isRunning())
275 {
276 getSimulator().stop();
277 }
278 this.simulator.step();
279 }
280 if (actionCommand.equals("RunPause"))
281 {
282 if (this.simulator.isRunning())
283 {
284 this.simulator.stop();
285 }
286 else if (getSimulator().getEventList().size() > 0)
287 {
288 this.simulator.start();
289 }
290 }
291 if (actionCommand.equals("NextTime"))
292 {
293 if (getSimulator().isRunning())
294 {
295 getSimulator().stop();
296 }
297 double now = getSimulator().getSimulatorTime().getTime().getSI();
298
299 try
300 {
301 this.stopAtEvent =
302 scheduleEvent(new Time(now, TimeUnit.SI), SimEventInterface.MIN_PRIORITY, this, this,
303 "autoPauseSimulator", null);
304 }
305 catch (SimRuntimeException exception)
306 {
307 this.logger.logp(Level.SEVERE, "ControlPanel", "autoPauseSimulator", "Caught an exception "
308 + "while trying to schedule an autoPauseSimulator event at the current simulator time");
309 }
310 this.simulator.start();
311 }
312 if (actionCommand.equals("Reset"))
313 {
314 if (getSimulator().isRunning())
315 {
316 getSimulator().stop();
317 }
318
319 if (null == OTSControlPanel.this.wrappableAnimation)
320 {
321 throw new RuntimeException("Do not know how to restart this simulation");
322 }
323
324
325 Container root = OTSControlPanel.this;
326 while (!(root instanceof JFrame))
327 {
328 root = root.getParent();
329 }
330 JFrame frame = (JFrame) root;
331 Rectangle rect = frame.getBounds();
332 frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
333 frame.dispose();
334 OTSControlPanel.this.cleanup();
335 try
336 {
337 OTSControlPanel.this.wrappableAnimation.rebuildSimulator(rect);
338 }
339 catch (Exception exception)
340 {
341 exception.printStackTrace();
342 }
343 }
344 fixButtons();
345 }
346 catch (Exception exception)
347 {
348 exception.printStackTrace();
349 }
350 }
351
352
353
354
355 private void cleanup()
356 {
357 if (!this.isCleanUp)
358 {
359 this.isCleanUp = true;
360 try
361 {
362 if (this.simulator != null)
363 {
364 if (this.simulator.isRunning())
365 {
366 this.simulator.stop();
367 }
368
369
370 getSimulator().getReplication().getExperiment().removeFromContext();
371 getSimulator().cleanUp();
372 }
373
374 if (this.clockPanel != null)
375 {
376 this.clockPanel.cancelTimer();
377 }
378
379 if (this.wrappableAnimation != null)
380 {
381 this.wrappableAnimation.stopTimersThreads();
382 }
383 }
384 catch (Throwable exception)
385 {
386 exception.printStackTrace();
387 }
388 }
389 }
390
391
392
393
394 protected final void fixButtons()
395 {
396
397 final boolean moreWorkToDo = getSimulator().getEventList().size() > 0;
398 for (JButton button : this.buttons)
399 {
400 final String actionCommand = button.getActionCommand();
401 if (actionCommand.equals("Step"))
402 {
403 button.setEnabled(moreWorkToDo);
404 }
405 else if (actionCommand.equals("RunPause"))
406 {
407 button.setEnabled(moreWorkToDo);
408 if (this.simulator.isRunning())
409 {
410 button.setToolTipText("Pause the simulation");
411 button.setIcon(new ImageIcon(URLResource.getResource("/Pause.png")));
412 }
413 else
414 {
415 button.setToolTipText("Run the simulation at the indicated speed");
416 button.setIcon(new ImageIcon(URLResource.getResource("/Play.png")));
417 }
418 button.setEnabled(moreWorkToDo);
419 }
420 else if (actionCommand.equals("NextTime"))
421 {
422 button.setEnabled(moreWorkToDo);
423 }
424 else if (actionCommand.equals("Reset"))
425 {
426 button.setEnabled(true);
427 }
428 else
429 {
430 this.logger.logp(Level.SEVERE, "ControlPanel", "fixButtons", "", new Exception("Unknown button?"));
431 }
432 }
433
434 }
435
436
437
438
439 public final void autoPauseSimulator()
440 {
441 if (getSimulator().isRunning())
442 {
443 getSimulator().stop();
444 double currentTick = getSimulator().getSimulatorTime().getTime().getSI();
445 double nextTick = getSimulator().getEventList().first().getAbsoluteExecutionTime().get().getSI();
446
447
448 if (nextTick > currentTick)
449 {
450
451
452
453
454 try
455 {
456 this.stopAtEvent =
457 scheduleEvent(new Time(nextTick, TimeUnit.SI), SimEventInterface.MAX_PRIORITY, this, this,
458 "autoPauseSimulator", null);
459 getSimulator().start();
460 }
461 catch (SimRuntimeException exception)
462 {
463 this.logger.logp(Level.SEVERE, "ControlPanel", "autoPauseSimulator",
464 "Caught an exception while trying to re-schedule an autoPauseEvent at the next real event");
465 }
466 }
467 else
468 {
469
470 if (SwingUtilities.isEventDispatchThread())
471 {
472
473 fixButtons();
474 }
475 else
476 {
477 try
478 {
479
480 SwingUtilities.invokeAndWait(new Runnable()
481 {
482 @Override
483 public void run()
484 {
485
486 fixButtons();
487
488 }
489 });
490 }
491 catch (Exception e)
492 {
493 if (e instanceof InterruptedException)
494 {
495 System.out.println("Caught " + e);
496
497 }
498 else
499 {
500 e.printStackTrace();
501 }
502 }
503 }
504 }
505 }
506 }
507
508
509 @Override
510 public final void propertyChange(final PropertyChangeEvent evt)
511 {
512
513 if (null != this.stopAtEvent)
514 {
515 getSimulator().cancelEvent(this.stopAtEvent);
516 this.stopAtEvent = null;
517 }
518 String newValue = (String) evt.getNewValue();
519 String[] fields = newValue.split("[:\\.]");
520 int hours = Integer.parseInt(fields[0]);
521 int minutes = Integer.parseInt(fields[1]);
522 int seconds = Integer.parseInt(fields[2]);
523 int fraction = Integer.parseInt(fields[3]);
524 double stopTime = hours * 3600 + minutes * 60 + seconds + fraction / 1000d;
525 if (stopTime < getSimulator().getSimulatorTime().getTime().getSI())
526 {
527 return;
528 }
529 else
530 {
531 try
532 {
533 this.stopAtEvent =
534 scheduleEvent(new Time(stopTime, TimeUnit.SECOND), SimEventInterface.MAX_PRIORITY, this, this,
535 "autoPauseSimulator", null);
536 }
537 catch (SimRuntimeException exception)
538 {
539 this.logger.logp(Level.SEVERE, "ControlPanel", "propertyChange",
540 "Caught an exception while trying to schedule an autoPauseSimulator event");
541 }
542 }
543 }
544
545
546
547
548 @SuppressWarnings("unchecked")
549 public final DEVSSimulator<Time, Duration, OTSSimTimeDouble> getSimulator()
550 {
551 return (DEVSSimulator<Time, Duration, OTSSimTimeDouble>) this.simulator;
552 }
553
554
555 @Override
556 public void windowOpened(final WindowEvent e)
557 {
558
559 }
560
561
562 @Override
563 public final void windowClosing(final WindowEvent e)
564 {
565 if (this.simulator != null)
566 {
567 try
568 {
569 if (this.simulator.isRunning())
570 {
571 this.simulator.stop();
572 }
573 }
574 catch (SimRuntimeException exception)
575 {
576 exception.printStackTrace();
577 }
578 }
579 }
580
581
582 @Override
583 public final void windowClosed(final WindowEvent e)
584 {
585 cleanup();
586 }
587
588
589 @Override
590 public final void windowIconified(final WindowEvent e)
591 {
592
593 }
594
595
596 @Override
597 public final void windowDeiconified(final WindowEvent e)
598 {
599
600 }
601
602
603 @Override
604 public final void windowActivated(final WindowEvent e)
605 {
606
607 }
608
609
610 @Override
611 public final void windowDeactivated(final WindowEvent e)
612 {
613
614 }
615
616
617
618
619 public final Font getTimeFont()
620 {
621 return this.timeFont;
622 }
623
624
625 static class TimeWarpPanel extends JPanel
626 {
627
628 private static final long serialVersionUID = 20150408L;
629
630
631 private final JSlider slider;
632
633
634 private final int[] ratios;
635
636
637 private Map<Integer, Double> tickValues = new HashMap<>();
638
639
640
641
642
643
644
645
646
647
648
649 TimeWarpPanel(final double minimum, final double maximum, final double initialValue, final int ticksPerDecade,
650 final DEVSSimulatorInterface<?, ?, ?> simulator)
651 {
652 if (minimum <= 0 || minimum > initialValue || initialValue > maximum)
653 {
654 throw new RuntimeException("Bad (combination of) minimum, maximum and initialValue; "
655 + "(restrictions: 0 < minimum <= initialValue <= maximum)");
656 }
657 switch (ticksPerDecade)
658 {
659 case 1:
660 this.ratios = new int[] { 1 };
661 break;
662 case 2:
663 this.ratios = new int[] { 1, 3 };
664 break;
665 case 3:
666 this.ratios = new int[] { 1, 2, 5 };
667 break;
668 default:
669 throw new RuntimeException("Bad ticksPerDecade value (must be 1, 2 or 3)");
670 }
671 int minimumTick = (int) Math.floor(Math.log10(minimum / initialValue) * ticksPerDecade);
672 int maximumTick = (int) Math.ceil(Math.log10(maximum / initialValue) * ticksPerDecade);
673 this.slider = new JSlider(SwingConstants.HORIZONTAL, minimumTick, maximumTick + 1, 0);
674 this.slider.setPreferredSize(new Dimension(350, 45));
675 Hashtable<Integer, JLabel> labels = new Hashtable<Integer, JLabel>();
676 for (int step = 0; step <= maximumTick; step++)
677 {
678 StringBuilder text = new StringBuilder();
679 text.append(this.ratios[step % this.ratios.length]);
680 for (int decade = 0; decade < step / this.ratios.length; decade++)
681 {
682 text.append("0");
683 }
684 this.tickValues.put(step, Double.parseDouble(text.toString()));
685 labels.put(step, new JLabel(text.toString().replace("000", "K")));
686
687 }
688
689 String decimalSeparator =
690 "" + ((DecimalFormat) NumberFormat.getInstance()).getDecimalFormatSymbols().getDecimalSeparator();
691 for (int step = -1; step >= minimumTick; step--)
692 {
693 StringBuilder text = new StringBuilder();
694 text.append("0");
695 text.append(decimalSeparator);
696 for (int decade = (step + 1) / this.ratios.length; decade < 0; decade++)
697 {
698 text.append("0");
699 }
700 int index = step % this.ratios.length;
701 if (index < 0)
702 {
703 index += this.ratios.length;
704 }
705 text.append(this.ratios[index]);
706 labels.put(step, new JLabel(text.toString()));
707 this.tickValues.put(step, Double.parseDouble(text.toString()));
708
709 }
710 labels.put(maximumTick + 1, new JLabel("\u221E"));
711 this.tickValues.put(maximumTick + 1, 1E9);
712 this.slider.setLabelTable(labels);
713 this.slider.setPaintLabels(true);
714 this.slider.setPaintTicks(true);
715 this.slider.setMajorTickSpacing(1);
716 this.add(this.slider);
717
718
719
720
721
722
723
724
725 if (simulator instanceof DEVSRealTimeClock)
726 {
727 DEVSRealTimeClock<?, ?, ?> clock = (DEVSRealTimeClock<?, ?, ?>) simulator;
728 clock.setSpeedFactor(TimeWarpPanel.this.tickValues.get(this.slider.getValue()));
729 }
730
731
732 this.slider.addChangeListener(new ChangeListener()
733 {
734 public void stateChanged(final ChangeEvent ce)
735 {
736 JSlider source = (JSlider) ce.getSource();
737 if (!source.getValueIsAdjusting() && simulator instanceof DEVSRealTimeClock)
738 {
739 DEVSRealTimeClock<?, ?, ?> clock = (DEVSRealTimeClock<?, ?, ?>) simulator;
740 clock.setSpeedFactor(((TimeWarpPanel) source.getParent()).getTickValues().get(source.getValue()));
741 }
742 }
743 });
744 }
745
746
747
748
749
750 protected Map<Integer, Double> getTickValues()
751 {
752 return this.tickValues;
753 }
754
755
756
757
758
759
760 private double stepToFactor(final int step)
761 {
762 int index = step % this.ratios.length;
763 if (index < 0)
764 {
765 index += this.ratios.length;
766 }
767 double result = this.ratios[index];
768
769 int power = (step + 1000 * this.ratios.length) / this.ratios.length - 1000;
770 while (power > 0)
771 {
772 result *= 10;
773 power--;
774 }
775 while (power < 0)
776 {
777 result /= 10;
778 power++;
779 }
780 return result;
781 }
782
783
784
785
786
787 public final double getFactor()
788 {
789 return stepToFactor(this.slider.getValue());
790 }
791
792
793 @Override
794 public final String toString()
795 {
796 return "TimeWarpPanel [timeWarp=" + this.getFactor() + "]";
797 }
798
799
800
801
802
803 public void setSpeedFactor(final double factor)
804 {
805 int bestStep = -1;
806 double bestError = Double.MAX_VALUE;
807 for (int step = this.slider.getMinimum(); step < this.slider.getMaximum(); step++)
808 {
809 double ratio = getTickValues().get(step);
810 double logError = Math.abs(Math.log(factor / ratio));
811 if (logError < bestError)
812 {
813 bestStep = step;
814 bestError = logError;
815 }
816 }
817 System.out.println("setSpeedfactor: factor is " + factor + ", best slider value is " + bestStep
818 + " current value is " + this.slider.getValue());
819 if (this.slider.getValue() != bestStep)
820 {
821 this.slider.setValue(bestStep);
822 }
823 }
824 }
825
826
827 class ClockPanel extends JLabel
828 {
829
830 private static final long serialVersionUID = 20141211L;
831
832
833 private final JLabel clockLabel;
834
835
836 private Timer timer;
837
838
839 private static final long UPDATEINTERVAL = 1000;
840
841
842 ClockPanel()
843 {
844 super("00:00:00.000");
845 this.clockLabel = this;
846 this.setFont(getTimeFont());
847 this.timer = new Timer();
848 this.timer.scheduleAtFixedRate(new TimeUpdateTask(), 0, ClockPanel.UPDATEINTERVAL);
849 }
850
851
852
853
854 public void cancelTimer()
855 {
856 if (this.timer != null)
857 {
858 this.timer.cancel();
859 }
860 this.timer = null;
861 }
862
863
864 private class TimeUpdateTask extends TimerTask implements Serializable
865 {
866
867 private static final long serialVersionUID = 20140000L;
868
869
870
871
872 TimeUpdateTask()
873 {
874 }
875
876
877 @Override
878 public void run()
879 {
880 double now = Math.round(getSimulator().getSimulatorTime().getTime().getSI() * 1000) / 1000d;
881 int seconds = (int) Math.floor(now);
882 int fractionalSeconds = (int) Math.floor(1000 * (now - seconds));
883 getClockLabel().setText(
884 String.format(" %02d:%02d:%02d.%03d ", seconds / 3600, seconds / 60 % 60, seconds % 60,
885 fractionalSeconds));
886 getClockLabel().repaint();
887 }
888
889
890 @Override
891 public final String toString()
892 {
893 return "TimeUpdateTask of ClockPanel";
894 }
895 }
896
897
898
899
900 protected JLabel getClockLabel()
901 {
902 return this.clockLabel;
903 }
904
905
906 @Override
907 public final String toString()
908 {
909 return "ClockPanel [clockLabel=" + this.clockLabel + ", time=" + getText() + "]";
910 }
911
912 }
913
914
915 class TimeEdit extends JFormattedTextField
916 {
917
918 private static final long serialVersionUID = 20141212L;
919
920
921
922
923
924 TimeEdit(final Time initialValue)
925 {
926 super(new RegexFormatter("\\d\\d\\d\\d:[0-5]\\d:[0-5]\\d\\.\\d\\d\\d"));
927 MaskFormatter mf = null;
928 try
929 {
930 mf = new MaskFormatter("####:##:##.###");
931 mf.setPlaceholderCharacter('0');
932 mf.setAllowsInvalid(false);
933 mf.setCommitsOnValidEdit(true);
934 mf.setOverwriteMode(true);
935 mf.install(this);
936 }
937 catch (ParseException exception)
938 {
939 exception.printStackTrace();
940 }
941 setTime(initialValue);
942 setFont(getTimeFont());
943 }
944
945
946
947
948
949 public void setTime(final Time newValue)
950 {
951 double v = newValue.getSI();
952 int integerPart = (int) Math.floor(v);
953 int fraction = (int) Math.floor((v - integerPart) * 1000);
954 String text =
955 String.format("%04d:%02d:%02d.%03d", integerPart / 3600, integerPart / 60 % 60, integerPart % 60, fraction);
956 this.setText(text);
957 }
958
959
960 @Override
961 public final String toString()
962 {
963 return "TimeEdit [time=" + getText() + "]";
964 }
965 }
966
967
968
969
970
971
972
973
974
975
976 static class RegexFormatter extends DefaultFormatter
977 {
978
979 private static final long serialVersionUID = 20141212L;
980
981
982 private Pattern pattern;
983
984
985
986
987
988 RegexFormatter(final String pattern)
989 {
990 this.pattern = Pattern.compile(pattern);
991 }
992
993 @Override
994 public Object stringToValue(final String text) throws ParseException
995 {
996 Matcher matcher = this.pattern.matcher(text);
997 if (matcher.matches())
998 {
999
1000 return super.stringToValue(text);
1001 }
1002
1003 throw new ParseException("Pattern did not match", 0);
1004 }
1005
1006
1007 @Override
1008 public final String toString()
1009 {
1010 return "RegexFormatter [pattern=" + this.pattern + "]";
1011 }
1012 }
1013
1014
1015 @Override
1016 public final void notify(final EventInterface event) throws RemoteException
1017 {
1018 if (event.getType().equals(SimulatorInterface.END_OF_REPLICATION_EVENT)
1019 || event.getType().equals(SimulatorInterface.START_EVENT)
1020 || event.getType().equals(SimulatorInterface.STOP_EVENT)
1021 || event.getType().equals(DEVSRealTimeClock.CHANGE_SPEED_FACTOR_EVENT))
1022 {
1023 if (event.getType().equals(DEVSRealTimeClock.CHANGE_SPEED_FACTOR_EVENT))
1024 {
1025 this.timeWarpPanel.setSpeedFactor((Double) event.getContent());
1026 }
1027 fixButtons();
1028 }
1029 }
1030
1031
1032 @Override
1033 public final String toString()
1034 {
1035 return "OTSControlPanel [simulatorTime=" + this.simulator.getSimulatorTime().getTime() + ", timeWarp="
1036 + this.timeWarpPanel.getFactor() + ", stopAtEvent=" + this.stopAtEvent + "]";
1037 }
1038
1039 }