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