package scriptbuilder.gui.panels; import event.editor.Editor; import event.editor.Properties; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.MouseEvent; import java.util.HashMap; import java.util.Map; import javax.swing.JPanel; import javax.swing.event.MouseInputAdapter; import scriptbuilder.gui.ScriptBuilderFrame; import scriptbuilder.gui.ScriptBuilderGuiConstants; import scriptbuilder.gui.drawers.CursorDrawer; import scriptbuilder.gui.drawers.EventIconDrawer; import scriptbuilder.gui.drawers.IncidentTimelineDrawer; import scriptbuilder.structures.ScriptEvent; import scriptbuilder.structures.ScriptEvent.ScriptEventType; import scriptbuilder.structures.ScriptIncident; import scriptbuilder.structures.TimeSlice; import scriptbuilder.structures.events.I_ScriptEvent; /** * Represents a single incident timeline in the GUI. Listens for mouse actions. * * @author Greg Eddington * @author Bryan McGuffin * @version 2017/06/30 */ public class IncidentTimelinePanel extends JPanel { /** * The incident this panel represents. */ ScriptIncident incident; /** * If true, this panel is in its minimized state. */ boolean collapsed; /** * If false, this panel won't be drawn. */ boolean visible; /** * If true, this panel has focus. */ boolean focused; int cursorTime, lastSlice, x, y; /** * The map representing the properties of this incident's events. Keys: * event types. Values: Properties objects for those events. */ Map eventTypeToPropertyMap; /** * Listener for the mouse. Receives notifications when the mouse enters, * exits, moves through, or clicks inside the panel. */ public class IncidentTimelineMouseListener extends MouseInputAdapter { /** * Action to take when the mouse enters the panel. Here, the incident * corresponding to this panel gains focus. * * @param e the mouse event */ @Override public void mouseEntered(MouseEvent e) { incident.setIncidentActive(); focused = true; } /** * Action to take when the mouse leaves the panel. Here, the incident * loses focus and the panel gets repainted. * * @param e the mouse event */ @Override public void mouseExited(MouseEvent e) { focused = false; repaint(); } /** * Determine if the mouse click happened within a valid timeSlice on * this incident; if so, activate the Editor window for that timeSlice. * * @param e the mouse event */ @Override public void mouseClicked(MouseEvent e) { Editor ed = new Editor(); ScriptBuilderFrame f = (ScriptBuilderFrame) getTopLevelAncestor(); x = cursorTime = e.getX(); y = e.getY(); if (e.getX() % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK > ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK / 2) { cursorTime += ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK - e.getX() % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK; } else { cursorTime -= e.getX() % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK; } int newSlice = (cursorTime / ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK); newSlice *= ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION; /** * Check if click is out of bounds * */ if (newSlice < 0 || incident == null) { return; } if (incident.slices.get(newSlice) != null) { for (I_ScriptEvent se : incident.slices.get(newSlice).events) { ed.addProperty(eventTypeToPropertyMap.get(se.getScriptEventType()), se); } } /** * Add a new icon if left mouse button was clicked * */ if (e.getButton() == MouseEvent.BUTTON1) { if (f.currentEventType != null) { I_ScriptEvent s = ScriptEvent.factoryByType(f.currentEventType); ed.addProperty(eventTypeToPropertyMap.get(f.currentEventType), s); if (incident.slices.get(newSlice) == null) { incident.addNewEvent(s, newSlice); } else { incident.slices.get(newSlice).addEvent(s); } f.update(f.getScript(), f.getScript()); } } if (incident.slices.get(newSlice) != null) { ed.setVisible(true); System.out.println(incident.slices.get(newSlice).toXML()); } } /** * Determine if the mouse is now hovering over a valid timeslice; if so, * alter tooltip text and info window text to reflect the new timeslice. * * @param e the mouse event */ @Override public void mouseMoved(MouseEvent e) { x = cursorTime = e.getX(); y = e.getY(); if (e.getX() % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK > ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK / 2) { cursorTime += ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK - e.getX() % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK; } else { cursorTime -= e.getX() % ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK; } if (incident != null) { int newSlice = (cursorTime / ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK); newSlice *= ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION; if (newSlice >= 0 && incident.slices.get(newSlice) != null) { incident.setSliceActive(newSlice); lastSlice = newSlice; String newToolTip; if (collapsed) { newToolTip = incident.slices.get(newSlice).toString(); } else { newToolTip = incident.slices.get(newSlice).getToolTipText(y); } setToolTipText((newToolTip == null || newToolTip.equals("")) ? null : newToolTip); } } repaint(); } } /** * Constructor. Generates a HashMap of all possible event types. */ public IncidentTimelinePanel() { super(); // FACILITATOR_EVAL_EVENT, RADIO_EVAL_EVENT eventTypeToPropertyMap = new HashMap(); eventTypeToPropertyMap.put(ScriptEventType.AUDIO_EVENT, Properties.Audio); eventTypeToPropertyMap.put(ScriptEventType.CAD_EVENT, Properties.CADLog); eventTypeToPropertyMap.put(ScriptEventType.CCTV_EVENT, Properties.CCTV); eventTypeToPropertyMap.put(ScriptEventType.CHP_RADIO_EVENT, Properties.CHPRadio); eventTypeToPropertyMap.put(ScriptEventType.PARAMICS_EVENT, Properties.Paramics); eventTypeToPropertyMap.put(ScriptEventType.TOW_EVENT, Properties.Tow); eventTypeToPropertyMap.put(ScriptEventType.UNIT_EVENT, Properties.Unit); eventTypeToPropertyMap.put(ScriptEventType.WITNESS_EVENT, Properties.Witness); eventTypeToPropertyMap.put(ScriptEventType.MAINTENANCE_RADIO_EVENT, Properties.MaintenanceRadio); eventTypeToPropertyMap.put(ScriptEventType.TMT_RADIO_EVENT, Properties.TMTRadio); eventTypeToPropertyMap.put(ScriptEventType.TELEPHONE_EVENT, Properties.Telephone); eventTypeToPropertyMap.put(ScriptEventType.ATMS_EVAL_EVENT, Properties.ATMS); eventTypeToPropertyMap.put(ScriptEventType.ACTIVITY_LOG_EVAL_EVENT, Properties.ActivityLog); eventTypeToPropertyMap.put(ScriptEventType.CAD_EVAL_EVENT, Properties.CAD); eventTypeToPropertyMap.put(ScriptEventType.CMS_EVAL_EVENT, Properties.CMS); eventTypeToPropertyMap.put(ScriptEventType.FACILITATOR_EVAL_EVENT, Properties.Facilitator); eventTypeToPropertyMap.put(ScriptEventType.RADIO_EVAL_EVENT, Properties.Radio); // Add the mouse listener IncidentTimelineMouseListener mouseListener = new IncidentTimelineMouseListener(); addMouseMotionListener(mouseListener); addMouseListener(mouseListener); } /** * Update the panel if it's changed collapsed status. Redraw it with the * correct dimensions for its status. * * @param incident the incident this panel represents */ public void timelinePanelUpdate(ScriptIncident incident) { this.incident = incident; if (incident != null) { this.collapsed = incident.collapsed; } this.visible = incident != null; Dimension newSize; if (visible) { if (collapsed) { newSize = new Dimension((incident.length + incident.offset) / ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION * ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK, ScriptBuilderGuiConstants.TIMELINE_COLLAPSED_HEIGHT); } else { int mostEvents = 0; for (TimeSlice slice : incident.getSlices()) { if (slice.events.size() > mostEvents) { mostEvents = slice.events.size(); } } newSize = new Dimension((ScriptBuilderGuiConstants.MAX_SIMULATION_LENGTH) / ScriptBuilderGuiConstants.HORIZONTAL_TICK_RESOLUTION * ScriptBuilderGuiConstants.PIXEL_WIDTH_PER_HORIZONTAL_TICK, ScriptBuilderGuiConstants.TIMELINE_COLLAPSED_HEIGHT + ScriptBuilderGuiConstants.SCRIPT_EVENT_ICON_STEP * (mostEvents + 1)); } } else { newSize = new Dimension(0, 0); } this.setSize(newSize); this.setPreferredSize(newSize); invalidate(); } /** * Redraw this panel and all the icons inside it. If user is holding an * event, draw that icon under the mouse. * * @param g the graphics component */ @Override public void paint(Graphics g) { super.paint(g); if (!visible) { return; } Graphics2D g2d = (Graphics2D) g; IncidentTimelineDrawer.DrawIncidentTimeline(g2d, incident, collapsed); if (focused) { CursorDrawer.DrawCursor(g2d, cursorTime, false); if (((ScriptBuilderFrame) this.getTopLevelAncestor()).currentEventType != null) { EventIconDrawer.DrawEventIcon(g2d, ((ScriptBuilderFrame) this.getTopLevelAncestor()).currentEventType, x + 5, y + 10); } } } }