// ********************************************************************** // // <copyright> // // BBN Technologies, a Verizon Company // 10 Moulton Street // Cambridge, MA 02138 // (617) 873-8000 // // Copyright (C) BBNT Solutions LLC. All rights reserved. // // </copyright> // ********************************************************************** // // $Source: // /cvs/darwars/ambush/OM/src/com/bbn/ambush/gui/EventListPanel.java,v // $ // $RCSfile: EventListPresenter.java,v $ // $Revision: 1.2 $ // $Date: 2007/09/27 23:12:50 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.gui.event; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.Point2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ConcurrentModificationException; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Properties; import java.util.Set; import java.util.TreeSet; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.BorderFactory; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JToolBar; import javax.swing.ListCellRenderer; import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.ToolTipManager; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import com.bbn.openmap.MapBean; import com.bbn.openmap.event.OMEvent; import com.bbn.openmap.event.OMEventComparator; import com.bbn.openmap.event.OMEventHandler; import com.bbn.openmap.event.OMEventSelectionCoordinator; import com.bbn.openmap.event.OMEventSelectionListener; import com.bbn.openmap.gui.time.TimePanel; import com.bbn.openmap.omGraphics.DrawingAttributes; import com.bbn.openmap.time.Clock; import com.bbn.openmap.time.TimeBoundsEvent; import com.bbn.openmap.time.TimeBoundsListener; import com.bbn.openmap.time.TimeEvent; import com.bbn.openmap.time.TimeEventListener; import com.bbn.openmap.util.ComponentFactory; import com.bbn.openmap.util.PropUtils; /** * The EventListPresenter presents OMEvents as a list. It will find * OMEventHandlers in the MapHandler and display the events of the active ones. * Clicking on the list will set the current time to the event time, and also * move the map to the event location. You can also use the filter and rating * controls to group events. The TimePanel will limit playback over events that * have been marked with the play filter, and the TimelineLayer will display the * rating colors over the range of events designated with those ratings. * * <pre> * eventListPresenter.class=com.bbn.openmap.gui.event.EventListPresenter * eventListPresenter.prettyName=List * eventListPresenter.cellRendererClass=com.bbn.openmap.gui.event.EventListCellRenderer * eventListPresenter.selectColor=0xAA006699 * eventListPresenter.timeWindowColor=0x55666666 * # can override if you want to change what the icons look like * eventListPresenter.iconPackageClass=com.bbn.openmap.gui.event.EventListIconPackage * # optional, if you want to turn these off at the bottom of the presenter. * eventListPresenter.showRatings=false * eventListPresenter.showPlayFilter=false * </pre> */ public class EventListPresenter extends AbstractEventPresenter implements EventPresenter, ListSelectionListener, PropertyChangeListener, MouseListener, MouseMotionListener, TimeBoundsListener, TimeEventListener { public static Logger logger = Logger.getLogger("com.bbn.openmap.gui.event.EventListPresenter"); protected LinkedList<OMEventHandler> eventHandlers; protected LinkedList macroFilters; protected EventPanel parentPanel; protected JList displayList; protected Clock clock = null; protected MapBean map = null; protected List activeFilters; protected Hashtable filters = new Hashtable(); protected JPanel filterPanel; protected long displayTimeWindow = 1000; protected int prefWidth = 200; protected int prefHeight = 0; protected JLabel detailSpace; protected JPanel detailSpacePanel; protected TreeSet<OMEvent> activeEvents; protected TreeSet<OMEvent> allEvents; protected OMEventSelectionCoordinator aesc; protected EventListCellRenderer cellRenderer; public static final String DisplayIntervalProperty = "displayInterval"; public static final String CellRendererClassProperty = "cellRendererClass"; public static final String PreferredWidthProperty = "width"; public static final String PreferredHeightProperty = "height"; /** * A drawing attributes object that holds the basic colors used for display. * The font color is held as line paint, the select color is held as the * select paint, the time window color is held as the matting paint, and the * background color is held as the fill paint. */ protected DrawingAttributes drawingAttributes = new DrawingAttributes(); private OMEvent lastSelectedEvent; /** * Create a new EventListPresenter with a BorderLayout. The scrolled pane * containing the list is in the main body, and controls for bringing up the * filter controls are display in the NORTH. */ public EventListPresenter() { setIsolated(true); eventHandlers = new LinkedList<OMEventHandler>(); macroFilters = new LinkedList(); setLayout(new BorderLayout()); } public void setProperties(String prefix, Properties props) { super.setProperties(prefix, props); prefix = PropUtils.getScopedPropertyPrefix(prefix); String crc = props.getProperty(prefix + CellRendererClassProperty); if (crc != null) { setEventCellRenderer((EventListCellRenderer) ComponentFactory.create(crc, prefix, props)); } displayTimeWindow = PropUtils.longFromProperties(props, prefix + DisplayIntervalProperty, displayTimeWindow); prefWidth = PropUtils.intFromProperties(props, prefix + PreferredWidthProperty, prefWidth); prefHeight = PropUtils.intFromProperties(props, prefix + PreferredHeightProperty, prefHeight); } public DrawingAttributes getSelectionDrawingAttributes() { return drawingAttributes; } public void addEventHandler(OMEventHandler aeh) { if (logger.isLoggable(Level.FINE)) { logger.fine("adding " + aeh.getClass().getName()); } eventHandlers.add(aeh); retrieveFiltersFromEventHandlers(); updateInterface(false); } public void removeEventHandler(OMEventHandler aeh) { if (logger.isLoggable(Level.FINE)) { logger.fine("removing " + aeh.getClass().getName()); } eventHandlers.remove(aeh); retrieveFiltersFromEventHandlers(); updateInterface(); } public void clearEventHandlers() { if (logger.isLoggable(Level.FINE)) { logger.fine("clearing event handlers"); } eventHandlers.clear(); retrieveFiltersFromEventHandlers(); updateInterface(false); } public void rebuildEventList() { rebuildEventList(true); } /** * This is the method that rebuilds the list of visible events. */ public void rebuildEventList(boolean resetSelected) { if (logger.isLoggable(Level.FINE)) { logger.fine("rebuilding list"); } // If allEvents isn't recreated here, weird things start happening with // selection. I seems that this gets the entire event list reset for the // interface. allEvents = null; activeEvents = null; hideDetails(); initInterface(getActiveEvents(), resetSelected); highlightCurrentEvent(currentTime); firePropertyChange(ActiveEventsProperty, null, getActiveEvents()); } /** * This is the method that creates a sorted list of all events. * * @return Iterator of OMEvents. */ public synchronized Iterator<OMEvent> getAllEvents() { if (allEvents == null) { if (logger.isLoggable(Level.FINE)) { logger.fine("fetching all events from event handlers"); } allEvents = new TreeSet<OMEvent>(new OMEventComparator()); for (Iterator<OMEventHandler> it = eventHandlers.iterator(); it.hasNext();) { List<OMEvent> eventList = it.next().getEventList(); if (eventList != null) { try { allEvents.addAll(eventList); } catch (NullPointerException npe) { return getAllEvents(); } } } } return allEvents.iterator(); } public boolean isEventActive(OMEvent OMe) { if (activeEvents != null) { for (Iterator<OMEvent> it = getActiveEvents(); it.hasNext();) { if (it.next() == OMe) { return true; } } } return false; } /** * This is the method that creates a sorted list of visible events. * * @return Iterator of OMEvents. */ public synchronized Iterator<OMEvent> getActiveEvents() { if (activeEvents == null) { if (logger.isLoggable(Level.FINE)) { logger.fine("building list of active events"); } activeEvents = new TreeSet<OMEvent>(new OMEventComparator()); List<OMEvent> activeFilters = getActiveFilters(); for (Iterator<OMEventHandler> it = eventHandlers.iterator(); it.hasNext();) { OMEventHandler aeh = (OMEventHandler) it.next(); if (logger.isLoggable(Level.FINE)) { logger.fine("getting filtered list from (" + aeh.getClass().getName() + ")"); } // Pass a filter to the OMEventHandler to get a // filtered list back if desired. Need to ask // MissionEventHandler for what filters it can handle // (with pretty name) so that the filters can be created // by the presenter and passed back List<OMEvent> eventList = aeh.getEventList(activeFilters); if (eventList != null) { if (logger.isLoggable(Level.FINE)) { logger.fine("list from " + aeh.getClass().getName() + "has (" + eventList.size() + ") events"); } for (Iterator<OMEvent> it2 = eventList.iterator(); it2.hasNext();) { if (logger.isLoggable(Level.FINER)) { logger.finer("adding OM event"); } OMEvent me = it2.next(); if (me != null) { activeEvents.add(me); } else { if (logger.isLoggable(Level.FINE)) { logger.fine("The " + aeh.getClass().getName() + " is providing null events."); } } } } } if (logger.isLoggable(Level.FINER)) { logger.finer("--------"); for (Iterator<OMEvent> it = activeEvents.iterator(); it.hasNext();) { OMEvent eve = (OMEvent) it.next(); logger.finer(eve.getTimeStamp() + " " + eve); } logger.finer("--------"); } } return activeEvents.iterator(); } protected ToolTipManager ttmanager; /** * Resets the event list. * * @param it Iterator over all visible events (active) * @param setSelected select the last currently selected on the list, has * the side effect of resetting the clock. You want this to be false * when event handlers are being added. */ protected synchronized void initInterface(Iterator<OMEvent> it, boolean setSelected) { if (logger.isLoggable(Level.FINE)) { logger.fine("initing interface"); } DefaultListModel listModel = new DefaultListModel(); int selectedIndex = -1; int curIndex = 0; while (it.hasNext()) { // if (logger.isLoggable(Level.FINER)) { // logger.finer("adding event to list model"); // } OMEvent curEvent = it.next(); listModel.addElement(curEvent); if (lastSelectedEvent == curEvent && lastSelectedEvent != null) { selectedIndex = curIndex; } curIndex++; } if (logger.isLoggable(Level.FINE)) { logger.fine("added " + curIndex + " events to list"); } // This code below will cause the first visible event to be marked as // selected // if the list contents change and the previous selected version is no // longer visible. // if (selectedIndex < 0) { // selectedIndex = 0; // } if (displayList == null) { if (logger.isLoggable(Level.FINE)) { logger.fine("Creating gui components"); } JPanel wrapper = new JPanel(); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); wrapper.setLayout(gridbag); c.fill = GridBagConstraints.BOTH; c.weighty = 1f; c.weightx = 1f; c.gridwidth = GridBagConstraints.REMAINDER; c.insets = new Insets(0, 2, 0, 2); // Don't show the panel if there are no events to show. // if (listModel.getSize() > 0) { if (ttmanager == null) { ttmanager = ToolTipManager.sharedInstance(); ttmanager.setEnabled(true); ttmanager.setInitialDelay(1); } else if (displayList != null) { ttmanager.unregisterComponent(displayList); } displayList = new JList(listModel); displayList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION/* SINGLE_SELECTION */); displayList.addListSelectionListener(this); displayList.addMouseListener(this); displayList.addMouseMotionListener(this); displayList.setCellRenderer(getEventCellRenderer()); ttmanager.registerComponent(displayList); JScrollPane listScrollPane = new JScrollPane(displayList); listScrollPane.setPreferredSize(new Dimension(prefWidth, prefHeight)); listScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); listScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); gridbag.setConstraints(listScrollPane, c); wrapper.add(listScrollPane); c.fill = GridBagConstraints.HORIZONTAL; c.weighty = 0f; // Initialize the detail space. We need to embed it in a JPanel // so the background color fills the area. detailSpace = new JLabel(""); detailSpacePanel = new JPanel(new BorderLayout()); detailSpacePanel.setBackground(new Color(0xffffcc)); detailSpacePanel.setVisible(false); detailSpacePanel.add(detailSpace, BorderLayout.CENTER); gridbag.setConstraints(detailSpacePanel, c); wrapper.add(detailSpacePanel); JComponent ecp = getEventControlPanel(); gridbag.setConstraints(ecp, c); wrapper.add(ecp); add(wrapper, BorderLayout.CENTER); validate(); repaint(); if (logger.isLoggable(Level.FINE)) { logger.fine("Done creating gui components"); } } else { setListModel(listModel); displayList.revalidate(); displayList.repaint(); } if (logger.isLoggable(Level.FINE)) { logger.fine("done initing"); } if (selectedIndex >= 0 && setSelected) { // only select the event if it's there. Otherwise, the list and // timeline jump inexplicably to the top. displayList.setSelectedIndex(selectedIndex); } else { lastSelectedEvent = null; } } public ListCellRenderer getEventCellRenderer() { if (cellRenderer == null) { setEventCellRenderer(new EventListCellRenderer()); } return cellRenderer; } public void setEventCellRenderer(EventListCellRenderer lcr) { cellRenderer = lcr; if (cellRenderer != null) { drawingAttributes = cellRenderer.setRenderingAttributes(drawingAttributes); } } /** * @param string adding a filter string to the list of presentable filters * available from one of the MissionEventHandler. */ protected void addFilter(String string, Boolean value) { filters.put(string, value); } /** * Clear out the list of filters. */ protected void clearFilters() { filters.clear(); } public JPanel getFilterPanel() { if (filterPanel == null) { filterPanel = new JPanel(); } return filterPanel; } /** * Rebuilds the contents of the panel in the popup window for the filters * supplied by the event handlers. */ protected void rebuildFilterInterface() { JPanel filterPanel = getFilterPanel(); filterPanel.removeAll(); // Set up the overall panel layout GridBagLayout gb1 = new GridBagLayout(); GridBagConstraints c1 = new GridBagConstraints(); filterPanel.setLayout(gb1); c1.insets = new Insets(10, 10, 10, 10); c1.gridwidth = GridBagConstraints.REMAINDER; c1.fill = GridBagConstraints.VERTICAL; c1.weighty = .33; // Set up the layout of the filter options JPanel filterSubPanel = new JPanel(); GridBagLayout gb2 = new GridBagLayout(); GridBagConstraints c2 = new GridBagConstraints(); filterSubPanel.setLayout(gb2); c2.anchor = GridBagConstraints.WEST; c2.gridwidth = GridBagConstraints.REMAINDER; // Set up the filter options, add them to panel that centers // itself in the window while aligning all on the left. try { Set keys = filters.keySet(); for (Iterator it = keys.iterator(); it.hasNext();) { String title = (String) it.next(); JCheckBox jcb = new JCheckBox(title, ((Boolean) filters.get(title)).booleanValue()); jcb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { JCheckBox jcb = (JCheckBox) ae.getSource(); String title = jcb.getText(); setFilterValue(title, jcb.isSelected()); rebuildEventList(); } }); gb2.setConstraints(jcb, c2); filterSubPanel.add(jcb); } } catch (ConcurrentModificationException cme) { if (logger.isLoggable(Level.FINE)) { logger.fine("ConcurrentModificationException caught while rebuilding the event list"); } } catch (ArrayIndexOutOfBoundsException aioobe) { if (logger.isLoggable(Level.FINE)) { logger.fine("ArrayIndexOutOfBoundsException caught while rebuilding the event list: " + aioobe.getMessage()); } } // Add filter choices to the overall panel gb1.setConstraints(filterSubPanel, c1); filterPanel.add(filterSubPanel); // Create the buttons to turn them all on or off JButton allFiltersOnButton = new JButton(EventPanel.SHOW_ALL_EVENTS_STRING); allFiltersOnButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { resetFilters(Boolean.TRUE); } }); JButton allFiltersOffButton = new JButton(EventPanel.HIDE_ALL_EVENTS_STRING); allFiltersOffButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { resetFilters(Boolean.FALSE); } }); // Need to put these on/off buttons in their own panel so they // float in the middle of all the extra space in the window // without resizing themselves to fill in the space. JPanel resetButtonPanel = new JPanel(); GridBagLayout gb3 = new GridBagLayout(); GridBagConstraints c3 = new GridBagConstraints(); resetButtonPanel.setLayout(gb3); gb3.setConstraints(allFiltersOnButton, c3); gb3.setConstraints(allFiltersOffButton, c3); resetButtonPanel.add(allFiltersOnButton); resetButtonPanel.add(allFiltersOffButton); c1.fill = GridBagConstraints.BOTH; c1.weighty = .66; c1.weightx = 1; c1.anchor = GridBagConstraints.CENTER; gb1.setConstraints(resetButtonPanel, c1); filterPanel.add(resetButtonPanel); filterPanel.revalidate(); } /** * @param title * @param b */ protected void setFilterValue(String title, boolean b) { setFilterValue(title, b ? Boolean.TRUE : Boolean.FALSE); } protected void setFilterValue(String title, Boolean val) { filters.put(title, val); // Tell all the OMEventHandlers that the setting has been // updated, let the one that cares update itself. for (Iterator<OMEventHandler> it = eventHandlers.iterator(); it.hasNext();) { OMEventHandler eh = it.next(); // EventHandlers should only let this be set if they // control the events described by the filter. eh.setFilterState(title, val); } activeFilters = null; } /** * Set the filters so that all event handlers will return anything they * have, and rebuild interface. */ public void resetFilters() { resetFilters(Boolean.TRUE); } /** * Set the filters on/off so that all event handlers will return anything * they have, and rebuild interface. */ public void resetFilters(Boolean enabled) { if (logger.isLoggable(Level.FINE)) { logger.fine("enabled = " + enabled); } Set keys = filters.keySet(); for (Iterator it = keys.iterator(); it.hasNext();) { String title = (String) it.next(); setFilterValue(title, enabled); } updateInterface(); } public void updateInterface() { updateInterface(true); } public void updateInterface(boolean resetSelected) { activeFilters = null; rebuildFilterInterface(); rebuildEventList(resetSelected); if (parentPanel != null) { parentPanel.initInterface(); } // Tell everyone interested in the event list to check the events. } /** * @return a Hashtable containing filter strings as keys, with Boolean * TRUE/FALSE for whether they are enabled. */ public Hashtable getFilters() { return filters; } /** * @return a List of Strings for the filters that have been turned on. */ public List getActiveFilters() { if (activeFilters == null) { activeFilters = new LinkedList(); Set filterNames = filters.keySet(); for (Iterator it = filterNames.iterator(); it.hasNext();) { String filterName = (String) it.next(); if (filters.get(filterName) == Boolean.TRUE) { activeFilters.add(filterName); } } } return activeFilters; } /** * Contacts the OMEventHandlers and gets their filters from them. * Re-initializes the filter GUI. */ public void retrieveFiltersFromEventHandlers() { clearFilters(); for (Iterator<OMEventHandler> it = eventHandlers.iterator(); it.hasNext();) { OMEventHandler meh = it.next(); if (logger.isLoggable(Level.FINE)) { logger.fine("Checking out OM event handler " + meh.getClass().getName()); } // While we're checking out the OMEventHandler, might // as well ask it for it's filter strings, and reset the // filter interface. List filters = meh.getFilters(); if (filters != null && !filters.isEmpty()) { for (Iterator it2 = filters.iterator(); it2.hasNext();) { Object filterObj = it2.next(); if (filterObj instanceof String) { Boolean val = meh.getFilterState((String) filterObj); addFilter((String) filterObj, val != null ? val : Boolean.FALSE); } } } } rebuildFilterInterface(); } public void clearSelection() { if (displayList != null) { displayList.clearSelection(); } } protected synchronized void setListModel(ListModel lm) { displayList.setModel(lm); } protected synchronized ListModel getListModel() { return displayList.getModel(); } /** * This method is required by ListSelectionListener. It causes events to * happen when something in the list is clicked on. */ public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting() == false && displayList != null) { int[] indicies = displayList.getSelectedIndices(); if (indicies.length > 0) { ListModel listModel = getListModel(); OMEvent selectedEvent = (OMEvent) listModel.getElementAt(indicies[0]); lastSelectedEvent = selectedEvent; if (selectedEvent != null) { if (clock != null) { clock.setTime((long) selectedEvent.getTimeStamp()); Point2D location = selectedEvent.getLocation(); if (location != null && map != null) { map.setCenter(location); } } String details = selectedEvent.getDetailedInformation(); if (details != null) { showDetails(details); } else { hideDetails(); } } for (Iterator<OMEvent> it = getAllEvents(); it.hasNext();) { ((OMEvent) it.next()).putAttribute(OMEvent.ATT_KEY_SELECTED, null); } Vector<OMEvent> v = new Vector<OMEvent>(); OMEvent firstInRangeEvent = null; OMEvent lastSelectedEvent = null; int lastIndex = -2; boolean inRange = false; for (int i = 0; i < indicies.length; i++) { int curIndex = indicies[i]; selectedEvent = (OMEvent) listModel.getElementAt(curIndex); selectedEvent.putAttribute(OMEvent.ATT_KEY_SELECTED, OMEvent.ATT_VAL_SELECTED); if (curIndex == lastIndex + 1) { inRange = true; if (firstInRangeEvent == null) { firstInRangeEvent = lastSelectedEvent; firstInRangeEvent.putAttribute(OMEvent.ATT_KEY_SELECTED, OMEvent.ATT_VAL_SELECTED_START_RANGE); } } else if (inRange && lastSelectedEvent != null) { lastSelectedEvent.putAttribute(OMEvent.ATT_KEY_SELECTED, OMEvent.ATT_VAL_SELECTED_END_RANGE); inRange = false; } v.add(selectedEvent); // forward the call back to the thing that provided // the event, so it knows that the event was selected // if it cares. Object src = selectedEvent.getSource(); if (src instanceof OMEventSelectionListener) { ((OMEventSelectionListener) src).selected(selectedEvent); } lastSelectedEvent = selectedEvent; lastIndex = curIndex; } if (inRange && lastSelectedEvent != null) { lastSelectedEvent.putAttribute(OMEvent.ATT_KEY_SELECTED, OMEvent.ATT_VAL_SELECTED_END_RANGE); } if (aesc != null) { aesc.eventsSelected(v); } } } } public boolean selectEvent(OMEvent event) { boolean ret = false; if (displayList != null) { displayList.setSelectedValue(event, true); String details = event.getDetailedInformation(); if (details != null) { showDetails(details); } else { hideDetails(); } ret = true; } return ret; } public void findAndInit(Object someObj) { if (someObj instanceof OMEventHandler) { addEventHandler((OMEventHandler) someObj); } if (someObj instanceof Clock) { setClock((Clock) someObj); } if (someObj instanceof MapBean) { setMap((MapBean) someObj); } if (someObj instanceof FilterPresenter && someObj != this) { macroFilters.add(someObj); ((FilterPresenter) someObj).addPropertyChangeListener(this); } if (someObj instanceof EventPanel) { parentPanel = (EventPanel) someObj; } if (someObj instanceof OMEventSelectionCoordinator && aesc == null) { aesc = (OMEventSelectionCoordinator) someObj; aesc.addPropertyChangeListener(this); } if (someObj instanceof TimePanel) { ListCellRenderer lcr = getEventCellRenderer(); if (lcr instanceof EventListCellRenderer) { EventListCellRenderer elcr = (EventListCellRenderer) lcr; ((TimePanel) someObj).setPlayFilterVisible(elcr.getIconPackage() .isShowPlayFilter()); } } } public void findAndUndo(Object someObj) { if (someObj instanceof OMEventHandler) { removeEventHandler((OMEventHandler) someObj); } if (someObj == getClock()) { setClock(null); } if (someObj instanceof MapBean) { setMap((MapBean) null); } if (someObj instanceof FilterPresenter && someObj != this) { macroFilters.remove(someObj); ((FilterPresenter) someObj).removePropertyChangeListener(this); } if (someObj == parentPanel) { parentPanel = null; } if (someObj == aesc) { aesc.removePropertyChangeListener(this); aesc = null; } } public void setClock(Clock cl) { if (clock != null) { clock.removeTimeEventListener(this); clock.removeTimeBoundsListener(this); } clock = cl; if (clock != null) { clock.addTimeEventListener(this); clock.addTimeBoundsListener(this); } } public Clock getClock() { return clock; } public void setMap(MapBean map) { this.map = map; } public MapBean getMap() { return map; } /** * @return Returns the displayTimeWindow. */ public long getDisplayTimeWindow() { return displayTimeWindow; } /** * @param displayTimeWindow The displayTimeWindow to set. */ public void setDisplayTimeWindow(long displayTimeWindow) { this.displayTimeWindow = displayTimeWindow; } public void updateTimeBounds(TimeBoundsEvent tbe) { // This section used to be called when the Clock.TIME_BOUNDS_PROPERTY // was sent. allEvents = null; if (displayList != null) { displayList.repaint(); } // ///// // This part got called when a TimeBoundsProvider was activated or // deactivated. // This gets called when the events change! Need to tell the // timelines to recharge... lastSelectedEvent = null; // Resets active and all event lists. rebuildEventList(); firePropertyChange(EventAttributesUpdatedProperty, null, Boolean.TRUE); // /////////// } public void updateTime(TimeEvent te) { highlightCurrentEvent(te.getSystemTime()); if (displayList != null) { displayList.repaint(); } } /* * (non-Javadoc) * * @seejava.beans.PropertyChangeListener#propertyChange(java.beans. * PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent evt) { String eventPropertyName = evt.getPropertyName(); if (eventPropertyName == FilterPresenter.FILTER_STATE) { boolean rebuildFilters = ((Boolean) evt.getNewValue()).booleanValue(); if (rebuildFilters) { logger.fine(eventPropertyName + " rebuilding filters and updating interface (list rebuild to follow)"); retrieveFiltersFromEventHandlers(); updateInterface(); } else { logger.fine(eventPropertyName + " clearing active filters and rebuilding list"); activeFilters = null; rebuildEventList(); } } if (eventPropertyName == OMEventSelectionCoordinator.EventsSelectedProperty) { scrollToSelected((List<OMEvent>) evt.getNewValue()); displayList.repaint(); } } protected void scrollToSelected(List<OMEvent> selectedEvents) { if (selectedEvents != null && !selectedEvents.isEmpty()) { OMEvent event = selectedEvents.get(0); lastSelectedEvent = event; // need to check timestamps because the first selected event might // not be visible, so if we come across a dlm timestamp that's // greater than this one, that should be the first selected event. long timeStamp = event.getTimeStamp(); OMEvent dlmEvent = null; ListModel dlm = getListModel(); int fvi = displayList.getFirstVisibleIndex(); int lvi = displayList.getLastVisibleIndex(); int numRowsVisible = lvi - fvi; int size = dlm.getSize(); for (int i = 0; i < size; i++) { dlmEvent = (OMEvent) dlm.getElementAt(i); if (dlmEvent == event || dlmEvent.getTimeStamp() > timeStamp) { if (i < fvi || i > lvi - 1) { fvi = i; if (size - i > numRowsVisible) { lvi = i + numRowsVisible - 1; } else { lvi = size; } Rectangle rect = displayList.getCellBounds(fvi, lvi); if (rect != null) { displayList.scrollRectToVisible(rect); } } break; } } } } protected long currentTime; /** * @param newCurrentTime the time to use for selecting current event. */ protected synchronized void highlightCurrentEvent(long newCurrentTime) { currentTime = newCurrentTime; if (displayList == null) return; ListModel dlm = displayList.getModel(); boolean timeMarked = false; OMEvent lastClosest = null; int size = dlm.getSize(); for (int i = 0; i < size; i++) { OMEvent event = (OMEvent) dlm.getElementAt(i); long timeStamp = event.getTimeStamp(); boolean atCurrentTime = (Math.abs(timeStamp - currentTime) <= displayTimeWindow); event.setAtCurrentTime(atCurrentTime); // This work is done to mark the closest, but not over, // event marking where the time is. timeMarked = atCurrentTime || timeMarked; if (!timeMarked) { if (timeStamp < currentTime) { lastClosest = event; } else if (lastClosest != null) { lastClosest.setAtCurrentTime(true); timeMarked = true; } } } } public void mouseClicked(MouseEvent e) { // hideDetails(); // // if (e.getButton() == MouseEvent.BUTTON3 && displayList != null) { // // Right click // int index = getDisplayListIndex(e); // lastIndexOfCellDetail = index; // ListModel model = displayList.getModel(); // Object obj = model.getElementAt(index); // // String labelContents = null; // // if (obj instanceof OMEvent) { // OMEvent ae = (OMEvent) obj; // labelContents = ae.getDetailedInformation(); // } // // if (labelContents == null) { // labelContents = " No further information available. "; // } // // showDetails(labelContents); // } } public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mouseDragged(MouseEvent e) {} public void mouseMoved(MouseEvent e) { // if (lastIndexOfCellDetail != -1 // && lastIndexOfCellDetail != getDisplayListIndex(e)) { // hideDetails(); // } } protected int lastIndexOfCellDetail = -1; public void showDetails(String contents) { if (contents != null) { detailSpace.setText("<html><body bgcolor=\"#ffffcc\"> <font style=\"plain\">" + contents + "</html>"); } detailSpacePanel.setVisible(contents != null); } public void hideDetails() { if (detailSpacePanel != null) { detailSpacePanel.setVisible(false); } lastIndexOfCellDetail = -1; } protected int getDisplayListIndex(MouseEvent e) { int index = 0; if (displayList != null) { double height = getDisplayListCellHeight(); index = e.getY() / (int) height; if (logger.isLoggable(Level.FINE)) { logger.fine("For " + e.getY() + ", the first vis: " + displayList.getFirstVisibleIndex() + ", heights are " + height + ", so index of click is " + index); } } return index; } protected double getDisplayListCellHeight() { double height = 0; if (displayList != null) { int rlFVI = displayList.getFirstVisibleIndex(); Rectangle bounds = displayList.getCellBounds(rlFVI, rlFVI); height = bounds.getHeight(); } return height; } JPanel eventControlPanel = null; protected JComponent getEventControlPanel() { if (eventControlPanel == null) { eventControlPanel = cellRenderer.getIconPackage() .createEventControlPanel(this); } JToolBar jsp = new JToolBar(); jsp.add(eventControlPanel); jsp.setFloatable(false); jsp.setBorder(BorderFactory.createEmptyBorder()); // return eventControlPanel; return jsp; } public void setSelectedEventsAttribute(Object key, Object value) { for (Iterator<OMEvent> it = getActiveEvents(); it.hasNext();) { OMEvent OMe = it.next(); if (OMe.getAttribute(OMEvent.ATT_KEY_SELECTED) != null) { OMe.putAttribute(key, value); } } resolveSelectionForHiddenEvents(key, value); firePropertyChange(EventAttributesUpdatedProperty, null, Boolean.TRUE); displayList.repaint(); } public void resolveSelectionForHiddenEvents(Object key, Object value) { boolean inRange = false; for (Iterator<OMEvent> iIt = getAllEvents(); iIt.hasNext();) { OMEvent iEvent = (OMEvent) iIt.next(); if (iEvent.getAttribute(OMEvent.ATT_KEY_SELECTED) == OMEvent.ATT_VAL_SELECTED_START_RANGE) { inRange = true; } else if (iEvent.getAttribute(OMEvent.ATT_KEY_SELECTED) == OMEvent.ATT_VAL_SELECTED_END_RANGE) { inRange = false; } else if (inRange) { if (logger.isLoggable(Level.FINER)) { logger.finer("setting " + iEvent.getClass().getName() + " " + key + ", " + value); } iEvent.putAttribute(key, value); } } } public void setAllEventsAttribute(Object key, Object value) { for (Iterator<OMEvent> it = getActiveEvents(); it.hasNext();) { ((OMEvent) it.next()).putAttribute(key, value); } displayList.repaint(); } public static void main(String[] argv) { javax.swing.JFrame frame = new javax.swing.JFrame("EventListPresenter"); EventListPresenter elp = new EventListPresenter(); frame.add(elp); frame.pack(); frame.setVisible(true); } }