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