Warning: Can't use blame annotator:
svn blame failed on trunk/src/scriptbuilder/gui/panels/IncidentTimelinePanel.java: ("Can't find a temporary directory: Internal error", 20014)

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

Revision 122, 19.5 KB checked in by bmcguffin, 9 years ago (diff)

Added message panel warning user, if the first event placed in an incident is placed at a time other than 00:00:00, that the event will be moved to time 00:00:00 relative to the start of the incident.

Commented out code in ScriptIncident?.addNewEvent which rounds event start times to the nearest minute. I need to find a better solution which doesn't damage the program's ability to accurately generate scripts.

RevLine 
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 && newSlice != 0)
246                    {
247                        JOptionPane.showMessageDialog(g, "This is the first event in the incident.\n"
248                                + "Therefore, it will be automatically positioned at time 00:00:00,\n"
249                                + "relative to the start of the incident.", "Event will be moved", JOptionPane.INFORMATION_MESSAGE);
250                        newSlice = 0;
251                    }
252                    if (g.currentEventType != null)
253                    {
254                        I_ScriptEvent s = ScriptEvent.factoryByType(g.currentEventType);
255                        if (ed != null)
256                        {
257                            ed.addEvent(eventTypeToPropertyMap.get(g.currentEventType), s);
258                        }
259                        if (incident.slices.get(newSlice) == null)
260                        {
261                            incident.addNewEvent(s, newSlice);
262                        }
263                        else
264                        {
265                            incident.slices.get(newSlice).addEvent(s);
266                        }
267                        g.update(null, g.getIncident());
268                    }
269                }
270            }
271
272            if (incident.slices.get(newSlice) != null
273                    && getTopLevelAncestor() instanceof IncidentEditorFrame)
274            {
275                ed.setVisible(true);
276            }
277        }
278
279        /**
280         * Determine if the mouse is now hovering over a valid timeslice; if so,
281         * alter tooltip text and info window text to reflect the new timeslice.
282         *
283         * @param e the mouse event
284         */
285        @Override
286        public void mouseMoved(MouseEvent e)
287        {
288            x = cursorTime = e.getX();
289            y = e.getY();
290
291            if (e.getX() % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK
292                    > ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK / 2)
293            {
294                cursorTime += ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK
295                        - e.getX()
296                        % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK;
297            }
298            else
299            {
300                cursorTime -= e.getX()
301                        % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK;
302            }
303
304            if (incident != null)
305            {
306                int newSlice = (cursorTime / ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK);
307                newSlice *= ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION;
308                if (newSlice >= 0 && incident.slices.get(newSlice) != null)
309                {
310                    incident.setSliceActive(newSlice);
311                    lastSlice = newSlice;
312                    String newToolTip;
313
314                    newToolTip = incident.slices.get(newSlice).getToolTipText(y);
315
316                    setToolTipText((newToolTip == null || newToolTip.equals(""))
317                            ? null : newToolTip);
318                }
319            }
320
321            repaint();
322        }
323    }
324
325    /**
326     * Constructor. Generates a HashMap of all possible event types.
327     */
328    public IncidentTimelinePanel()
329    {
330        super();
331
332        hasPopupAccess = true;
333
334        requestedEditorFillerTime = 0;
335//        FACILITATOR_EVAL_EVENT, RADIO_EVAL_EVENT
336        eventTypeToPropertyMap = new HashMap();
337        eventTypeToPropertyMap.put(ScriptEventType.AUDIO_EVENT, Properties.Audio);
338        eventTypeToPropertyMap.put(ScriptEventType.CAD_EVENT, Properties.CADLog);
339        eventTypeToPropertyMap.put(ScriptEventType.CCTV_EVENT, Properties.CCTV);
340        eventTypeToPropertyMap.put(ScriptEventType.CHP_RADIO_EVENT, Properties.CHPRadio);
341        eventTypeToPropertyMap.put(ScriptEventType.PARAMICS_EVENT, Properties.Paramics);
342        eventTypeToPropertyMap.put(ScriptEventType.TOW_EVENT, Properties.Tow);
343        eventTypeToPropertyMap.put(ScriptEventType.UNIT_EVENT, Properties.Unit);
344        eventTypeToPropertyMap.put(ScriptEventType.WITNESS_EVENT, Properties.Witness);
345        eventTypeToPropertyMap.put(ScriptEventType.MAINTENANCE_RADIO_EVENT, Properties.MaintenanceRadio);
346        eventTypeToPropertyMap.put(ScriptEventType.TMT_RADIO_EVENT, Properties.TMTRadio);
347        eventTypeToPropertyMap.put(ScriptEventType.TELEPHONE_EVENT, Properties.Telephone);
348        eventTypeToPropertyMap.put(ScriptEventType.ATMS_EVAL_EVENT, Properties.ATMS);
349        eventTypeToPropertyMap.put(ScriptEventType.ACTIVITY_LOG_EVAL_EVENT, Properties.ActivityLog);
350        eventTypeToPropertyMap.put(ScriptEventType.CAD_EVAL_EVENT, Properties.CAD);
351        eventTypeToPropertyMap.put(ScriptEventType.CMS_EVAL_EVENT, Properties.CMS);
352        eventTypeToPropertyMap.put(ScriptEventType.FACILITATOR_EVAL_EVENT, Properties.Facilitator);
353        eventTypeToPropertyMap.put(ScriptEventType.RADIO_EVAL_EVENT, Properties.Radio);
354
355        // Add the mouse listener
356        IncidentTimelineMouseListener mouseListener
357                = new IncidentTimelineMouseListener();
358        addMouseMotionListener(mouseListener);
359        addMouseListener(mouseListener);
360    }
361
362    /**
363     * Constructor. Generates a HashMap of all possible event types.
364     *
365     * @param usesPopup determines whether or not right-clicking on this panel
366     * will display a popup menu.
367     */
368    public IncidentTimelinePanel(boolean usesPopup)
369    {
370        super();
371
372        hasPopupAccess = usesPopup;
373
374        requestedEditorFillerTime = 0;
375//        FACILITATOR_EVAL_EVENT, RADIO_EVAL_EVENT
376        eventTypeToPropertyMap = new HashMap();
377        eventTypeToPropertyMap.put(ScriptEventType.AUDIO_EVENT, Properties.Audio);
378        eventTypeToPropertyMap.put(ScriptEventType.CAD_EVENT, Properties.CADLog);
379        eventTypeToPropertyMap.put(ScriptEventType.CCTV_EVENT, Properties.CCTV);
380        eventTypeToPropertyMap.put(ScriptEventType.CHP_RADIO_EVENT, Properties.CHPRadio);
381        eventTypeToPropertyMap.put(ScriptEventType.PARAMICS_EVENT, Properties.Paramics);
382        eventTypeToPropertyMap.put(ScriptEventType.TOW_EVENT, Properties.Tow);
383        eventTypeToPropertyMap.put(ScriptEventType.UNIT_EVENT, Properties.Unit);
384        eventTypeToPropertyMap.put(ScriptEventType.WITNESS_EVENT, Properties.Witness);
385        eventTypeToPropertyMap.put(ScriptEventType.MAINTENANCE_RADIO_EVENT, Properties.MaintenanceRadio);
386        eventTypeToPropertyMap.put(ScriptEventType.TMT_RADIO_EVENT, Properties.TMTRadio);
387        eventTypeToPropertyMap.put(ScriptEventType.TELEPHONE_EVENT, Properties.Telephone);
388        eventTypeToPropertyMap.put(ScriptEventType.ATMS_EVAL_EVENT, Properties.ATMS);
389        eventTypeToPropertyMap.put(ScriptEventType.ACTIVITY_LOG_EVAL_EVENT, Properties.ActivityLog);
390        eventTypeToPropertyMap.put(ScriptEventType.CAD_EVAL_EVENT, Properties.CAD);
391        eventTypeToPropertyMap.put(ScriptEventType.CMS_EVAL_EVENT, Properties.CMS);
392        eventTypeToPropertyMap.put(ScriptEventType.FACILITATOR_EVAL_EVENT, Properties.Facilitator);
393        eventTypeToPropertyMap.put(ScriptEventType.RADIO_EVAL_EVENT, Properties.Radio);
394
395        // Add the mouse listener
396        IncidentTimelineMouseListener mouseListener
397                = new IncidentTimelineMouseListener();
398        addMouseMotionListener(mouseListener);
399        addMouseListener(mouseListener);
400    }
401
402    /**
403     * Update the panel if it's changed collapsed status. Redraw it with the
404     * correct dimensions for its status.
405     *
406     * @param incident the incident this panel represents
407     */
408    public void timelinePanelUpdate(ScriptIncident incident)
409    {
410        this.incident = incident;
411        this.visible = (incident != null);
412
413        Dimension newSize;
414        if (visible)
415        {
416
417            if (getTopLevelAncestor() instanceof IncidentEditorFrame)
418            {
419
420                newSize = new Dimension(((incident.length + incident.offset + requestedEditorFillerTime)
421                        / ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION
422                        * ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK)
423                        + ScriptBuilderGuiConstants.EVENT_ICON_WIDTH,
424                        ScriptBuilderGuiConstants.TIMELINE_COLLAPSED_HEIGHT
425                        + ScriptBuilderGuiConstants.SCRIPT_EVENT_ICON_STEP * 2);
426            }
427            else
428            {
429                newSize = new Dimension(((incident.length + incident.offset + requestedScriptBuilderFillerTime)
430                        / ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION
431                        * ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK)
432                        + ScriptBuilderGuiConstants.EVENT_ICON_WIDTH,
433                        ScriptBuilderGuiConstants.TIMELINE_COLLAPSED_HEIGHT
434                        + ScriptBuilderGuiConstants.SCRIPT_EVENT_ICON_STEP * 2);
435            }
436
437        }
438        else
439        {
440            newSize = new Dimension(0, 0);
441        }
442        this.setSize(newSize);
443        this.setPreferredSize(newSize);
444
445        invalidate();
446    }
447
448    /**
449     * Redraw this panel and all the icons inside it. If user is holding an
450     * event, draw that icon under the mouse.
451     *
452     * @param g the graphics component
453     */
454    @Override
455    public void paint(Graphics g)
456    {
457        super.paint(g);
458
459        if (!visible)
460        {
461            return;
462        }
463
464        Graphics2D g2d = (Graphics2D) g;
465//        jdalbey removed this decision and replaced it with ELSE clause
466//                so it will work with any alternate ancestor.
467//        if (getTopLevelAncestor() instanceof ScriptBuilderFrame)
468//        {
469//            IncidentTimelineDrawer.DrawScriptBuilderTimeline(g2d, incident);
470//        }
471        if (getTopLevelAncestor() instanceof IncidentEditorFrame)
472        {
473            IncidentTimelineDrawer.DrawIncidentTimeline(g2d, incident, false);
474        }
475        else
476        {
477            IncidentTimelineDrawer.DrawScriptBuilderTimeline(g2d, incident);
478        }
479
480        if (focused)
481        {
482            CursorDrawer.DrawCursor(g2d, cursorTime, false);
483            if (this.getTopLevelAncestor() instanceof ScriptBuilderFrame)
484            {
485                if (((ScriptBuilderFrame) this.getTopLevelAncestor()).currentEventType != null)
486                {
487                    EventIconDrawer.DrawEventIcon(g2d,
488                            ((ScriptBuilderFrame) this.getTopLevelAncestor()).currentEventType,
489                            x + 5, y + 10);
490                }
491            }
492            if (this.getTopLevelAncestor() instanceof IncidentEditorFrame)
493            {
494                if (((IncidentEditorFrame) this.getTopLevelAncestor()).currentEventType != null)
495                {
496                    EventIconDrawer.DrawEventIcon(g2d,
497                            ((IncidentEditorFrame) this.getTopLevelAncestor()).currentEventType,
498                            x + 5, y + 10);
499                }
500            }
501        }
502    }
503
504    /**
505     * Local main for viewing this panel only.
506     *
507     * @author jdalbey
508     * @param args not used
509     */
510    public static void main(String[] args)
511    {
512        JFrame frame = new JFrame("ScriptBuilderTimelinePanel Demo");
513        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
514
515        IncidentTimelinePanel pnl = new IncidentTimelinePanel();
516
517        // Create a script
518        File inFile = new File("test/scriptbuilder/structures/test_input_file.xml");
519        SimulationScript script = new SimulationScript();
520        script.loadScriptFromFile(inFile);
521        // retrieve a single incident from the script
522        ScriptIncident inci = script.incidents.get(2);
523        // update this panel with an incident
524        pnl.timelinePanelUpdate(inci);
525
526        frame.getContentPane().add(pnl, BorderLayout.CENTER);
527        frame.pack();
528
529        frame.setVisible(true);
530
531    }
532}
Note: See TracBrowser for help on using the repository browser.