// ********************************************************************** // // <copyright> // // BBN Technologies // 10 Moulton Street // Cambridge, MA 02138 // (617) 873-8000 // // Copyright (C) BBNT Solutions LLC. All rights reserved. // // </copyright> // ********************************************************************** // // $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/InformationDelegator.java,v $ // $RCSfile: InformationDelegator.java,v $ // $Revision: 1.17 $ // $Date: 2006/02/27 23:16:26 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap; import java.awt.Cursor; import java.awt.Font; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.net.URL; import java.util.ArrayList; import java.util.Properties; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.SwingConstants; import javax.swing.ToolTipManager; import com.bbn.openmap.event.InfoDisplayEvent; import com.bbn.openmap.event.InfoDisplayListener; import com.bbn.openmap.event.MapMouseMode; import com.bbn.openmap.event.ProgressEvent; import com.bbn.openmap.event.ProgressListener; import com.bbn.openmap.gui.MapPanelChild; import com.bbn.openmap.gui.OMComponentPanel; import com.bbn.openmap.gui.StatusLightPanel; import com.bbn.openmap.util.Debug; import com.bbn.openmap.util.PropUtils; import com.bbn.openmap.util.WebBrowser; /** * The InformationDelegator manages the display of information requested by * Layers and other map components. It can bring up a web browser to display web * pages and files, and pop up a message window to provide status information to * the user. It also has a visible status window that contains a layer status * indicator, and an information line that can display short messages. * <p> * InformationDelegators are added to layers, and the layer fires events through * the InfoDisplayListener interface. The InformationDelegator has a method * called listenToLayers() that lets you give it an array of layers, and it adds * itself as a InfoDisplayListener to those layers. * <p> * The InformationDelegator lets you alter its behavior with property settings: * * <pre> * * # Make the status lights buttons that bring up layer palettes. * infoDelegator.triggers=true * # Show the layer status lights. * infoDelegator.showLights=true * # Show the text line for map object information * infoDelegator.showInfoLine=true * # Show the text line for coordinate information * infoDelegator.showCoordsInfoLine=true * * </pre> */ public class InformationDelegator extends OMComponentPanel implements InfoDisplayListener, PropertyChangeListener, ProgressListener, MapPanelChild { protected JLabel infoLineHolder; protected JLabel infoLineHolder2; protected WebBrowser browser; protected StatusLightPanel statusBar; protected JProgressBar progressBar; protected MapBean map; protected ToolTipManager ttmanager; protected String propertyPrefix; private String fudgeString = " "; /** * Used to remember what the MouseModeCursor is, which is the base cursor * setting for the MapBean. The gesture modes set this cursor, and it gets * used when the currentMapBeanCursor is null. */ protected Cursor fallbackMapBeanCursor = Cursor.getDefaultCursor(); /** * Used to remember any cursor that may bave been requested by a layer. This * is usually null, unless a layer has requested a cursor. The MapBean * gesture modes set the fallbackMapBeanCursor instead. */ protected Cursor currentMapBeanCursor = null; protected boolean waitingForLayers = false; protected boolean showWaitCursor = false; /** * Flag to show the status lights. */ protected boolean showLights = true; public final static String ShowLightsProperty = "showLights"; public final static String ShowInfoLineProperty = "showInfoLine"; public final static String ShowCoordsInfoLineProperty = "showCoordsInfoLine"; public final static String WebBrowserClassProperty = "webBrowserClass"; public final static int MAP_OBJECT_INFO_LINE = 0; // Default public final static int COORDINATE_INFO_LINE = 1; protected ArrayList infoLineOrganizer = new ArrayList(); public InformationDelegator() { super(); initInfoWidgets(); } /** * If you want to subclass the InformationDelegator and have it handle * messages differently, you can override this method to set the widgets you * want. */ public void initInfoWidgets() { Debug.message("info", "InformationDelegator.initInfoWidgets"); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setFont(new Font("Helvetica", Font.PLAIN, 9)); setLayout(gridbag); progressBar = new JProgressBar(); gridbag.setConstraints(progressBar, c); add(progressBar); progressBar.setVisible(false); JPanel infoLinePanel = new JPanel(); c.weightx = 1; c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.HORIZONTAL; gridbag.setConstraints(infoLinePanel, c); GridBagLayout gridbag2 = new GridBagLayout(); GridBagConstraints c2 = new GridBagConstraints(); infoLinePanel.setLayout(gridbag2); infoLineHolder = new JLabel(fudgeString); c2.weightx = .9; c2.fill = GridBagConstraints.HORIZONTAL; c2.anchor = GridBagConstraints.WEST; c2.insets = new Insets(3, 10, 3, 10); gridbag2.setConstraints(infoLineHolder, c2); infoLinePanel.add(infoLineHolder); infoLineHolder2 = new JLabel(fudgeString, SwingConstants.RIGHT); c2.weightx = .1; c2.anchor = GridBagConstraints.EAST; gridbag2.setConstraints(infoLineHolder2, c2); infoLinePanel.add(infoLineHolder2); addInfoLine(COORDINATE_INFO_LINE, infoLineHolder); addInfoLine(MAP_OBJECT_INFO_LINE, infoLineHolder2); add(infoLinePanel); c.weightx = 0; c.anchor = GridBagConstraints.EAST; statusBar = new StatusLightPanel(); gridbag.setConstraints(statusBar, c); add(statusBar); statusBar.setVisible(showLights); } /** * Set the MapBean so that when the mouse mode changes, the cursor can * change. This gets called from findAndInit if a MapHandler is involved * with the application. */ public void setMap(MapBean map) { if (this.map != null) { this.map.removePropertyChangeListener(this); } this.map = map; if (map != null) { map.addPropertyChangeListener(this); fallbackMapBeanCursor = map.getCursor(); } } /** * Listen for changes to the active mouse mode and for any changes to the * list of available mouse modes. If the active mouse mode is "gestures", * then the lat lon updates to the status line are deactivated. */ public void propertyChange(PropertyChangeEvent evt) { String propName = evt.getPropertyName(); if (propName == MapBean.CursorProperty) { fallbackMapBeanCursor = ((Cursor) evt.getNewValue()); } else { if (propName == MouseDelegator.ActiveModeProperty) { MapMouseMode mmm = (MapMouseMode) evt.getNewValue(); setResetCursor(mmm.getModeCursor()); } else if (propName == MapBean.LayersProperty) { resetForLayers((Layer[]) evt.getNewValue(), (Layer[]) evt.getOldValue()); } else if (propName != MapBean.ProjectionProperty) { // For stuff we don't care about, just return from // here. Otherwise, reset the GUI below... return; } // Clear out all the information lines, resetting the GUI setAllLabels(fudgeString); } initToolTip(); } /** * Set the InformationDelegator on Layers. Usually called because the layers * changed on the map, and we need to add the InformationDelegator as an * InfoDisplayListener for the new layers, and remove it from the old * layers. */ public void resetForLayers(Layer[] connectToLayers, Layer[] removeFromLayers) { int i = 0; if (removeFromLayers != null && removeFromLayers.length != 0) { int removeLength = removeFromLayers.length; for (i = 0; i < removeLength; i++) { removeFromLayers[i].removeInfoDisplayListener(this); } } if (connectToLayers != null && connectToLayers.length != 0) { int removeLength = connectToLayers.length; for (i = 0; i < removeLength; i++) { connectToLayers[i].addInfoDisplayListener(this); } } } /** * Receive a ProgressEvent, and use it if possible. */ public void updateProgress(ProgressEvent evt) { if (progressBar != null) { int type = evt.getType(); if (type == ProgressEvent.START || type == ProgressEvent.UPDATE) { progressBar.setVisible(true); progressBar.setValue(evt.getPercentComplete()); setLabel(evt.getTaskDescription()); } else { progressBar.setVisible(false); } } } public void addInfoLine(int refIndex, JLabel iLine) { try { infoLineOrganizer.set(refIndex, iLine); } catch (IndexOutOfBoundsException ioobe) { while (refIndex > 0 && infoLineOrganizer.size() <= refIndex + 1) { infoLineOrganizer.add(iLine); } } } public void removeInfoLine(int refIndex) { try { infoLineOrganizer.set(refIndex, null); } catch (IndexOutOfBoundsException iiobe) { } } /** * Set the information line label. * * @param str String */ public void setLabel(String str) { setLabel(str, MAP_OBJECT_INFO_LINE); } public void setAllLabels(String str) { for (int i = 0; i < infoLineOrganizer.size(); i++) { setLabel(str, i); } } /** * Set the information line label. * * @param str String * @param infoLineDesignator the designator used to specify which * information line to use to display the string. */ public void setLabel(String str, int infoLineDesignator) { JLabel iLine; try { iLine = (JLabel) infoLineOrganizer.get(infoLineDesignator); } catch (IndexOutOfBoundsException ioobe) { // This should be OK. iLine = (JLabel) infoLineOrganizer.get(MAP_OBJECT_INFO_LINE); } if (iLine != null) { iLine.setText(str); } } /** * The method that updates the InformationDelegator display window with the * correct layer representation. A status light reset method. */ protected void setStatusBar() { statusBar.reset(); } public void setBrowser(WebBrowser wb) { browser = wb; browser.setInfoDelegator(this); } public WebBrowser getBrowser() { return browser; } /** * Try to display a URL in a web browser. */ public void displayURL(String url) { WebBrowser wb = getBrowser(); if (wb == null) { MapHandler mh = (MapHandler) getBeanContext(); Frame frame = null; if (mh != null) { frame = (Frame) mh.get(java.awt.Frame.class); } try { com.bbn.openmap.gui.MiniBrowser.display(frame, new URL(url)); } catch (java.net.MalformedURLException murle) { Debug.error("InformationDelegator can't launch " + url); } } else { wb.launch(url); } } /** * Display a html String in a window. */ public void displayBrowserContent(String content) { MapHandler mh = (MapHandler) getBeanContext(); Frame frame = null; if (mh != null) { frame = (Frame) mh.get(java.awt.Frame.class); } com.bbn.openmap.gui.MiniBrowser.display(frame, "text/html", content); } /** * Display a line of text in a info line. */ public void displayInfoLine(String infoLine) { displayInfoLine(infoLine, MAP_OBJECT_INFO_LINE); } /** * Display a line of text in a designated info line. */ public void displayInfoLine(String infoLine, int labelDesignator) { if (infoLineHolder != null) { setLabel((infoLine != null && infoLine.length() > 0) ? infoLine : fudgeString, labelDesignator); } } /** * Display a message in a pop-up window. */ public void displayMessage(String title, String message) { if (Environment.getBoolean(Environment.UseInternalFrames)) { JOptionPane.showInternalMessageDialog(Environment.getInternalFrameDesktop(), message, title, JOptionPane.INFORMATION_MESSAGE); } else { JOptionPane.showMessageDialog(null, message, title, JOptionPane.INFORMATION_MESSAGE); } } // ///////////////////////////////////////// // InfoDisplayListener interface /** * Handle layer requests to have a URL displayed in a Browser. * * @param event InfoDisplayEvent */ public void requestURL(InfoDisplayEvent event) { displayURL(event.getInformation()); } /** * Handle layer requests to have a message displayed in a dialog window. * * @param event InfoDisplayEvent */ public void requestMessage(InfoDisplayEvent event) { Layer l = event.getLayer(); String layername = (l == null) ? null : l.getName(); displayMessage("Message from " + layername + " layer:", event.getInformation()); } /** * Handle layer requests to have an information line displayed in an * application status window. * * @param event InfoDisplayEvent */ public void requestInfoLine(InfoDisplayEvent event) { displayInfoLine(event.getInformation(), event.getPreferredLocation()); } /** * Handle layer requests that plain text or html text be displayed in a * browser. * * @param event InfoDisplayEvent */ public void requestBrowserContent(InfoDisplayEvent event) { displayBrowserContent(event.getInformation()); } /** * If a tooltip is required over a spot on the map then a * <code>MouseMapListener</code> should pass a MouseEvent to this method. * The Swing ToolTipManager is used to achieve this. A call to this method * should always be followed by a call to <code>hideToolTip</code> * * @param me A MouseEvent from a <code>MapMouseListener</code> which * indicates where the tooltip is to appear (unused) * @param event an event containing the ToolTip to show * @deprecated use requestShowToolTip(InfoDisplayEvent) instead. */ public void requestShowToolTip(MouseEvent me, InfoDisplayEvent event) { requestShowToolTip(event); } /** * If a tooltip is required over a spot on the map then a * <code>MouseMapListener</code> should pass a MouseEvent to this method. * The Swing ToolTipManager is used to achieve this. A call to this method * should always be followed by a call to <code>hideToolTip</code> * * @param event an event containing the ToolTip to show */ public void requestShowToolTip(InfoDisplayEvent event) { // shows a tooltip over the map if (map != null) { if (ttmanager == null) { initToolTip(); } map.setToolTipText(event.getInformation()); } } /** * This method should follow a call to showToolTip in order to indicate that * the tooltip should no longer be displayed. This method should always * follow a call to <code>showToolTip</code> * * @param me A MouseEvent which passes from a MapMouseListener to indicate * that a tooltip should disappear * @deprecated call requestHideToolTip() instead. */ public void requestHideToolTip(MouseEvent me) { requestHideToolTip(); } /** * This method should follow a call to showToolTip in order to indicate that * the tooltip should no longer be displayed. This method should always * follow a call to <code>showToolTip</code> */ public void requestHideToolTip() { initToolTip(); } /** * This method should be called to initialize the tooltip status so that an * old tooltip doesn't remain when a layer starts listening to mouse events. */ public void initToolTip() { if (ttmanager == null) { // make sure the MapBean is registered first ttmanager = ToolTipManager.sharedInstance(); ttmanager.registerComponent(map); ttmanager.setEnabled(true); return; } // If it already exists, clear out the current tip if (map != null) { map.setToolTipText(null); } } /** * Change the cursor for the MapBean. If the MapBean hasn't been set, then * nothing will happen on the screen. If a null value is passed in, the * cursor is reset to the MouseMode value. If the InformationDelegator is * allowed to show the wait cursor, and the layers are busy, the wait cursor * will take precedence. The requested cursor from a layer will be set if * the layers finish. * * @param cursor java.awt.Cursor to change the cursor to. */ public void requestCursor(java.awt.Cursor cursor) { // This is interpreted as a release from a requester if (cursor == null) { // If we're not supposed to be showing the wait cursor... if (showWaitCursor && !waitingForLayers) resetCursor(); // Set this to null, so that when we're done waiting for // the layers, we'll just reset. currentMapBeanCursor = null; } else if (this.map != null) { Cursor newCursor; // If we're supposed to be showing the watch, do it, but // save the request for when the layers are done. if (showWaitCursor && waitingForLayers) { newCursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR); currentMapBeanCursor = cursor; } else newCursor = cursor; map.setCursor(newCursor); } } /** * Set the cursor to use when the waiting is done, if a layer hasn't asked * for one to be displayed. For the MouseMode changes, this is automatically * called. */ public void setResetCursor(java.awt.Cursor cursor) { fallbackMapBeanCursor = cursor; } /** * Sets the cursor over the mapbean to the assigned default, or whatever has * been set by the MouseMode. */ public void resetCursor() { if (this.map != null) map.setCursor(fallbackMapBeanCursor); } /** * If the value passed in is true, the cursor over the MapBean will be the * waiting cursor layers are off working. The status lights will work, too, * no matter what the value is. If false, the cursor won't change if the * layers are working. */ public void setShowWaitCursor(boolean value) { showWaitCursor = value; } /** * Returns whether the wait cursor will be shown if the layers are working. */ public boolean isShowWaitCursor() { return showWaitCursor; } // ////////// MapHandlerChild methods overridden from // OMComponentPanel /** * Called when an object has been added to the BeanContext. The * InformationDelegator will look for certain objects it needs. */ public void findAndInit(Object someObj) { if (someObj instanceof MapBean) { setMap((MapBean) someObj); } if (someObj instanceof MouseDelegator) { MouseDelegator md = (MouseDelegator) someObj; md.addPropertyChangeListener(this); } statusBar.findAndInit(someObj); } /** * Called when an object is being removed from the BeanContext. Will cause * the object to be disconnected from the InformationDelegator if it is * being used. */ public void findAndUndo(Object someObj) { if (someObj instanceof MapBean) { setMap(null); } if (someObj instanceof MouseDelegator) { MouseDelegator md = (MouseDelegator) someObj; md.removePropertyChangeListener(this); } statusBar.findAndUndo(someObj); } // ///// PropertyConsumer methods overridden from OMComponentPanel public void setProperties(String prefix, Properties props) { setPropertyPrefix(prefix); prefix = PropUtils.getScopedPropertyPrefix(prefix); statusBar.setProperties(prefix, props); setShowLights(PropUtils.booleanFromProperties(props, prefix + ShowLightsProperty, showLights)); setShowInfoLine(PropUtils.booleanFromProperties(props, prefix + ShowInfoLineProperty, getShowInfoLine())); setShowCoordsInfoLine(PropUtils.booleanFromProperties(props, prefix + ShowCoordsInfoLineProperty, getShowCoordsInfoLine())); String pl = props.getProperty(prefix + PreferredLocationProperty); if (pl != null) { setPreferredLocation(pl); } Object webBrowserObj = PropUtils.objectFromProperties(props, prefix + WebBrowserClassProperty); if (webBrowserObj instanceof WebBrowser) { setBrowser((WebBrowser) webBrowserObj); } } public Properties getProperties(Properties props) { if (props == null) { props = new Properties(); } statusBar.getProperties(props); String prefix = PropUtils.getScopedPropertyPrefix(this); props.put(prefix + ShowLightsProperty, new Boolean(showLights).toString()); props.put(prefix + ShowInfoLineProperty, new Boolean(getShowInfoLine()).toString()); props.put(prefix + ShowCoordsInfoLineProperty, new Boolean(getShowCoordsInfoLine()).toString()); props.put(prefix + PreferredLocationProperty, getPreferredLocation()); WebBrowser wb = getBrowser(); if (wb != null) { props.put(prefix + WebBrowserClassProperty, wb.getClass().getName()); } return props; } public Properties getPropertyInfo(Properties props) { if (props == null) { props = new Properties(); } statusBar.getPropertyInfo(props); PropUtils.setI18NPropertyInfo(i18n, props, InformationDelegator.class, ShowLightsProperty, "Show Layer Status", "Show the layer status lights.", "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor"); PropUtils.setI18NPropertyInfo(i18n, props, InformationDelegator.class, ShowInfoLineProperty, "Show Map Information", "Show the text line containing map object information.", "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor"); PropUtils.setI18NPropertyInfo(i18n, props, InformationDelegator.class, ShowCoordsInfoLineProperty, "Show Coordinate Information", "Show the text line containing coordinate information.", "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor"); PropUtils.setI18NPropertyInfo(i18n, props, InformationDelegator.class, PreferredLocationProperty, "Preferred Location", "Set the preferred location of the information lines (default under the map).", null); PropUtils.setI18NPropertyInfo(i18n, props, InformationDelegator.class, WebBrowserClassProperty, "WebBrowser Class", "Set an object to be used to handle displaying web pages (default is null).", null); return props; } // ///// Setters and Getters public void setInfoLineHolder(JLabel ilh) { infoLineHolder = ilh; } public JLabel getInfoLineHolder() { return infoLineHolder; } public void setProgressBar(JProgressBar progressBar) { this.progressBar = progressBar; } public JProgressBar getProgressBar() { return progressBar; } public void setShowLights(boolean set) { showLights = set; statusBar.setVisible(set); } public boolean getShowLights() { return showLights; } /** * This only holds if the MAP_OBJECT_INFO_LINE has been created. * * @param set sets the visibility of the information line for map object * information. */ public void setShowInfoLine(boolean set) { if (infoLineHolder2 != null) { infoLineHolder2.setVisible(set); } } public boolean getShowInfoLine() { boolean ret = true; if (infoLineHolder2 != null) { ret = infoLineHolder2.isVisible(); } return ret; } /** * This only holds if the COORDINATE_INFO_LINE has been created. * * @param set sets the visibility of the information line for coordinate * information. */ public void setShowCoordsInfoLine(boolean set) { if (infoLineHolder != null) { infoLineHolder.setVisible(set); } } public boolean getShowCoordsInfoLine() { boolean ret = true; if (infoLineHolder != null) { ret = infoLineHolder.isVisible(); } return ret; } public void setLightTriggers(boolean set) { statusBar.setLightTriggers(set); } public boolean getLightTriggers() { return statusBar.getLightTriggers(); } public void setFloatable(boolean value) { } /** * BorderLayout.SOUTH by default for this class. */ protected String preferredLocation = java.awt.BorderLayout.SOUTH; /** * MapPanelChild method. */ public void setPreferredLocation(String value) { preferredLocation = value; } /** * MapPanelChild method. */ public String getPreferredLocation() { return preferredLocation; } public String getParentName() { // TODO Auto-generated method stub return null; } }