source: tmcsimulator-scriptbuilder/trunk/src/scriptbuilder/gui/panels/IncidentTimelinePanel.java @ 88

Revision 88, 15.7 KB checked in by jdalbey, 9 years ago (diff)

IncidentTimelinePanel?.java: Added right-click context menu ("popup") with stubbed out functionality.

Line 
1package scriptbuilder.gui.panels;
2
3import event.editor.Editor;
4import event.editor.Properties;
5import java.awt.BorderLayout;
6import java.awt.Dimension;
7import java.awt.Graphics;
8import java.awt.Graphics2D;
9import java.awt.event.ActionEvent;
10import java.awt.event.ActionListener;
11import java.awt.event.MouseEvent;
12import java.io.File;
13import java.util.HashMap;
14import java.util.Map;
15import javax.swing.JFrame;
16import javax.swing.JMenuItem;
17import javax.swing.JOptionPane;
18import javax.swing.JPanel;
19import javax.swing.JPopupMenu;
20import javax.swing.event.MouseInputAdapter;
21import scriptbuilder.gui.IncidentEditorFrame;
22import scriptbuilder.gui.ScriptBuilderFrame;
23import scriptbuilder.gui.ScriptBuilderGuiConstants;
24import scriptbuilder.gui.drawers.CursorDrawer;
25import scriptbuilder.gui.drawers.EventIconDrawer;
26import scriptbuilder.gui.drawers.IncidentTimelineDrawer;
27import scriptbuilder.structures.ScriptEvent;
28import scriptbuilder.structures.ScriptEvent.ScriptEventType;
29import scriptbuilder.structures.ScriptIncident;
30import scriptbuilder.structures.SimulationScript;
31import scriptbuilder.structures.TimeSlice;
32import scriptbuilder.structures.events.I_ScriptEvent;
33
34/**
35 * Represents a single incident timeline in the GUI. Listens for mouse actions.
36 *
37 * @author Greg Eddington <geddingt@calpoly.edu>
38 * @author Bryan McGuffin
39 * @version 2017/06/30
40 */
41public class IncidentTimelinePanel extends JPanel
42{
43
44    /**
45     * The incident this panel represents.
46     */
47    ScriptIncident incident;
48    /**
49     * If true, this panel is in its minimized state.
50     */
51    //boolean collapsed;
52    /**
53     * If false, this panel won't be drawn.
54     */
55    boolean visible;
56    /**
57     * If true, this panel has focus.
58     */
59    boolean focused;
60
61    int cursorTime, lastSlice, x, y;
62
63    /**
64     * The map representing the properties of this incident's events. Keys:
65     * event types. Values: Properties objects for those events.
66     */
67    Map<ScriptEventType, Properties> eventTypeToPropertyMap;
68
69    /**
70     * Listener for the mouse. Receives notifications when the mouse enters,
71     * exits, moves through, or clicks inside the panel.
72     */
73    public class IncidentTimelineMouseListener extends MouseInputAdapter
74    {
75
76        /**
77         * Action to take when the mouse enters the panel. Here, the incident
78         * corresponding to this panel gains focus.
79         *
80         * @param e the mouse event
81         */
82        @Override
83        public void mouseEntered(MouseEvent e)
84        {
85            incident.setIncidentActive();
86            focused = true;
87        }
88
89        /**
90         * Action to take when the mouse leaves the panel. Here, the incident
91         * loses focus and the panel gets repainted.
92         *
93         * @param e the mouse event
94         */
95        @Override
96        public void mouseExited(MouseEvent e)
97        {
98            focused = false;
99            repaint();
100        }
101
102    /*
103    *   Popup menu for incident actions
104     */
105    private JPopupMenu createPopup()
106    {
107        JPopupMenu menu = new JPopupMenu();
108        JMenuItem eventsMenuItem = new JMenuItem("Events");
109        JMenuItem propsMenuItem = new JMenuItem("Properties");
110        JMenuItem deleteMenuItem = new JMenuItem("Delete");
111        eventsMenuItem.setActionCommand("Edit Events");
112        propsMenuItem.setActionCommand("Modify Incident Properties");
113        deleteMenuItem.setActionCommand("Delete Incident");
114
115        PopupMenuItemListener menuItemListener = new PopupMenuItemListener();
116
117        eventsMenuItem.addActionListener(menuItemListener);
118        propsMenuItem.addActionListener(menuItemListener);
119        deleteMenuItem.addActionListener(menuItemListener);
120
121        menu.add(eventsMenuItem);
122        menu.add(propsMenuItem);
123        menu.add(deleteMenuItem);
124        return menu;
125    }
126
127    class PopupMenuItemListener implements ActionListener
128    {
129
130        public void actionPerformed(ActionEvent e)
131        {
132            JOptionPane.showMessageDialog(null, e.getActionCommand() + " will be handled here.");
133        }
134    }
135        @Override
136        public void mousePressed(MouseEvent e)
137        {
138            int currentMouseX = e.getX();
139            int currentMouseY = e.getY();
140
141            // Does user want a popup menu?
142            if (e.isPopupTrigger())
143            {
144                JPopupMenu popup = createPopup();
145                popup.show(e.getComponent(), currentMouseX, currentMouseY);
146            }           
147        }
148        /**
149         * Determine if the mouse click happened within a valid timeSlice on
150         * this incident; if so, activate the Editor window for that timeSlice.
151         *
152         * @param e the mouse event
153         */
154        @Override
155        public void mouseClicked(MouseEvent e)
156        {
157            Editor ed = new Editor();
158            ScriptBuilderFrame f = null;
159            IncidentEditorFrame g = null;
160            if (getTopLevelAncestor() instanceof ScriptBuilderFrame)
161            {
162                f = (ScriptBuilderFrame) getTopLevelAncestor();
163            }
164            else if (getTopLevelAncestor() instanceof IncidentEditorFrame)
165            {
166                g = (IncidentEditorFrame) getTopLevelAncestor();
167            }
168
169            x = cursorTime = e.getX();
170            y = e.getY();
171           
172            if (e.getX() % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK
173                    > ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK / 2)
174            {
175                cursorTime += ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK
176                        - e.getX()
177                        % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK;
178            }
179            else
180            {
181                cursorTime -= e.getX()
182                        % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK;
183            }
184
185            int newSlice = (cursorTime / ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK);
186            newSlice *= ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION;
187            /**
188             * Check if click is out of bounds *
189             */
190            if (newSlice < 0 || incident == null)
191            {
192                return;
193            }
194
195            if (incident.slices.get(newSlice) != null)
196
197            {
198                for (I_ScriptEvent se : incident.slices.get(newSlice).events)
199                {
200                    ed.addProperty(eventTypeToPropertyMap.get(se.getScriptEventType()), se);
201                }
202            }
203
204            /**
205             * Add a new icon if left mouse button was clicked *
206             */
207            if (e.getButton() == MouseEvent.BUTTON1)
208            {
209                if (getTopLevelAncestor() instanceof ScriptBuilderFrame)
210                {
211                    if (f.currentEventType != null)
212                    {
213                        I_ScriptEvent s = ScriptEvent.factoryByType(f.currentEventType);
214                        ed.addProperty(eventTypeToPropertyMap.get(f.currentEventType), s);
215                        if (incident.slices.get(newSlice) == null)
216                        {
217                            incident.addNewEvent(s, newSlice);
218                        }
219                        else
220                        {
221                            incident.slices.get(newSlice).addEvent(s);
222                        }
223                        f.update(f.getScript(), f.getScript());
224                    }
225                }
226                if (getTopLevelAncestor() instanceof IncidentEditorFrame)
227                {
228                    if (g.currentEventType != null)
229                    {
230                        I_ScriptEvent s = ScriptEvent.factoryByType(g.currentEventType);
231                        ed.addProperty(eventTypeToPropertyMap.get(g.currentEventType), s);
232                        if (incident.slices.get(newSlice) == null)
233                        {
234                            incident.addNewEvent(s, newSlice);
235                        }
236                        else
237                        {
238                            incident.slices.get(newSlice).addEvent(s);
239                        }
240                        g.update(null, g.getIncident());
241                    }
242                }
243            }
244
245            if (incident.slices.get(newSlice) != null
246                    && getTopLevelAncestor() instanceof IncidentEditorFrame)
247            {
248                ed.setVisible(true);
249            }
250        }
251
252        /**
253         * Determine if the mouse is now hovering over a valid timeslice; if so,
254         * alter tooltip text and info window text to reflect the new timeslice.
255         *
256         * @param e the mouse event
257         */
258        @Override
259        public void mouseMoved(MouseEvent e)
260        {
261            x = cursorTime = e.getX();
262            y = e.getY();
263
264            if (e.getX() % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK
265                    > ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK / 2)
266            {
267                cursorTime += ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK
268                        - e.getX()
269                        % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK;
270            }
271            else
272            {
273                cursorTime -= e.getX()
274                        % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK;
275            }
276
277            if (incident != null)
278            {
279                int newSlice = (cursorTime / ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK);
280                newSlice *= ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION;
281                if (newSlice >= 0 && incident.slices.get(newSlice) != null)
282                {
283                    incident.setSliceActive(newSlice);
284                    lastSlice = newSlice;
285                    String newToolTip;
286
287                    newToolTip = incident.slices.get(newSlice).getToolTipText(y);
288
289                    setToolTipText((newToolTip == null || newToolTip.equals(""))
290                            ? null : newToolTip);
291                }
292            }
293
294            repaint();
295        }
296    }
297
298    /**
299     * Constructor. Generates a HashMap of all possible event types.
300     */
301    public IncidentTimelinePanel()
302    {
303        super();
304
305//        FACILITATOR_EVAL_EVENT, RADIO_EVAL_EVENT
306        eventTypeToPropertyMap = new HashMap();
307        eventTypeToPropertyMap.put(ScriptEventType.AUDIO_EVENT, Properties.Audio);
308        eventTypeToPropertyMap.put(ScriptEventType.CAD_EVENT, Properties.CADLog);
309        eventTypeToPropertyMap.put(ScriptEventType.CCTV_EVENT, Properties.CCTV);
310        eventTypeToPropertyMap.put(ScriptEventType.CHP_RADIO_EVENT, Properties.CHPRadio);
311        eventTypeToPropertyMap.put(ScriptEventType.PARAMICS_EVENT, Properties.Paramics);
312        eventTypeToPropertyMap.put(ScriptEventType.TOW_EVENT, Properties.Tow);
313        eventTypeToPropertyMap.put(ScriptEventType.UNIT_EVENT, Properties.Unit);
314        eventTypeToPropertyMap.put(ScriptEventType.WITNESS_EVENT, Properties.Witness);
315        eventTypeToPropertyMap.put(ScriptEventType.MAINTENANCE_RADIO_EVENT, Properties.MaintenanceRadio);
316        eventTypeToPropertyMap.put(ScriptEventType.TMT_RADIO_EVENT, Properties.TMTRadio);
317        eventTypeToPropertyMap.put(ScriptEventType.TELEPHONE_EVENT, Properties.Telephone);
318        eventTypeToPropertyMap.put(ScriptEventType.ATMS_EVAL_EVENT, Properties.ATMS);
319        eventTypeToPropertyMap.put(ScriptEventType.ACTIVITY_LOG_EVAL_EVENT, Properties.ActivityLog);
320        eventTypeToPropertyMap.put(ScriptEventType.CAD_EVAL_EVENT, Properties.CAD);
321        eventTypeToPropertyMap.put(ScriptEventType.CMS_EVAL_EVENT, Properties.CMS);
322        eventTypeToPropertyMap.put(ScriptEventType.FACILITATOR_EVAL_EVENT, Properties.Facilitator);
323        eventTypeToPropertyMap.put(ScriptEventType.RADIO_EVAL_EVENT, Properties.Radio);
324
325        // Add the mouse listener
326        IncidentTimelineMouseListener mouseListener
327                = new IncidentTimelineMouseListener();
328        addMouseMotionListener(mouseListener);
329        addMouseListener(mouseListener);
330    }
331
332    /**
333     * Update the panel if it's changed collapsed status. Redraw it with the
334     * correct dimensions for its status.
335     *
336     * @param incident the incident this panel represents
337     */
338    public void timelinePanelUpdate(ScriptIncident incident)
339    {
340        this.incident = incident;
341        this.visible = incident != null;
342
343        Dimension newSize;
344        if (visible)
345        {
346
347            newSize = new Dimension(((incident.length + incident.offset)
348                    / ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION
349                    * ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK)
350                    + ScriptBuilderGuiConstants.EVENT_ICON_WIDTH,
351                    ScriptBuilderGuiConstants.TIMELINE_COLLAPSED_HEIGHT
352                    + ScriptBuilderGuiConstants.SCRIPT_EVENT_ICON_STEP * 2);
353
354        }
355        else
356        {
357            newSize = new Dimension(0, 0);
358        }
359        this.setSize(newSize);
360        this.setPreferredSize(newSize);
361
362        invalidate();
363    }
364
365    /**
366     * Redraw this panel and all the icons inside it. If user is holding an
367     * event, draw that icon under the mouse.
368     *
369     * @param g the graphics component
370     */
371    @Override
372    public void paint(Graphics g)
373    {
374        super.paint(g);
375
376        if (!visible)
377        {
378            return;
379        }
380
381        Graphics2D g2d = (Graphics2D) g;
382//        jdalbey removed this decision and replaced it with ELSE clause
383//                so it will work with any alternate ancestor.
384//        if (getTopLevelAncestor() instanceof ScriptBuilderFrame)
385//        {
386//            IncidentTimelineDrawer.DrawScriptBuilderTimeline(g2d, incident);
387//        }
388        if (getTopLevelAncestor() instanceof IncidentEditorFrame)
389        {
390            IncidentTimelineDrawer.DrawIncidentTimeline(g2d, incident, false);
391        }
392        else
393        {
394            IncidentTimelineDrawer.DrawScriptBuilderTimeline(g2d, incident);
395        }
396
397        if (focused)
398        {
399            CursorDrawer.DrawCursor(g2d, cursorTime, false);
400            if (this.getTopLevelAncestor() instanceof ScriptBuilderFrame)
401            {
402                if (((ScriptBuilderFrame) this.getTopLevelAncestor()).currentEventType != null)
403                {
404                    EventIconDrawer.DrawEventIcon(g2d,
405                            ((ScriptBuilderFrame) this.getTopLevelAncestor()).currentEventType,
406                            x + 5, y + 10);
407                }
408            }
409            if (this.getTopLevelAncestor() instanceof IncidentEditorFrame)
410            {
411                if (((IncidentEditorFrame) this.getTopLevelAncestor()).currentEventType != null)
412                {
413                    EventIconDrawer.DrawEventIcon(g2d,
414                            ((IncidentEditorFrame) this.getTopLevelAncestor()).currentEventType,
415                            x + 5, y + 10);
416                }
417            }
418        }
419    }
420
421    /**
422     * Local main for viewing this panel only.
423     *
424     * @author jdalbey
425     * @param args not used
426     */
427    public static void main(String[] args)
428    {
429        JFrame frame = new JFrame("ScriptBuilderTimelinePanel Demo");
430        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
431
432        IncidentTimelinePanel pnl = new IncidentTimelinePanel();
433
434        // Create a script
435        File inFile = new File("test/scriptbuilder/structures/test_input_file.xml");
436        SimulationScript script = new SimulationScript();
437        script.loadScriptFromFile(inFile);
438        // retrieve a single incident from the script
439        ScriptIncident inci = script.incidents.get(2);
440        // update this panel with an incident
441        pnl.timelinePanelUpdate(inci);
442
443        frame.getContentPane().add(pnl, BorderLayout.CENTER);
444        frame.pack();
445
446        frame.setVisible(true);
447
448    }
449}
Note: See TracBrowser for help on using the repository browser.