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

Revision 117, 19.2 KB checked in by bmcguffin, 9 years ago (diff)

Redesigned the method by which the first event in an event is added to relative time 00:00:00.

Added a second timestamp panel to the incident editor screen, which displays absolute script time in red. The original timestamp panel displays time relative to the start of the incident, in black.

Line 
1package scriptbuilder.gui.panels;
2
3import event.editor.frame.Editor;
4import event.editor.frame.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    /**
62     * If true, right-clicking on this panel will produce the popup menu.
63     */
64    private boolean hasPopupAccess;
65
66    int cursorTime, lastSlice, x, y;
67
68    /**
69     * Filler time at the end of the screen for a particular incident
70     */
71    public int requestedEditorFillerTime;
72
73    /**
74     * Filler time at the end of the screen for the whole script
75     */
76    public static int requestedScriptBuilderFillerTime = 0;
77
78    /**
79     * Constant for amount of filler to add, in seconds. Set to 15 minutes
80     */
81    public static final int FILLER_INTERVAL_SECONDS = 900;
82
83    /**
84     * The map representing the properties of this incident's events. Keys:
85     * event types. Values: Properties objects for those events.
86     */
87    public static Map<ScriptEventType, Properties> eventTypeToPropertyMap;
88
89    /**
90     * Listener for the mouse. Receives notifications when the mouse enters,
91     * exits, moves through, or clicks inside the panel.
92     */
93    public class IncidentTimelineMouseListener extends MouseInputAdapter
94    {
95
96        /**
97         * Action to take when the mouse enters the panel. Here, the incident
98         * corresponding to this panel gains focus.
99         *
100         * @param e the mouse event
101         */
102        @Override
103        public void mouseEntered(MouseEvent e)
104        {
105            incident.setIncidentActive();
106            focused = true;
107        }
108
109        /**
110         * Action to take when the mouse leaves the panel. Here, the incident
111         * loses focus and the panel gets repainted.
112         *
113         * @param e the mouse event
114         */
115        @Override
116        public void mouseExited(MouseEvent e)
117        {
118            focused = false;
119            repaint();
120        }
121
122        /*
123         *   Popup menu for incident actions
124         */
125        private JPopupMenu createPopup()
126        {
127            JPopupMenu menu = new JPopupMenu();
128            JMenuItem eventsMenuItem = new JMenuItem("Events");
129            JMenuItem propsMenuItem = new JMenuItem("Properties");
130            eventsMenuItem.setActionCommand("Edit Events");
131            propsMenuItem.setActionCommand("Modify Incident Properties");
132
133            PopupMenuItemListener menuItemListener = new PopupMenuItemListener();
134
135            eventsMenuItem.addActionListener(menuItemListener);
136            propsMenuItem.addActionListener(menuItemListener);
137
138            menu.add(eventsMenuItem);
139            menu.add(propsMenuItem);
140            return menu;
141        }
142
143        class PopupMenuItemListener implements ActionListener
144        {
145
146            public void actionPerformed(ActionEvent e)
147            {
148                JFrame topFrame = (JFrame) getTopLevelAncestor();
149                if (topFrame instanceof ScriptBuilderFrame)
150                {
151                    SimulationScript script = ((ScriptBuilderFrame) topFrame).getScript();
152                    if (e.getActionCommand().equals("Edit Events"))
153                    {
154                        IncidentEditorFrame editor = new IncidentEditorFrame(incident, (ScriptBuilderFrame) topFrame);
155                        script.addObserver(editor);
156                        editor.setVisible(true);
157                        ((ScriptBuilderFrame) topFrame).update(script, script);
158                    }
159                    if (e.getActionCommand().equals("Modify Incident Properties"))
160                    {
161                        ((ScriptBuilderFrame) topFrame).incidentDetailsScreen(incident);
162                        ((ScriptBuilderFrame) topFrame).update(script, script);
163                    }
164                }
165                topFrame.repaint();
166            }
167        }
168
169        @Override
170        public void mousePressed(MouseEvent e)
171        {
172            int currentMouseX = e.getX();
173            int currentMouseY = e.getY();
174
175            // Does user want a popup menu?
176            if (e.isPopupTrigger() && hasPopupAccess)
177            {
178                JPopupMenu popup = createPopup();
179                popup.show(e.getComponent(), currentMouseX, currentMouseY);
180            }
181        }
182
183        /**
184         * Determine if the mouse click happened within a valid timeSlice on
185         * this incident; if so, activate the Editor window for that timeSlice.
186         *
187         * @param e the mouse event
188         */
189        @Override
190        public void mouseClicked(MouseEvent e)
191        {
192            Editor ed = null;
193            ScriptBuilderFrame f = null;
194            IncidentEditorFrame g = null;
195            if (getTopLevelAncestor() instanceof ScriptBuilderFrame)
196            {
197                f = (ScriptBuilderFrame) getTopLevelAncestor();
198
199            }
200            else if (getTopLevelAncestor() instanceof IncidentEditorFrame)
201            {
202                g = (IncidentEditorFrame) getTopLevelAncestor();
203                ed = new Editor(g);
204            }
205
206            x = cursorTime = e.getX();
207            y = e.getY();
208
209            if (e.getX() % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK
210                    > ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK / 2)
211            {
212                cursorTime += ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK
213                        - e.getX()
214                        % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK;
215            }
216            else
217            {
218                cursorTime -= e.getX()
219                        % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK;
220            }
221
222            int newSlice = (cursorTime / ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK);
223            newSlice *= ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION;
224            /**
225             * Check if click is out of bounds *
226             */
227            if (newSlice < 0 || incident == null)
228            {
229                return;
230            }
231
232            if (ed != null)
233            {
234                ed.setSlice(incident.slices.get(newSlice));
235            }
236
237            /**
238             * Add a new icon if left mouse button was clicked *
239             */
240            if (e.getButton() == MouseEvent.BUTTON1)
241            {
242
243                if (getTopLevelAncestor() instanceof IncidentEditorFrame)
244                {
245                    if (incident.slices.size() == 0)
246                    {
247                        newSlice = 0;
248                    }
249                    if (g.currentEventType != null)
250                    {
251                        I_ScriptEvent s = ScriptEvent.factoryByType(g.currentEventType);
252                        if (ed != null)
253                        {
254                            ed.addEvent(eventTypeToPropertyMap.get(g.currentEventType), s);
255                        }
256                        if (incident.slices.get(newSlice) == null)
257                        {
258                            incident.addNewEvent(s, newSlice);
259                        }
260                        else
261                        {
262                            incident.slices.get(newSlice).addEvent(s);
263                        }
264                        g.update(null, g.getIncident());
265                    }
266                }
267            }
268
269            if (incident.slices.get(newSlice) != null
270                    && getTopLevelAncestor() instanceof IncidentEditorFrame)
271            {
272                ed.setVisible(true);
273            }
274        }
275
276        /**
277         * Determine if the mouse is now hovering over a valid timeslice; if so,
278         * alter tooltip text and info window text to reflect the new timeslice.
279         *
280         * @param e the mouse event
281         */
282        @Override
283        public void mouseMoved(MouseEvent e)
284        {
285            x = cursorTime = e.getX();
286            y = e.getY();
287
288            if (e.getX() % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK
289                    > ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK / 2)
290            {
291                cursorTime += ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK
292                        - e.getX()
293                        % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK;
294            }
295            else
296            {
297                cursorTime -= e.getX()
298                        % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK;
299            }
300
301            if (incident != null)
302            {
303                int newSlice = (cursorTime / ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK);
304                newSlice *= ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION;
305                if (newSlice >= 0 && incident.slices.get(newSlice) != null)
306                {
307                    incident.setSliceActive(newSlice);
308                    lastSlice = newSlice;
309                    String newToolTip;
310
311                    newToolTip = incident.slices.get(newSlice).getToolTipText(y);
312
313                    setToolTipText((newToolTip == null || newToolTip.equals(""))
314                            ? null : newToolTip);
315                }
316            }
317
318            repaint();
319        }
320    }
321
322    /**
323     * Constructor. Generates a HashMap of all possible event types.
324     */
325    public IncidentTimelinePanel()
326    {
327        super();
328
329        hasPopupAccess = true;
330
331        requestedEditorFillerTime = 0;
332//        FACILITATOR_EVAL_EVENT, RADIO_EVAL_EVENT
333        eventTypeToPropertyMap = new HashMap();
334        eventTypeToPropertyMap.put(ScriptEventType.AUDIO_EVENT, Properties.Audio);
335        eventTypeToPropertyMap.put(ScriptEventType.CAD_EVENT, Properties.CADLog);
336        eventTypeToPropertyMap.put(ScriptEventType.CCTV_EVENT, Properties.CCTV);
337        eventTypeToPropertyMap.put(ScriptEventType.CHP_RADIO_EVENT, Properties.CHPRadio);
338        eventTypeToPropertyMap.put(ScriptEventType.PARAMICS_EVENT, Properties.Paramics);
339        eventTypeToPropertyMap.put(ScriptEventType.TOW_EVENT, Properties.Tow);
340        eventTypeToPropertyMap.put(ScriptEventType.UNIT_EVENT, Properties.Unit);
341        eventTypeToPropertyMap.put(ScriptEventType.WITNESS_EVENT, Properties.Witness);
342        eventTypeToPropertyMap.put(ScriptEventType.MAINTENANCE_RADIO_EVENT, Properties.MaintenanceRadio);
343        eventTypeToPropertyMap.put(ScriptEventType.TMT_RADIO_EVENT, Properties.TMTRadio);
344        eventTypeToPropertyMap.put(ScriptEventType.TELEPHONE_EVENT, Properties.Telephone);
345        eventTypeToPropertyMap.put(ScriptEventType.ATMS_EVAL_EVENT, Properties.ATMS);
346        eventTypeToPropertyMap.put(ScriptEventType.ACTIVITY_LOG_EVAL_EVENT, Properties.ActivityLog);
347        eventTypeToPropertyMap.put(ScriptEventType.CAD_EVAL_EVENT, Properties.CAD);
348        eventTypeToPropertyMap.put(ScriptEventType.CMS_EVAL_EVENT, Properties.CMS);
349        eventTypeToPropertyMap.put(ScriptEventType.FACILITATOR_EVAL_EVENT, Properties.Facilitator);
350        eventTypeToPropertyMap.put(ScriptEventType.RADIO_EVAL_EVENT, Properties.Radio);
351
352        // Add the mouse listener
353        IncidentTimelineMouseListener mouseListener
354                = new IncidentTimelineMouseListener();
355        addMouseMotionListener(mouseListener);
356        addMouseListener(mouseListener);
357    }
358
359    /**
360     * Constructor. Generates a HashMap of all possible event types.
361     *
362     * @param usesPopup determines whether or not right-clicking on this panel
363     * will display a popup menu.
364     */
365    public IncidentTimelinePanel(boolean usesPopup)
366    {
367        super();
368
369        hasPopupAccess = usesPopup;
370
371        requestedEditorFillerTime = 0;
372//        FACILITATOR_EVAL_EVENT, RADIO_EVAL_EVENT
373        eventTypeToPropertyMap = new HashMap();
374        eventTypeToPropertyMap.put(ScriptEventType.AUDIO_EVENT, Properties.Audio);
375        eventTypeToPropertyMap.put(ScriptEventType.CAD_EVENT, Properties.CADLog);
376        eventTypeToPropertyMap.put(ScriptEventType.CCTV_EVENT, Properties.CCTV);
377        eventTypeToPropertyMap.put(ScriptEventType.CHP_RADIO_EVENT, Properties.CHPRadio);
378        eventTypeToPropertyMap.put(ScriptEventType.PARAMICS_EVENT, Properties.Paramics);
379        eventTypeToPropertyMap.put(ScriptEventType.TOW_EVENT, Properties.Tow);
380        eventTypeToPropertyMap.put(ScriptEventType.UNIT_EVENT, Properties.Unit);
381        eventTypeToPropertyMap.put(ScriptEventType.WITNESS_EVENT, Properties.Witness);
382        eventTypeToPropertyMap.put(ScriptEventType.MAINTENANCE_RADIO_EVENT, Properties.MaintenanceRadio);
383        eventTypeToPropertyMap.put(ScriptEventType.TMT_RADIO_EVENT, Properties.TMTRadio);
384        eventTypeToPropertyMap.put(ScriptEventType.TELEPHONE_EVENT, Properties.Telephone);
385        eventTypeToPropertyMap.put(ScriptEventType.ATMS_EVAL_EVENT, Properties.ATMS);
386        eventTypeToPropertyMap.put(ScriptEventType.ACTIVITY_LOG_EVAL_EVENT, Properties.ActivityLog);
387        eventTypeToPropertyMap.put(ScriptEventType.CAD_EVAL_EVENT, Properties.CAD);
388        eventTypeToPropertyMap.put(ScriptEventType.CMS_EVAL_EVENT, Properties.CMS);
389        eventTypeToPropertyMap.put(ScriptEventType.FACILITATOR_EVAL_EVENT, Properties.Facilitator);
390        eventTypeToPropertyMap.put(ScriptEventType.RADIO_EVAL_EVENT, Properties.Radio);
391
392        // Add the mouse listener
393        IncidentTimelineMouseListener mouseListener
394                = new IncidentTimelineMouseListener();
395        addMouseMotionListener(mouseListener);
396        addMouseListener(mouseListener);
397    }
398
399    /**
400     * Update the panel if it's changed collapsed status. Redraw it with the
401     * correct dimensions for its status.
402     *
403     * @param incident the incident this panel represents
404     */
405    public void timelinePanelUpdate(ScriptIncident incident)
406    {
407        this.incident = incident;
408        this.visible = (incident != null);
409
410        Dimension newSize;
411        if (visible)
412        {
413
414            if (getTopLevelAncestor() instanceof IncidentEditorFrame)
415            {
416
417                newSize = new Dimension(((incident.length + incident.offset + requestedEditorFillerTime)
418                        / ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION
419                        * ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK)
420                        + ScriptBuilderGuiConstants.EVENT_ICON_WIDTH,
421                        ScriptBuilderGuiConstants.TIMELINE_COLLAPSED_HEIGHT
422                        + ScriptBuilderGuiConstants.SCRIPT_EVENT_ICON_STEP * 2);
423            }
424            else
425            {
426                newSize = new Dimension(((incident.length + incident.offset + requestedScriptBuilderFillerTime)
427                        / ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION
428                        * ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK)
429                        + ScriptBuilderGuiConstants.EVENT_ICON_WIDTH,
430                        ScriptBuilderGuiConstants.TIMELINE_COLLAPSED_HEIGHT
431                        + ScriptBuilderGuiConstants.SCRIPT_EVENT_ICON_STEP * 2);
432            }
433
434        }
435        else
436        {
437            newSize = new Dimension(0, 0);
438        }
439        this.setSize(newSize);
440        this.setPreferredSize(newSize);
441
442        invalidate();
443    }
444
445    /**
446     * Redraw this panel and all the icons inside it. If user is holding an
447     * event, draw that icon under the mouse.
448     *
449     * @param g the graphics component
450     */
451    @Override
452    public void paint(Graphics g)
453    {
454        super.paint(g);
455
456        if (!visible)
457        {
458            return;
459        }
460
461        Graphics2D g2d = (Graphics2D) g;
462//        jdalbey removed this decision and replaced it with ELSE clause
463//                so it will work with any alternate ancestor.
464//        if (getTopLevelAncestor() instanceof ScriptBuilderFrame)
465//        {
466//            IncidentTimelineDrawer.DrawScriptBuilderTimeline(g2d, incident);
467//        }
468        if (getTopLevelAncestor() instanceof IncidentEditorFrame)
469        {
470            IncidentTimelineDrawer.DrawIncidentTimeline(g2d, incident, false);
471        }
472        else
473        {
474            IncidentTimelineDrawer.DrawScriptBuilderTimeline(g2d, incident);
475        }
476
477        if (focused)
478        {
479            CursorDrawer.DrawCursor(g2d, cursorTime, false);
480            if (this.getTopLevelAncestor() instanceof ScriptBuilderFrame)
481            {
482                if (((ScriptBuilderFrame) this.getTopLevelAncestor()).currentEventType != null)
483                {
484                    EventIconDrawer.DrawEventIcon(g2d,
485                            ((ScriptBuilderFrame) this.getTopLevelAncestor()).currentEventType,
486                            x + 5, y + 10);
487                }
488            }
489            if (this.getTopLevelAncestor() instanceof IncidentEditorFrame)
490            {
491                if (((IncidentEditorFrame) this.getTopLevelAncestor()).currentEventType != null)
492                {
493                    EventIconDrawer.DrawEventIcon(g2d,
494                            ((IncidentEditorFrame) this.getTopLevelAncestor()).currentEventType,
495                            x + 5, y + 10);
496                }
497            }
498        }
499    }
500
501    /**
502     * Local main for viewing this panel only.
503     *
504     * @author jdalbey
505     * @param args not used
506     */
507    public static void main(String[] args)
508    {
509        JFrame frame = new JFrame("ScriptBuilderTimelinePanel Demo");
510        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
511
512        IncidentTimelinePanel pnl = new IncidentTimelinePanel();
513
514        // Create a script
515        File inFile = new File("test/scriptbuilder/structures/test_input_file.xml");
516        SimulationScript script = new SimulationScript();
517        script.loadScriptFromFile(inFile);
518        // retrieve a single incident from the script
519        ScriptIncident inci = script.incidents.get(2);
520        // update this panel with an incident
521        pnl.timelinePanelUpdate(inci);
522
523        frame.getContentPane().add(pnl, BorderLayout.CENTER);
524        frame.pack();
525
526        frame.setVisible(true);
527
528    }
529}
Note: See TracBrowser for help on using the repository browser.