1   package org.opentrafficsim.swing.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.Graphics;
8   import java.awt.event.ActionEvent;
9   import java.awt.event.ActionListener;
10  import java.awt.event.ContainerEvent;
11  import java.awt.event.ContainerListener;
12  import java.awt.event.MouseAdapter;
13  import java.awt.event.MouseEvent;
14  import java.awt.event.MouseListener;
15  import java.awt.event.MouseWheelEvent;
16  import java.awt.event.MouseWheelListener;
17  import java.awt.event.WindowEvent;
18  import java.awt.event.WindowListener;
19  import java.awt.geom.Point2D;
20  import java.awt.geom.Rectangle2D;
21  import java.rmi.RemoteException;
22  import java.text.NumberFormat;
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.swing.BoxLayout;
29  import javax.swing.Icon;
30  import javax.swing.JButton;
31  import javax.swing.JCheckBox;
32  import javax.swing.JFrame;
33  import javax.swing.JLabel;
34  import javax.swing.JPanel;
35  import javax.swing.JTextField;
36  import javax.swing.JToggleButton;
37  import javax.swing.border.EmptyBorder;
38  
39  import org.opentrafficsim.core.animation.gtu.colorer.GTUColorer;
40  import org.opentrafficsim.core.dsol.OTSAnimator;
41  import org.opentrafficsim.core.dsol.OTSModelInterface;
42  import org.opentrafficsim.core.gtu.GTU;
43  import org.opentrafficsim.core.network.Network;
44  import org.opentrafficsim.core.network.OTSNetwork;
45  
46  import nl.javel.gisbeans.map.MapInterface;
47  import nl.tudelft.simulation.dsol.animation.Locatable;
48  import nl.tudelft.simulation.dsol.animation.D2.GisRenderable2D;
49  import nl.tudelft.simulation.dsol.animation.D2.Renderable2DInterface;
50  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
51  import nl.tudelft.simulation.dsol.swing.animation.D2.AnimationPanel;
52  import nl.tudelft.simulation.dsol.swing.animation.D2.GridPanel;
53  import nl.tudelft.simulation.dsol.swing.animation.D2.mouse.InputListener;
54  import nl.tudelft.simulation.event.Event;
55  import nl.tudelft.simulation.event.EventInterface;
56  import nl.tudelft.simulation.event.EventListenerInterface;
57  import nl.tudelft.simulation.language.d3.DirectedPoint;
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  public class OTSAnimationPanel extends OTSSimulationPanel implements ActionListener, WindowListener, EventListenerInterface
71  {
72      
73      private static final long serialVersionUID = 20150617L;
74  
75      
76      private final AutoAnimationPanel animationPanel;
77  
78      
79      private final JPanel borderPanel;
80  
81      
82      private final JPanel togglePanel;
83  
84      
85      private JPanel demoPanel = null;
86  
87      
88      private Map<String, Class<? extends Locatable>> toggleLocatableMap = new HashMap<>();
89  
90      
91      private Map<Class<? extends Locatable>, JToggleButton> toggleButtons = new HashMap<>();
92  
93      
94      private Map<String, MapInterface> toggleGISMap = new HashMap<>();
95  
96      
97      private Map<String, JToggleButton> toggleGISButtons = new HashMap<>();
98  
99      
100     private GTUColorer gtuColorer = null;
101 
102     
103     private ColorControlPanel colorControlPanel = null;
104 
105     
106     private final JLabel coordinateField;
107 
108     
109     private final JLabel gtuCountField;
110 
111     
112     private int gtuCount = 0;
113 
114     
115     private final ArrayList<JButton> buttons = new ArrayList<>();
116 
117     
118     private static final NumberFormat FORMATTER = NumberFormat.getInstance();
119 
120     
121     @SuppressWarnings("checkstyle:visibilitymodifier")
122     protected boolean closeHandlerRegistered = false;
123 
124     
125     @SuppressWarnings("checkstyle:visibilitymodifier")
126     protected boolean windowExited = false;
127 
128     
129     private boolean autoPan = false;
130 
131     
132     private final JCheckBox autoPanToggle;
133 
134     
135     private final JTextField autoPanField;
136 
137     
138     static
139     {
140         FORMATTER.setMaximumFractionDigits(3);
141     }
142 
143     
144 
145 
146 
147 
148 
149 
150 
151 
152 
153     public OTSAnimationPanel(final Rectangle2D extent, final Dimension size, final OTSAnimator simulator,
154             final OTSModelInterface otsModel, final GTUColorer gtuColorer, final OTSNetwork network) throws RemoteException
155     {
156         super(simulator, otsModel);
157 
158         
159 
160         this.animationPanel = new AutoAnimationPanel(extent, size, simulator, network);
161         this.animationPanel.showGrid(false);
162         this.borderPanel = new JPanel(new BorderLayout());
163         this.borderPanel.add(this.animationPanel, BorderLayout.CENTER);
164         getTabbedPane().addTab(0, "animation", this.borderPanel);
165         getTabbedPane().setSelectedIndex(0); 
166 
167         
168         this.gtuColorer = gtuColorer;
169         this.colorControlPanel = new ColorControlPanel(this.gtuColorer);
170         JPanel buttonPanel = new JPanel();
171         buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
172         this.borderPanel.add(buttonPanel, BorderLayout.NORTH);
173         buttonPanel.add(this.colorControlPanel);
174 
175         
176         this.togglePanel = new JPanel();
177         this.togglePanel.setLayout(new BoxLayout(this.togglePanel, BoxLayout.Y_AXIS));
178         this.borderPanel.add(this.togglePanel, BorderLayout.WEST);
179 
180         
181         buttonPanel.add(new JLabel("   "));
182         buttonPanel.add(makeButton("allButton", "/Expand.png", "ZoomAll", "Zoom whole network", true));
183         buttonPanel.add(makeButton("homeButton", "/Home.png", "Home", "Zoom to original extent", true));
184         buttonPanel.add(makeButton("gridButton", "/Grid.png", "Grid", "Toggle grid on/off", true));
185         buttonPanel.add(new JLabel("   "));
186 
187         
188         JPanel infoTextPanel = new JPanel();
189         buttonPanel.add(infoTextPanel);
190         infoTextPanel.setMinimumSize(new Dimension(250, 20));
191         infoTextPanel.setPreferredSize(new Dimension(250, 20));
192         infoTextPanel.setLayout(new BoxLayout(infoTextPanel, BoxLayout.Y_AXIS));
193         this.coordinateField = new JLabel("Mouse: ");
194         this.coordinateField.setMinimumSize(new Dimension(250, 10));
195         this.coordinateField.setPreferredSize(new Dimension(250, 10));
196         infoTextPanel.add(this.coordinateField);
197         
198         JPanel gtuPanel = new JPanel();
199         gtuPanel.setAlignmentX(0.0f);
200         gtuPanel.setLayout(new BoxLayout(gtuPanel, BoxLayout.X_AXIS));
201         gtuPanel.setMinimumSize(new Dimension(250, 10));
202         gtuPanel.setPreferredSize(new Dimension(250, 10));
203         infoTextPanel.add(gtuPanel);
204         if (null != network)
205         {
206             network.addListener(this, Network.GTU_ADD_EVENT);
207             network.addListener(this, Network.GTU_REMOVE_EVENT);
208         }
209         
210         
211         class AppearanceTextField extends JTextField implements AppearanceControl
212         {
213             private static final long serialVersionUID = 20180430L;
214 
215             
216             @Override
217             public String toString()
218             {
219                 return "AppearanceTextField []";
220             }
221         }
222         this.autoPanField = new AppearanceTextField();
223         this.autoPanField.setMaximumSize(new Dimension(100, 20));
224         this.autoPanField.setVisible(false);
225         this.autoPanField.addActionListener(new ActionListener()
226         {
227             @SuppressWarnings("synthetic-access")
228             @Override
229             public void actionPerformed(final ActionEvent e)
230             {
231                 OTSAnimationPanel.this.animationPanel.repaint();
232             }
233         });
234         this.autoPanToggle = new JCheckBox();
235         this.autoPanToggle.setToolTipText("Pan to GTU");
236         this.autoPanToggle.addActionListener(new ActionListener()
237         {
238             @SuppressWarnings("synthetic-access")
239             @Override
240             public void actionPerformed(final ActionEvent e)
241             {
242                 OTSAnimationPanel.this.autoPan = !OTSAnimationPanel.this.autoPanField.isVisible();
243                 OTSAnimationPanel.this.autoPanField.setVisible(OTSAnimationPanel.this.autoPan);
244                 if (OTSAnimationPanel.this.autoPan)
245                 {
246                     OTSAnimationPanel.this.autoPanField.requestFocusInWindow();
247                     OTSAnimationPanel.this.autoPanField.selectAll();
248                 }
249                 gtuPanel.revalidate();
250             }
251         });
252         gtuPanel.add(this.autoPanToggle);
253         gtuPanel.add(this.autoPanField);
254         
255         this.gtuCountField = new JLabel("0 GTU's");
256         this.gtuCount = null == network ? 0 : network.getGTUs().size();
257         gtuPanel.add(this.gtuCountField);
258         setGtuCountText();
259 
260         
261         this.animationPanel.notify(new Event(SimulatorInterface.START_REPLICATION_EVENT, simulator, null));
262 
263         
264         this.animationPanel.setShowToolTip(false);
265 
266         
267         new UpdateTimer().start();
268 
269         
270         installWindowCloseHandler();
271 
272     }
273 
274     
275 
276 
277 
278 
279 
280 
281 
282 
283     private JButton makeButton(final String name, final String iconPath, final String actionCommand, final String toolTipText,
284             final boolean enabled)
285     {
286         
287         JButton result = new JButton(OTSControlPanel.loadIcon(iconPath));
288         result.setPreferredSize(new Dimension(34, 32));
289         result.setName(name);
290         result.setEnabled(enabled);
291         result.setActionCommand(actionCommand);
292         result.setToolTipText(toolTipText);
293         result.addActionListener(this);
294         this.buttons.add(result);
295         return result;
296     }
297 
298     
299 
300 
301 
302 
303 
304 
305 
306 
307 
308 
309     public final void addToggleAnimationButtonIcon(final String name, final Class<? extends Locatable> locatableClass,
310             final String iconPath, final String toolTipText, final boolean initiallyVisible, final boolean idButton)
311     {
312         JToggleButton button;
313         Icon icon = OTSControlPanel.loadIcon(iconPath);
314         Icon unIcon = OTSControlPanel.loadGrayscaleIcon(iconPath);
315         button = new JCheckBox();
316         button.setSelectedIcon(icon);
317         button.setIcon(unIcon);
318         button.setPreferredSize(new Dimension(32, 28));
319         button.setName(name);
320         button.setEnabled(true);
321         button.setSelected(initiallyVisible);
322         button.setActionCommand(name);
323         button.setToolTipText(toolTipText);
324         button.addActionListener(this);
325 
326         
327         if (idButton && this.togglePanel.getComponentCount() > 0)
328         {
329             JPanel lastToggleBox = (JPanel) this.togglePanel.getComponent(this.togglePanel.getComponentCount() - 1);
330             lastToggleBox.add(button);
331         }
332         else
333         {
334             JPanel toggleBox = new JPanel();
335             toggleBox.setLayout(new BoxLayout(toggleBox, BoxLayout.X_AXIS));
336             toggleBox.add(button);
337             this.togglePanel.add(toggleBox);
338             toggleBox.setAlignmentX(Component.LEFT_ALIGNMENT);
339         }
340 
341         if (initiallyVisible)
342         {
343             this.animationPanel.showClass(locatableClass);
344         }
345         else
346         {
347             this.animationPanel.hideClass(locatableClass);
348         }
349         this.toggleLocatableMap.put(name, locatableClass);
350         this.toggleButtons.put(locatableClass, button);
351     }
352 
353     
354 
355 
356 
357 
358 
359 
360     public final void addToggleAnimationButtonText(final String name, final Class<? extends Locatable> locatableClass,
361             final String toolTipText, final boolean initiallyVisible)
362     {
363         JToggleButton button;
364         button = new JCheckBox(name);
365         button.setName(name);
366         button.setEnabled(true);
367         button.setSelected(initiallyVisible);
368         button.setActionCommand(name);
369         button.setToolTipText(toolTipText);
370         button.addActionListener(this);
371 
372         JPanel toggleBox = new JPanel();
373         toggleBox.setLayout(new BoxLayout(toggleBox, BoxLayout.X_AXIS));
374         toggleBox.add(button);
375         this.togglePanel.add(toggleBox);
376         toggleBox.setAlignmentX(Component.LEFT_ALIGNMENT);
377 
378         if (initiallyVisible)
379         {
380             this.animationPanel.showClass(locatableClass);
381         }
382         else
383         {
384             this.animationPanel.hideClass(locatableClass);
385         }
386         this.toggleLocatableMap.put(name, locatableClass);
387         this.toggleButtons.put(locatableClass, button);
388     }
389 
390     
391 
392 
393 
394     public final void addToggleText(final String text)
395     {
396         JPanel textBox = new JPanel();
397         textBox.setLayout(new BoxLayout(textBox, BoxLayout.X_AXIS));
398         textBox.add(new JLabel(text));
399         this.togglePanel.add(textBox);
400         textBox.setAlignmentX(Component.LEFT_ALIGNMENT);
401     }
402 
403     
404 
405 
406 
407 
408 
409     public final void addAllToggleGISButtonText(final String header, final GisRenderable2D gisMap, final String toolTipText)
410     {
411         addToggleText(" ");
412         addToggleText(header);
413         try
414         {
415             for (String layerName : gisMap.getMap().getLayerMap().keySet())
416             {
417                 addToggleGISButtonText(layerName, layerName, gisMap, toolTipText);
418             }
419         }
420         catch (RemoteException exception)
421         {
422             exception.printStackTrace();
423         }
424     }
425 
426     
427 
428 
429 
430 
431 
432 
433     public final void addToggleGISButtonText(final String layerName, final String displayName, final GisRenderable2D gisMap,
434             final String toolTipText)
435     {
436         JToggleButton button;
437         button = new JCheckBox(displayName);
438         button.setName(layerName);
439         button.setEnabled(true);
440         button.setSelected(true);
441         button.setActionCommand(layerName);
442         button.setToolTipText(toolTipText);
443         button.addActionListener(this);
444 
445         JPanel toggleBox = new JPanel();
446         toggleBox.setLayout(new BoxLayout(toggleBox, BoxLayout.X_AXIS));
447         toggleBox.add(button);
448         this.togglePanel.add(toggleBox);
449         toggleBox.setAlignmentX(Component.LEFT_ALIGNMENT);
450 
451         this.toggleGISMap.put(layerName, gisMap.getMap());
452         this.toggleGISButtons.put(layerName, button);
453     }
454 
455     
456 
457 
458 
459     public final void showGISLayer(final String layerName)
460     {
461         MapInterface gisMap = this.toggleGISMap.get(layerName);
462         if (gisMap != null)
463         {
464             try
465             {
466                 gisMap.showLayer(layerName);
467                 this.toggleGISButtons.get(layerName).setSelected(true);
468                 this.animationPanel.repaint();
469             }
470             catch (RemoteException exception)
471             {
472                 exception.printStackTrace();
473             }
474         }
475     }
476 
477     
478 
479 
480 
481     public final void hideGISLayer(final String layerName)
482     {
483         MapInterface gisMap = this.toggleGISMap.get(layerName);
484         if (gisMap != null)
485         {
486             try
487             {
488                 gisMap.hideLayer(layerName);
489                 this.toggleGISButtons.get(layerName).setSelected(false);
490                 this.animationPanel.repaint();
491             }
492             catch (RemoteException exception)
493             {
494                 exception.printStackTrace();
495             }
496         }
497     }
498 
499     
500 
501 
502 
503     public final void toggleGISLayer(final String layerName)
504     {
505         MapInterface gisMap = this.toggleGISMap.get(layerName);
506         if (gisMap != null)
507         {
508             try
509             {
510                 if (gisMap.getVisibleLayers().contains(gisMap.getLayerMap().get(layerName)))
511                 {
512                     gisMap.hideLayer(layerName);
513                     this.toggleGISButtons.get(layerName).setSelected(false);
514                 }
515                 else
516                 {
517                     gisMap.showLayer(layerName);
518                     this.toggleGISButtons.get(layerName).setSelected(true);
519                 }
520                 this.animationPanel.repaint();
521             }
522             catch (RemoteException exception)
523             {
524                 exception.printStackTrace();
525             }
526         }
527     }
528 
529     
530     @Override
531     public final void actionPerformed(final ActionEvent actionEvent)
532     {
533         String actionCommand = actionEvent.getActionCommand();
534         
535         try
536         {
537             if (actionCommand.equals("Home"))
538             {
539                 this.animationPanel.home();
540             }
541             if (actionCommand.equals("ZoomAll"))
542             {
543                 this.animationPanel.zoomAll();
544             }
545             if (actionCommand.equals("Grid"))
546             {
547                 this.animationPanel.showGrid(!this.animationPanel.isShowGrid());
548             }
549 
550             if (this.toggleLocatableMap.containsKey(actionCommand))
551             {
552                 Class<? extends Locatable> locatableClass = this.toggleLocatableMap.get(actionCommand);
553                 this.animationPanel.toggleClass(locatableClass);
554                 this.togglePanel.repaint();
555             }
556 
557             if (this.toggleGISMap.containsKey(actionCommand))
558             {
559                 this.toggleGISLayer(actionCommand);
560                 this.togglePanel.repaint();
561             }
562         }
563         catch (Exception exception)
564         {
565             exception.printStackTrace();
566         }
567     }
568 
569     
570 
571 
572 
573     public final AnimationPanel getAnimationPanel()
574     {
575         return this.animationPanel;
576     }
577 
578     
579 
580 
581 
582     public JPanel getDemoPanel()
583     {
584         if (this.demoPanel == null)
585         {
586             this.demoPanel = new JPanel();
587             this.demoPanel.setLayout(new BoxLayout(this.demoPanel, BoxLayout.Y_AXIS));
588             this.demoPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
589             this.demoPanel.setPreferredSize(new Dimension(300, 300));
590             getAnimationPanel().getParent().add(this.demoPanel, BorderLayout.EAST);
591             this.demoPanel.addContainerListener(new ContainerListener()
592             {
593                 @Override
594                 public void componentAdded(final ContainerEvent e)
595                 {
596                     try
597                     {
598                         
599                     }
600                     catch (NullPointerException exception)
601                     {
602                         
603                     }
604                 }
605 
606                 @Override
607                 public void componentRemoved(final ContainerEvent e)
608                 {
609                     
610                 }
611             });
612         }
613         return this.demoPanel;
614     }
615 
616     
617 
618 
619 
620     public final void updateAnimationClassCheckBox(final Class<? extends Locatable> locatableClass)
621     {
622         JToggleButton button = this.toggleButtons.get(locatableClass);
623         if (button == null)
624         {
625             return;
626         }
627         button.setSelected(getAnimationPanel().isShowClass(locatableClass));
628     }
629 
630     
631 
632 
633     protected final void updateWorldCoordinate()
634     {
635         String worldPoint = "(x=" + FORMATTER.format(this.animationPanel.getWorldCoordinate().getX()) + " ; y="
636                 + FORMATTER.format(this.animationPanel.getWorldCoordinate().getY()) + ")";
637         this.coordinateField.setText("Mouse: " + worldPoint);
638         int requiredWidth = this.coordinateField.getGraphics().getFontMetrics().stringWidth(this.coordinateField.getText());
639         if (this.coordinateField.getPreferredSize().width < requiredWidth)
640         {
641             Dimension requiredSize = new Dimension(requiredWidth, this.coordinateField.getPreferredSize().height);
642             this.coordinateField.setPreferredSize(requiredSize);
643             this.coordinateField.setMinimumSize(requiredSize);
644             Container parent = this.coordinateField.getParent();
645             parent.setPreferredSize(requiredSize);
646             parent.setMinimumSize(requiredSize);
647             
648             parent.revalidate();
649         }
650         this.coordinateField.repaint();
651     }
652 
653     
654 
655 
656 
657 
658     public final GTUColorer getGTUColorer()
659     {
660         return this.gtuColorer;
661     }
662 
663     
664 
665 
666 
667 
668     public final ColorControlPanel getColorControlPanel()
669     {
670         return this.colorControlPanel;
671     }
672 
673     
674 
675 
676     public final void installWindowCloseHandler()
677     {
678         if (this.closeHandlerRegistered)
679         {
680             return;
681         }
682 
683         
684         new DisposeOnCloseThread(this).start();
685     }
686 
687     
688     protected class DisposeOnCloseThread extends Thread
689     {
690         
691         private OTSAnimationPanel panel;
692 
693         
694 
695 
696         public DisposeOnCloseThread(final OTSAnimationPanel panel)
697         {
698             super();
699             this.panel = panel;
700         }
701 
702         
703         @Override
704         public final void run()
705         {
706             Container root = this.panel;
707             while (!(root instanceof JFrame))
708             {
709                 try
710                 {
711                     Thread.sleep(10);
712                 }
713                 catch (InterruptedException exception)
714                 {
715                     
716                 }
717 
718                 
719                 root = this.panel;
720                 while (null != root.getParent() && !(root instanceof JFrame))
721                 {
722                     root = root.getParent();
723                 }
724             }
725             JFrame frame = (JFrame) root;
726             frame.addWindowListener(this.panel);
727             this.panel.closeHandlerRegistered = true;
728         }
729 
730         
731         @Override
732         public final String toString()
733         {
734             return "DisposeOnCloseThread of OTSAnimationPanel [panel=" + this.panel + "]";
735         }
736     }
737 
738     
739     @Override
740     public void windowOpened(final WindowEvent e)
741     {
742         
743     }
744 
745     
746     @Override
747     public final void windowClosing(final WindowEvent e)
748     {
749         
750     }
751 
752     
753     @Override
754     public final void windowClosed(final WindowEvent e)
755     {
756         this.windowExited = true;
757     }
758 
759     
760     @Override
761     public final void windowIconified(final WindowEvent e)
762     {
763         
764     }
765 
766     
767     @Override
768     public final void windowDeiconified(final WindowEvent e)
769     {
770         
771     }
772 
773     
774     @Override
775     public final void windowActivated(final WindowEvent e)
776     {
777         
778     }
779 
780     
781     @Override
782     public final void windowDeactivated(final WindowEvent e)
783     {
784         
785     }
786 
787     
788     @Override
789     public void notify(final EventInterface event) throws RemoteException
790     {
791         if (event.getType().equals(Network.GTU_ADD_EVENT))
792         {
793             this.gtuCount++;
794             setGtuCountText();
795         }
796         else if (event.getType().equals(Network.GTU_REMOVE_EVENT))
797         {
798             this.gtuCount--;
799             setGtuCountText();
800         }
801     }
802 
803     
804 
805 
806     private void setGtuCountText()
807     {
808         this.gtuCountField.setText(this.gtuCount + " GTU's");
809     }
810 
811     
812 
813 
814     protected class UpdateTimer extends Thread
815     {
816         
817         @Override
818         public final void run()
819         {
820             while (!OTSAnimationPanel.this.windowExited)
821             {
822                 if (OTSAnimationPanel.this.isShowing())
823                 {
824                     OTSAnimationPanel.this.updateWorldCoordinate();
825                 }
826                 try
827                 {
828                     Thread.sleep(50); 
829                 }
830                 catch (InterruptedException exception)
831                 {
832                     
833                 }
834             }
835         }
836 
837         
838         @Override
839         public final String toString()
840         {
841             return "UpdateTimer thread for OTSAnimationPanel";
842         }
843 
844     }
845 
846     
847 
848 
849 
850 
851 
852 
853 
854 
855 
856 
857 
858 
859     private class AutoAnimationPanel extends AnimationPanel
860     {
861 
862         
863         private static final long serialVersionUID = 20180430L;
864 
865         
866         private final OTSNetwork network;
867 
868         
869         private GTU lastGtu;
870 
871         
872 
873 
874 
875 
876 
877 
878 
879         AutoAnimationPanel(final Rectangle2D extent, final Dimension size, final SimulatorInterface<?, ?, ?> simulator,
880                 final OTSNetwork network) throws RemoteException
881         {
882             super(extent, size, simulator);
883             this.network = network;
884             MouseListener[] listeners = getMouseListeners();
885             for (MouseListener listener : listeners)
886             {
887                 removeMouseListener(listener);
888             }
889             this.addMouseListener(new MouseAdapter()
890             {
891                 
892                 @SuppressWarnings("synthetic-access")
893                 @Override
894                 public void mouseClicked(final MouseEvent e)
895                 {
896                     if (e.isControlDown())
897                     {
898                         GTU gtu = getSelectedGTU(e.getPoint());
899                         if (gtu != null)
900                         {
901                             OTSAnimationPanel.this.autoPanField.setText(gtu.getId());
902                             OTSAnimationPanel.this.autoPanField.setVisible(true);
903                             OTSAnimationPanel.this.autoPanToggle.setSelected(true);
904                             OTSAnimationPanel.this.autoPanToggle.getParent().validate();
905                             OTSAnimationPanel.this.autoPan = true;
906                             e.consume(); 
907                         }
908                     }
909                     e.consume();
910                 }
911             });
912             for (MouseListener listener : listeners)
913             {
914                 addMouseListener(listener);
915             }
916             
917             MouseWheelListener[] wheelListeners = getMouseWheelListeners();
918             for (MouseWheelListener wheelListener : wheelListeners)
919             {
920                 removeMouseWheelListener(wheelListener);
921             }
922             this.addMouseWheelListener(new InputListener(this)
923             {
924                 
925                 @Override
926                 public void mouseWheelMoved(final MouseWheelEvent e)
927                 {
928                     if (e.isShiftDown())
929                     {
930                         int amount = e.getUnitsToScroll();
931                         if (amount > 0)
932                         {
933                             zoomVertical(GridPanel.ZOOMFACTOR, e.getX(), e.getY());
934                         }
935                         else
936                         {
937                             zoomVertical(1.0 / GridPanel.ZOOMFACTOR, e.getX(), e.getY());
938                         }
939                     }
940                     else if (e.isAltDown())
941                     {
942                         int amount = e.getUnitsToScroll();
943                         if (amount > 0)
944                         {
945                             zoomHorizontal(GridPanel.ZOOMFACTOR, e.getX(), e.getY());
946                         }
947                         else
948                         {
949                             zoomHorizontal(1.0 / GridPanel.ZOOMFACTOR, e.getX(), e.getY());
950                         }
951                     }
952                     else
953                     {
954                         super.mouseWheelMoved(e);
955                     }
956                 }
957             });
958         }
959 
960         
961 
962 
963 
964 
965 
966         final synchronized void zoomVertical(final double factor, final int mouseX, final int mouseY)
967         {
968             
969             this.zoom(factor, mouseX, mouseY);
970             
971             
972             
973             
974             
975             
976             
977             
978             
979         }
980 
981         
982 
983 
984 
985 
986 
987         final synchronized void zoomHorizontal(final double factor, final int mouseX, final int mouseY)
988         {
989             this.zoom(factor, mouseX, mouseY);
990             
991             
992             
993             
994             
995             
996             
997             
998             
999         }
1000 
1001         
1002 
1003 
1004 
1005 
1006         @SuppressWarnings("synthetic-access")
1007         protected GTU getSelectedGTU(final Point2D mousePoint)
1008         {
1009             List<GTU> targets = new ArrayList<>();
1010             Point2D point = Renderable2DInterface.Util.getWorldCoordinates(mousePoint,
1011                     OTSAnimationPanel.this.animationPanel.getExtent(), OTSAnimationPanel.this.animationPanel.getSize());
1012             for (Renderable2DInterface<?> renderable : OTSAnimationPanel.this.animationPanel.getElements())
1013             {
1014                 if (OTSAnimationPanel.this.animationPanel.isShowElement(renderable) && renderable.contains(point,
1015                         OTSAnimationPanel.this.animationPanel.getExtent(), OTSAnimationPanel.this.animationPanel.getSize()))
1016                 {
1017                     if (renderable.getSource() instanceof GTU)
1018                     {
1019                         targets.add((GTU) renderable.getSource());
1020                     }
1021                 }
1022             }
1023             if (targets.size() == 1)
1024             {
1025                 return targets.get(0);
1026             }
1027             return null;
1028         }
1029 
1030         
1031         @SuppressWarnings("synthetic-access")
1032         @Override
1033         public void paintComponent(final Graphics g)
1034         {
1035             if (OTSAnimationPanel.this.autoPan)
1036             {
1037                 String id = OTSAnimationPanel.this.autoPanField.getText();
1038                 if (this.lastGtu == null || !this.lastGtu.getId().equals(id) || this.lastGtu.isDestroyed())
1039                 {
1040                     this.lastGtu = this.network.getGTU(id);
1041                 }
1042                 DirectedPoint point;
1043                 try
1044                 {
1045                     point = this.lastGtu != null ? this.lastGtu.getLocation() : null;
1046                 }
1047                 catch (RemoteException exception)
1048                 {
1049                     System.err.println("Could not pan to GTU location.");
1050                     return;
1051                 }
1052                 if (point != null)
1053                 {
1054                     double w = this.extent.getWidth();
1055                     double h = this.extent.getHeight();
1056                     this.extent = new Rectangle2D.Double(point.getX() - w / 2, point.getY() - h / 2, w, h);
1057                 }
1058             }
1059             super.paintComponent(g);
1060         }
1061 
1062         
1063         @Override
1064         public String toString()
1065         {
1066             return "AutoAnimationPanel [network=" + this.network + ", lastGtu=" + this.lastGtu + "]";
1067         }
1068     }
1069 
1070 }