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