1 package org.opentrafficsim.gui;
2
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.Container;
6 import java.awt.Dimension;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.awt.event.WindowEvent;
10 import java.awt.event.WindowListener;
11 import java.awt.geom.Rectangle2D;
12 import java.rmi.RemoteException;
13 import java.text.NumberFormat;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.Map;
17
18 import javax.swing.BoxLayout;
19 import javax.swing.ImageIcon;
20 import javax.swing.JButton;
21 import javax.swing.JCheckBox;
22 import javax.swing.JFrame;
23 import javax.swing.JLabel;
24 import javax.swing.JPanel;
25 import javax.swing.JToggleButton;
26
27 import org.opentrafficsim.base.modelproperties.PropertyException;
28 import org.opentrafficsim.core.gtu.animation.GTUColorer;
29 import org.opentrafficsim.simulationengine.SimpleAnimator;
30 import org.opentrafficsim.simulationengine.WrappableAnimation;
31
32 import nl.tudelft.simulation.dsol.animation.Locatable;
33 import nl.tudelft.simulation.dsol.animation.D2.AnimationPanel;
34 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
35 import nl.tudelft.simulation.event.Event;
36 import nl.tudelft.simulation.language.io.URLResource;
37
38
39
40
41
42
43
44
45
46
47
48
49 public class OTSAnimationPanel extends OTSSimulationPanel implements ActionListener, WindowListener
50 {
51
52 private static final long serialVersionUID = 20150617L;
53
54
55 private final AnimationPanel animationPanel;
56
57
58 private final JPanel borderPanel;
59
60
61 private final JPanel togglePanel;
62
63
64 private Map<String, Class<? extends Locatable>> toggleLocatableMap = new HashMap<>();
65
66
67 private GTUColorer gtuColorer = null;
68
69
70 private ColorControlPanel colorControlPanel = null;
71
72
73 private final JLabel coordinateField;
74
75
76 private final ArrayList<JButton> buttons = new ArrayList<JButton>();
77
78
79 private static final NumberFormat FORMATTER = NumberFormat.getInstance();
80
81
82 @SuppressWarnings("checkstyle:visibilitymodifier")
83 protected boolean closeHandlerRegistered = false;
84
85
86 @SuppressWarnings("checkstyle:visibilitymodifier")
87 protected boolean windowExited = false;
88
89
90 static
91 {
92 FORMATTER.setMaximumFractionDigits(3);
93 }
94
95
96
97
98
99
100
101
102
103
104
105 public OTSAnimationPanel(final Rectangle2D extent, final Dimension size, final SimpleAnimator simulator,
106 final WrappableAnimation wrappableAnimation, final GTUColorer gtuColorer) throws RemoteException, PropertyException
107 {
108 super(simulator, wrappableAnimation);
109
110
111 this.animationPanel = new AnimationPanel(extent, size, simulator);
112 this.borderPanel = new JPanel(new BorderLayout());
113 this.borderPanel.add(this.animationPanel, BorderLayout.CENTER);
114 getTabbedPane().addTab(0, "animation", this.borderPanel);
115 getTabbedPane().setSelectedIndex(0);
116
117
118 this.gtuColorer = gtuColorer;
119 this.colorControlPanel = new ColorControlPanel(this.gtuColorer);
120 JPanel buttonPanel = new JPanel();
121 buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
122 this.borderPanel.add(buttonPanel, BorderLayout.NORTH);
123 buttonPanel.add(this.colorControlPanel);
124
125
126 this.togglePanel = new JPanel();
127 this.togglePanel.setLayout(new BoxLayout(this.togglePanel, BoxLayout.Y_AXIS));
128 this.borderPanel.add(this.togglePanel, BorderLayout.WEST);
129
130
131 buttonPanel.add(new JLabel(" "));
132 buttonPanel.add(makeButton("allButton", "/Expand.png", "ZoomAll", "Zoom whole network", true));
133 buttonPanel.add(makeButton("homeButton", "/Home.png", "Home", "Zoom to original extent", true));
134 buttonPanel.add(makeButton("gridButton", "/Grid.png", "Grid", "Toggle grid on/off", true));
135 buttonPanel.add(new JLabel(" "));
136 this.coordinateField = new JLabel("Mouse: ");
137 this.coordinateField.setMinimumSize(new Dimension(250, 10));
138 this.coordinateField.setPreferredSize(new Dimension(250, 10));
139 buttonPanel.add(this.coordinateField);
140
141
142 this.animationPanel.notify(new Event(SimulatorInterface.START_REPLICATION_EVENT, simulator, null));
143
144
145 this.animationPanel.setShowToolTip(false);
146
147
148 new UpdateTimer().start();
149
150
151 installWindowCloseHandler();
152 }
153
154
155
156
157
158
159
160
161
162
163 private JButton makeButton(final String name, final String iconPath, final String actionCommand, final String toolTipText,
164 final boolean enabled)
165 {
166
167 JButton result = new JButton(new ImageIcon(URLResource.getResource(iconPath)));
168 result.setPreferredSize(new Dimension(34, 32));
169 result.setName(name);
170 result.setEnabled(enabled);
171 result.setActionCommand(actionCommand);
172 result.setToolTipText(toolTipText);
173 result.addActionListener(this);
174 this.buttons.add(result);
175 return result;
176 }
177
178
179
180
181
182
183
184
185
186
187
188
189 public final void addToggleAnimationButtonIcon(final String name, final Class<? extends Locatable> locatableClass,
190 final String iconPath, final String toolTipText, final boolean initiallyVisible, final boolean idButton)
191 {
192 JToggleButton button;
193 button = new JCheckBox(new ImageIcon(URLResource.getResource(iconPath)));
194 button.setPreferredSize(new Dimension(32, 28));
195 button.setName(name);
196 button.setEnabled(true);
197 button.setSelected(initiallyVisible);
198 button.setActionCommand(name);
199 button.setToolTipText(toolTipText);
200 button.addActionListener(this);
201
202
203 if (idButton && this.togglePanel.getComponentCount() > 0)
204 {
205 JPanel lastToggleBox = (JPanel) this.togglePanel.getComponent(this.togglePanel.getComponentCount() - 1);
206 lastToggleBox.add(button);
207 }
208 else
209 {
210 JPanel toggleBox = new JPanel();
211 toggleBox.setLayout(new BoxLayout(toggleBox, BoxLayout.X_AXIS));
212 toggleBox.add(button);
213 this.togglePanel.add(toggleBox);
214 toggleBox.setAlignmentX(Component.LEFT_ALIGNMENT);
215 }
216
217 if (initiallyVisible)
218 {
219 this.animationPanel.showClass(locatableClass);
220 }
221 else
222 {
223 this.animationPanel.hideClass(locatableClass);
224 }
225 this.toggleLocatableMap.put(name, locatableClass);
226 }
227
228
229
230
231
232
233
234
235 public final void addToggleAnimationButtonText(final String name, final Class<? extends Locatable> locatableClass,
236 final String toolTipText, final boolean initiallyVisible)
237 {
238 JToggleButton button;
239 button = new JCheckBox(name);
240 button.setName(name);
241 button.setEnabled(true);
242 button.setSelected(initiallyVisible);
243 button.setActionCommand(name);
244 button.setToolTipText(toolTipText);
245 button.addActionListener(this);
246
247 JPanel toggleBox = new JPanel();
248 toggleBox.setLayout(new BoxLayout(toggleBox, BoxLayout.X_AXIS));
249 toggleBox.add(button);
250 this.togglePanel.add(toggleBox);
251 toggleBox.setAlignmentX(Component.LEFT_ALIGNMENT);
252
253 if (initiallyVisible)
254 {
255 this.animationPanel.showClass(locatableClass);
256 }
257 else
258 {
259 this.animationPanel.hideClass(locatableClass);
260 }
261 this.toggleLocatableMap.put(name, locatableClass);
262 }
263
264
265 @Override
266 public final void actionPerformed(final ActionEvent actionEvent)
267 {
268 String actionCommand = actionEvent.getActionCommand();
269 try
270 {
271 if (actionCommand.equals("Home"))
272 {
273 this.animationPanel.home();
274 }
275 if (actionCommand.equals("ZoomAll"))
276 {
277 this.animationPanel.zoomAll();
278 }
279 if (actionCommand.equals("Grid"))
280 {
281 this.animationPanel.showGrid(!this.animationPanel.isShowGrid());
282 }
283
284 if (this.toggleLocatableMap.containsKey(actionCommand))
285 {
286 Class<? extends Locatable> locatableClass = this.toggleLocatableMap.get(actionCommand);
287 this.animationPanel.toggleClass(locatableClass);
288 this.togglePanel.repaint();
289 }
290 }
291 catch (Exception exception)
292 {
293 exception.printStackTrace();
294 }
295 }
296
297
298
299
300
301 public final AnimationPanel getAnimationPanel()
302 {
303 return this.animationPanel;
304 }
305
306
307
308
309 protected final void updateWorldCoordinate()
310 {
311 String worldPoint = "(x=" + FORMATTER.format(this.animationPanel.getWorldCoordinate().getX()) + " ; y="
312 + FORMATTER.format(this.animationPanel.getWorldCoordinate().getY()) + ")";
313 this.coordinateField.setText("Mouse: " + worldPoint);
314 this.coordinateField.repaint();
315 }
316
317
318
319
320
321
322 public final GTUColorer getGTUColorer()
323 {
324 return this.gtuColorer;
325 }
326
327
328
329
330
331
332 public final ColorControlPanel getColorControlPanel()
333 {
334 return this.colorControlPanel;
335 }
336
337
338
339
340 public final void installWindowCloseHandler()
341 {
342 if (this.closeHandlerRegistered)
343 {
344 return;
345 }
346
347
348 new DisposeOnCloseThread(this).start();
349 }
350
351
352 protected class DisposeOnCloseThread extends Thread
353 {
354
355 private OTSAnimationPanel panel;
356
357
358
359
360 public DisposeOnCloseThread(final OTSAnimationPanel panel)
361 {
362 super();
363 this.panel = panel;
364 }
365
366
367 @Override
368 public final void run()
369 {
370 Container root = this.panel;
371 while (!(root instanceof JFrame))
372 {
373 try
374 {
375 Thread.sleep(10);
376 }
377 catch (InterruptedException exception)
378 {
379
380 }
381
382
383 root = this.panel;
384 while (null != root.getParent() && !(root instanceof JFrame))
385 {
386 root = root.getParent();
387 }
388 }
389 JFrame frame = (JFrame) root;
390 frame.addWindowListener(this.panel);
391 this.panel.closeHandlerRegistered = true;
392 }
393
394
395 @Override
396 public final String toString()
397 {
398 return "DisposeOnCloseThread of OTSAnimationPanel [panel=" + this.panel + "]";
399 }
400 }
401
402
403 @Override
404 public void windowOpened(final WindowEvent e)
405 {
406
407 }
408
409
410 @Override
411 public final void windowClosing(final WindowEvent e)
412 {
413
414 }
415
416
417 @Override
418 public final void windowClosed(final WindowEvent e)
419 {
420 this.windowExited = true;
421 }
422
423
424 @Override
425 public final void windowIconified(final WindowEvent e)
426 {
427
428 }
429
430
431 @Override
432 public final void windowDeiconified(final WindowEvent e)
433 {
434
435 }
436
437
438 @Override
439 public final void windowActivated(final WindowEvent e)
440 {
441
442 }
443
444
445 @Override
446 public final void windowDeactivated(final WindowEvent e)
447 {
448
449 }
450
451
452
453
454 protected class UpdateTimer extends Thread
455 {
456
457 @Override
458 public final void run()
459 {
460 while (!OTSAnimationPanel.this.windowExited)
461 {
462 if (OTSAnimationPanel.this.isShowing())
463 {
464 OTSAnimationPanel.this.updateWorldCoordinate();
465 }
466 try
467 {
468 Thread.sleep(50);
469 }
470 catch (InterruptedException exception)
471 {
472
473 }
474 }
475 }
476
477
478 @Override
479 public final String toString()
480 {
481 return "UpdateTimer thread for OTSAnimationPanel";
482 }
483
484 }
485 }