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 }