1 package org.opentrafficsim.swing.gui;
2
3 import java.awt.Component;
4 import java.awt.Font;
5 import java.awt.Frame;
6 import java.awt.event.MouseAdapter;
7 import java.awt.event.MouseEvent;
8 import java.awt.event.WindowAdapter;
9 import java.awt.event.WindowEvent;
10 import java.awt.geom.Rectangle2D;
11 import java.io.File;
12 import java.io.FileReader;
13 import java.io.FileWriter;
14 import java.io.IOException;
15 import java.util.Dictionary;
16 import java.util.Enumeration;
17 import java.util.Properties;
18
19 import javax.imageio.ImageIO;
20 import javax.swing.ButtonGroup;
21 import javax.swing.JCheckBoxMenuItem;
22 import javax.swing.JComponent;
23 import javax.swing.JFrame;
24 import javax.swing.JLabel;
25 import javax.swing.JMenu;
26 import javax.swing.JMenuItem;
27 import javax.swing.JPanel;
28 import javax.swing.JPopupMenu;
29 import javax.swing.JSlider;
30 import javax.swing.MenuElement;
31 import javax.swing.MenuSelectionManager;
32 import javax.swing.WindowConstants;
33 import javax.swing.event.ChangeEvent;
34 import javax.swing.event.ChangeListener;
35
36 import org.opentrafficsim.core.animation.gtu.colorer.DefaultSwitchableGtuColorer;
37 import org.opentrafficsim.core.animation.gtu.colorer.GtuColorer;
38 import org.opentrafficsim.core.dsol.OtsModelInterface;
39
40 import nl.tudelft.simulation.dsol.swing.animation.D2.AnimationPanel;
41
42
43
44
45
46
47
48
49
50
51
52
53 public class OtsSwingApplication<T extends OtsModelInterface> extends JFrame
54 {
55
56 private static final long serialVersionUID = 20141216L;
57
58
59 public static final GtuColorer DEFAULT_COLORER = new DefaultSwitchableGtuColorer();
60
61
62 private final T model;
63
64
65 @SuppressWarnings("checkstyle:visibilitymodifier")
66 protected boolean closed = false;
67
68
69 protected Properties frameProperties;
70
71
72 private Appearance appearance = Appearance.GRAY;
73
74
75
76
77
78
79 public OtsSwingApplication(final T model, final JPanel panel)
80 {
81 this.model = model;
82 setTitle("OTS | The Open Traffic Simulator | " + model.getDescription());
83 setContentPane(panel);
84 pack();
85 setExtendedState(Frame.MAXIMIZED_BOTH);
86 setVisible(true);
87
88 setExitOnClose(true);
89 addWindowListener(new WindowAdapter()
90 {
91 @Override
92 public void windowClosing(final WindowEvent windowEvent)
93 {
94 OtsSwingApplication.this.closed = true;
95 super.windowClosing(windowEvent);
96 }
97 });
98
99
100
101
102
103 try
104 {
105 setIconImage(ImageIO.read(Resource.getResourceAsStream("/OTS_merge.png")));
106 }
107 catch (IOException io)
108 {
109
110 }
111
112
113 String sep = System.getProperty("file.separator");
114 String propertiesFile = System.getProperty("user.home") + sep + "OTS" + sep + "properties.ini";
115 addWindowListener(new WindowAdapter()
116 {
117
118 @Override
119 public void windowClosing(final WindowEvent windowEvent)
120 {
121 try
122 {
123 File f = new File(propertiesFile);
124 f.getParentFile().mkdirs();
125 FileWriter writer = new FileWriter(f);
126 OtsSwingApplication.this.frameProperties.store(writer, "OTS user settings");
127 }
128 catch (IOException exception)
129 {
130 System.err.println("Could not store properties at " + propertiesFile + ".");
131 }
132 }
133 });
134
135
136 Properties defaults = new Properties();
137 defaults.setProperty("Appearance", "GRAY");
138 this.frameProperties = new Properties(defaults);
139 try
140 {
141 FileReader reader = new FileReader(propertiesFile);
142 this.frameProperties.load(reader);
143 }
144 catch (IOException ioe)
145 {
146
147 }
148 this.appearance = Appearance.valueOf(this.frameProperties.getProperty("Appearance").toUpperCase());
149
150
151 class AppearanceControlMenu extends JMenu implements AppearanceControl
152 {
153
154 private static final long serialVersionUID = 20180206L;
155
156
157
158
159
160 AppearanceControlMenu(final String string)
161 {
162 super(string);
163 }
164
165
166 @Override
167 public boolean isFont()
168 {
169 return true;
170 }
171
172
173 @Override
174 public String toString()
175 {
176 return "AppearanceControlMenu []";
177 }
178 }
179
180
181 JMenu app = new AppearanceControlMenu("Appearance");
182 app.addMouseListener(new SubMenuShower(app));
183 ButtonGroup appGroup = new ButtonGroup();
184 for (Appearance appearanceValue : Appearance.values())
185 {
186 appGroup.add(addAppearance(app, appearanceValue));
187 }
188
189
190 class AppearanceControlPopupMenu extends JPopupMenu implements AppearanceControl
191 {
192
193 private static final long serialVersionUID = 20180206L;
194
195
196 @Override
197 public boolean isFont()
198 {
199 return true;
200 }
201
202
203 @Override
204 public String toString()
205 {
206 return "AppearanceControlPopupMenu []";
207 }
208 }
209
210
211 JPopupMenu popMenu = new AppearanceControlPopupMenu();
212 popMenu.add(app);
213 panel.setComponentPopupMenu(popMenu);
214
215
216 setAppearance(getAppearance());
217 }
218
219
220
221
222
223 public void setAppearance(final Appearance appearance)
224 {
225 this.appearance = appearance;
226 setAppearance(this.getContentPane(), appearance);
227 this.frameProperties.setProperty("Appearance", appearance.toString());
228 }
229
230
231
232
233
234
235 private void setAppearance(final Component c, final Appearance appear)
236 {
237 if (c instanceof AppearanceControl)
238 {
239 AppearanceControl ac = (AppearanceControl) c;
240 if (ac.isBackground())
241 {
242 c.setBackground(appear.getBackground());
243 }
244 if (ac.isForeground())
245 {
246 c.setForeground(appear.getForeground());
247 }
248 if (ac.isFont())
249 {
250 changeFont(c, appear.getFont());
251 }
252 }
253 else if (c instanceof AnimationPanel)
254 {
255
256 c.setBackground(appear.getBackdrop());
257 c.setForeground(appear.getForeground());
258 changeFont(c, appear.getFont());
259 }
260 else
261 {
262
263 c.setBackground(appear.getBackground());
264 c.setForeground(appear.getForeground());
265 changeFont(c, appear.getFont());
266 }
267 if (c instanceof JSlider)
268 {
269
270 Dictionary<?, ?> dictionary = ((JSlider) c).getLabelTable();
271 Enumeration<?> keys = dictionary.keys();
272 while (keys.hasMoreElements())
273 {
274 JLabel label = (JLabel) dictionary.get(keys.nextElement());
275 label.setForeground(appear.getForeground());
276 label.setBackground(appear.getBackground());
277 }
278 }
279
280 if (c instanceof JComponent)
281 {
282 for (Component child : ((JComponent) c).getComponents())
283 {
284 setAppearance(child, appear);
285 }
286 }
287 }
288
289
290
291
292
293
294 private void changeFont(final Component c, final String font)
295 {
296 Font prev = c.getFont();
297 c.setFont(new Font(font, prev.getStyle(), prev.getSize()));
298 }
299
300
301
302
303
304 public Appearance getAppearance()
305 {
306 return this.appearance;
307 }
308
309
310
311
312
313
314
315 private JMenuItem addAppearance(final JMenu group, final Appearance appear)
316 {
317 JCheckBoxMenuItem check = new StayOpenCheckBoxMenuItem(appear.getName(), appear.equals(getAppearance()));
318 check.addMouseListener(new MouseAdapter()
319 {
320
321 @Override
322 public void mouseClicked(final MouseEvent e)
323 {
324 setAppearance(appear);
325 }
326 });
327 return group.add(check);
328 }
329
330
331
332
333
334
335 @SuppressWarnings("checkstyle:designforextension")
336 protected Rectangle2D makeAnimationRectangle()
337 {
338 return this.model.getNetwork().getExtent();
339 }
340
341
342
343
344 public final void setExitOnClose(final boolean exitOnClose)
345 {
346 if (exitOnClose)
347 {
348 setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
349 }
350 else
351 {
352 setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
353 }
354 }
355
356
357
358
359 public final boolean isClosed()
360 {
361 return this.closed;
362 }
363
364
365
366
367 public final T getModel()
368 {
369 return this.model;
370 }
371
372
373
374
375
376
377
378
379
380
381
382
383 private class SubMenuShower extends MouseAdapter
384 {
385
386 private JMenu menu;
387
388
389
390
391
392 SubMenuShower(final JMenu menu)
393 {
394 this.menu = menu;
395 }
396
397
398 @Override
399 public void mouseEntered(final MouseEvent e)
400 {
401 MenuSelectionManager.defaultManager().setSelectedPath(
402 new MenuElement[] {(MenuElement) this.menu.getParent(), this.menu, this.menu.getPopupMenu()});
403 }
404
405
406 @Override
407 public String toString()
408 {
409 return "SubMenuShower [menu=" + this.menu + "]";
410 }
411 }
412
413
414
415
416
417
418
419
420
421
422
423
424 private static class StayOpenCheckBoxMenuItem extends JCheckBoxMenuItem implements AppearanceControl
425 {
426
427 private static final long serialVersionUID = 20180206L;
428
429
430 private static MenuElement[] path;
431
432 {
433 getModel().addChangeListener(new ChangeListener()
434 {
435
436 @Override
437 public void stateChanged(final ChangeEvent e)
438 {
439 if (getModel().isArmed() && isShowing())
440 {
441 setPath(MenuSelectionManager.defaultManager().getSelectedPath());
442 }
443 }
444 });
445 }
446
447
448
449
450
451 public static void setPath(final MenuElement[] path)
452 {
453 StayOpenCheckBoxMenuItem.path = path;
454 }
455
456
457
458
459
460
461 StayOpenCheckBoxMenuItem(final String text, final boolean selected)
462 {
463 super(text, selected);
464 }
465
466
467 @Override
468 public void doClick(final int pressTime)
469 {
470 super.doClick(pressTime);
471 for (MenuElement element : path)
472 {
473 if (element instanceof JComponent)
474 {
475 ((JComponent) element).setVisible(true);
476 }
477 }
478 JMenu menu = (JMenu) path[path.length - 3];
479 MenuSelectionManager.defaultManager()
480 .setSelectedPath(new MenuElement[] {(MenuElement) menu.getParent(), menu, menu.getPopupMenu()});
481 }
482
483
484 @Override
485 public boolean isFont()
486 {
487 return true;
488 }
489
490
491 @Override
492 public String toString()
493 {
494 return "StayOpenCheckBoxMenuItem []";
495 }
496 }
497
498 }