// // DisplayImpl.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and Tommy Jasmin. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package visad; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.image.BufferedImage; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.UnmarshalException; import java.util.Enumeration; import java.util.Iterator; import java.util.ListIterator; import java.util.Vector; import javax.swing.BoxLayout; import javax.swing.JPanel; import javax.swing.SwingUtilities; import visad.browser.Convert; import visad.browser.Divider; import visad.collab.ControlMonitorEvent; import visad.collab.DisplayMonitor; import visad.collab.DisplayMonitorImpl; import visad.collab.DisplaySync; import visad.collab.DisplaySyncImpl; import visad.collab.MonitorEvent; import visad.util.AnimationWidget; import visad.util.ContourWidget; import visad.util.GMCWidget; import visad.util.LabeledColorWidget; import visad.util.RangeWidget; import visad.util.SelectRangeWidget; import visad.util.VisADSlider; /** DisplayImpl is the abstract VisAD superclass for display implementations. It is runnable.<P> DisplayImpl is not Serializable and should not be copied between JVMs.<P> */ public abstract class DisplayImpl extends ActionImpl implements LocalDisplay { /** instance variables */ /** * a Vector of ScalarMap objects; * does not include ConstantMap objects */ private Vector MapVector = new Vector(); /** a Vector of ConstantMap objects */ private Vector ConstantMapVector = new Vector(); /** * a Vector of RealType (and TextType) objects occuring * in MapVector */ private Vector RealTypeVector = new Vector(); /** a Vector of DisplayRealType objects occuring in MapVector */ private Vector DisplayRealTypeVector = new Vector(); /** * list of Control objects linked to ScalarMap objects in MapVector; * the Control objects may be linked to UI widgets, or just computed */ private Vector ControlVector = new Vector(); /** ordered list of DataRenderer objects that render Data objects */ private Vector RendererVector = new Vector(); /** * list of objects interested in learning when DataRenderers * are deleted from this Display */ private Vector RendererSourceListeners = new Vector(); /** * list of objects interested in learning when Data objects * are deleted from this Display */ private Vector RmtSrcListeners = new Vector(); /** * list of objects interested in receiving messages * from this Display */ private Vector MessageListeners = new Vector(); /** DisplayRenderer object for background and metadata rendering */ private DisplayRenderer displayRenderer; /** * Component where data depictions are rendered; * must be set by concrete subclass constructor; * may be null for off-screen displays */ Component component; /** */ private ComponentChangedListener componentListener = null; /** * set to indicate need to compute ranges of RealType-s * and sampling for Animation */ private boolean initialize = true; /** * set to indicate that ranges should be auto-scaled * every time data are displayed */ private boolean always_initialize = false; /** set to re-display all linked Data */ private boolean redisplay_all = false; /** * length of ValueArray of distinct DisplayRealType values; * one per Single DisplayRealType that occurs in a ScalarMap, * plus one per ScalarMap per non-Single DisplayRealType; * ScalarMap.valueIndex is an index into ValueArray */ private int valueArrayLength; /** mapping from ValueArray to DisplayScalar */ private int[] valueToScalar; /** mapping from ValueArray to MapVector */ private int[] valueToMap; /** Vector of DisplayListeners */ private final transient Vector ListenerVector = new Vector(); /** */ private Object mapslock = new Object(); // WLH 16 March 99 /** */ private MouseBehavior mouse = null; // objects which monitor and synchronize with remote displays /** */ private transient DisplayMonitor displayMonitor = null; /** */ private transient DisplaySync displaySync = null; // activity monitor /** */ private transient DisplayActivity displayActivity = null; // Support for printing /** */ private Printable printer; /** has this display been destroyed */ private boolean destroyed = false; /** * construct a DisplayImpl with given name and DisplayRenderer * @param name String name for DisplayImpl (used for debugging) * @param renderer DisplayRenderer that controls aspects of the * display not specific to any particular Data * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public DisplayImpl(String name, DisplayRenderer renderer) throws VisADException, RemoteException { super(name); // put system intrinsic DisplayRealType-s in DisplayRealTypeVector for (int i = 0; i < DisplayRealArray.length; i++) { DisplayRealTypeVector.addElement(DisplayRealArray[i]); } displayMonitor = new DisplayMonitorImpl(this); displaySync = new DisplaySyncImpl(this); if (renderer != null) { displayRenderer = renderer; } else { displayRenderer = getDefaultDisplayRenderer(); } displayRenderer.setDisplay(this); // initialize ScalarMap's, ShadowDisplayReal's and Control's clearMaps(); } /** * construct a DisplayImpl collaborating with the given RemoteDisplay, * and with the given DisplayRenderer * @param rmtDpy RemoteDisplay to collaborate with * @param renderer DisplayRenderer that controls aspects of the * display not specific to any particular Data * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public DisplayImpl(RemoteDisplay rmtDpy, DisplayRenderer renderer) throws VisADException, RemoteException { // this(rmtDpy, renderer, false); super(rmtDpy.getName() + ".remote"); // WLH 11 April 2001 // get class used for remote display String className = rmtDpy.getDisplayClassName(); Class rmtClass; try { rmtClass = Class.forName(className); } catch (ClassNotFoundException cnfe) { throw new DisplayException("Cannot find remote display class " + className); } // make sure this display class // is compatible with the remote display class if (!rmtClass.isInstance(this)) { throw new DisplayException("Cannot construct " + getClass().getName() + " from remote " + className); } // put system intrinsic DisplayRealType-s in DisplayRealTypeVector for (int i = 0; i < DisplayRealArray.length; i++) { DisplayRealTypeVector.addElement(DisplayRealArray[i]); } displayMonitor = new DisplayMonitorImpl(this); displaySync = new DisplaySyncImpl(this); if (renderer != null) { displayRenderer = renderer; } else { try { String name = rmtDpy.getDisplayRendererClassName(); Object obj = Class.forName(name).newInstance(); displayRenderer = (DisplayRenderer)obj; } catch (Exception e) { displayRenderer = getDefaultDisplayRenderer(); } } displayRenderer.setDisplay(this); // initialize ScalarMap's, ShadowDisplayReal's and Control's clearMaps(); } // suck in any remote ScalarMaps /** * * * @param rmtDpy */ void copyScalarMaps(RemoteDisplay rmtDpy) { Vector m; try { m = rmtDpy.getMapVector(); } catch (Exception e) { System.err.println("Couldn't copy ScalarMaps"); return; } Enumeration me = m.elements(); while(me.hasMoreElements()) { ScalarMap sm = (ScalarMap)me.nextElement(); try { addMap((ScalarMap)sm.clone()); } catch (DisplayException de) { try { addMap(new ScalarMap(sm.getScalar(), sm.getDisplayScalar())); } catch (Exception e) { System.err.println("Couldn't copy remote ScalarMap " + sm); } } catch (Exception e) { e.printStackTrace(); } } } /** * copy ConstantMaps from RemoteDisplay to this * @param rmtDpy RemoteDisplay to get ConstantMaps from */ void copyConstantMaps(RemoteDisplay rmtDpy) { Vector c; try { c = rmtDpy.getConstantMapVector(); } catch (Exception e) { System.err.println("Couldn't copy ConstantMaps"); return; } Enumeration ce = c.elements(); while(ce.hasMoreElements()) { ConstantMap cm = (ConstantMap)ce.nextElement(); try { addMap((ConstantMap)cm.clone()); } catch (DisplayException de) { try { addMap(new ConstantMap(cm.getConstant(), cm.getDisplayScalar())); } catch (Exception e) { System.err.println("Couldn't copy remote ConstantMap " + cm); } } catch (Exception e) { e.printStackTrace(); } } } /** * copy GraphicsModeControl settings from RemoteDisplay to this * @param rmtDpy RemoteDisplay to get GraphicsModeControl settings from */ void copyGraphicsModeControl(RemoteDisplay rmtDpy) { GraphicsModeControl rc; try { getGraphicsModeControl().syncControl(rmtDpy.getGraphicsModeControl()); } catch (UnmarshalException ue) { System.err.println("Couldn't copy remote GraphicsModeControl"); return; } catch (java.rmi.ConnectException ce) { System.err.println("Couldn't copy remote GraphicsModeControl"); return; } catch (Exception e) { e.printStackTrace(); return; } } /** * copy DataReferences from RemoteDisplay to this * @param rmtDpy RemoteDisplay to get DataReferences from * @param localRefs array of DataReferences: don't get any * DataReference from rmtDpy that has the same * name as a DataReference in localRefs */ private void copyRefLinks(RemoteDisplay rmtDpy, DataReference[] localRefs) { Vector ml; if (rmtDpy == null) return; try { ml = rmtDpy.getReferenceLinks(); } catch (UnmarshalException ue) { System.err.println("Couldn't copy remote DataReferences"); return; } catch (java.rmi.ConnectException ce) { System.err.println("Couldn't copy remote DataReferences"); return; } catch (Exception e) { e.printStackTrace(); return; } String[] refNames; if (localRefs == null) { refNames = null; } else { refNames = new String[localRefs.length]; for (int i = 0; i < refNames.length; i++) { try { refNames[i] = localRefs[i].getName(); } catch (VisADException ve) { refNames[i] = null; } catch (RemoteException re) { refNames[i] = null; } } } Enumeration mle = ml.elements(); if (mle.hasMoreElements()) { DataRenderer dr = displayRenderer.makeDefaultRenderer(); String defaultClass = dr.getClass().getName(); while(mle.hasMoreElements()) { RemoteReferenceLink link = (RemoteReferenceLink)mle.nextElement(); // get reference to Data object RemoteDataReference ref; try { ref = link.getReference(); } catch (Exception e) { System.err.println("Couldn't copy remote DataReference"); ref = null; } if (ref != null && refNames != null) { String rName; try { rName = ref.getName(); } catch (VisADException ve) { System.err.println("Couldn't get DataReference name"); rName = null; } catch (RemoteException re) { System.err.println("Couldn't get remote DataReference name"); rName = null; } if (rName != null) { for (int i = 0; i < refNames.length; i++) { if (rName.equals(refNames[i])) { ref = null; break; } } } } if (ref != null) { // build array of ConstantMap values ConstantMap[] cm = null; try { Vector v = link.getConstantMapVector(); int len = v.size(); if (len > 0) { cm = new ConstantMap[len]; for (int i = 0; i < len; i++) { cm[i] = (ConstantMap)v.elementAt(i); } } } catch (Exception e) { System.err.println( "Couldn't copy ConstantMaps" + " for remote DataReference"); } // get proper DataRenderer DataRenderer renderer; try { String newClass = link.getRendererClassName(); if (newClass.equals(defaultClass)) { renderer = null; } else { Object obj = Class.forName(newClass).newInstance(); renderer = (DataRenderer)obj; } } catch (Exception e) { System.err.println( "Couldn't copy remote DataRenderer name" + "; using " + defaultClass); renderer = null; } // build RemoteDisplayImpl to which reference is attached try { RemoteDisplayImpl rd = new RemoteDisplayImpl(this); // if this reference uses the default renderer... if (renderer == null) { rd.addReference(ref, cm); } else { rd.addReferences(renderer, ref, cm); } } catch (Exception e) { System.err.println("Couldn't add remote DataReference " + ref); } } } } } /** * copy Data from RemoteDisplay to this * @param rmtDpy RemoteDisplay to get Data from * * @throws RemoteException * @throws VisADException */ protected void syncRemoteData(RemoteDisplay rmtDpy) throws VisADException, RemoteException { copyScalarMaps(rmtDpy); copyConstantMaps(rmtDpy); copyGraphicsModeControl(rmtDpy); copyRefLinks(rmtDpy, null); notifyAction(); waitForTasks(); // only add remote display as listener *after* we've synced displayMonitor.addRemoteListener(rmtDpy); initializeControls(); } // get current state of all controls from remote display(s) /** * */ private void initializeControls() { ListIterator iter = ControlVector.listIterator(); while(iter.hasNext()) { try { Control ctl = (Control)iter.next(); ControlMonitorEvent evt; evt = new ControlMonitorEvent(MonitorEvent.CONTROL_INIT_REQUESTED, (Control)ctl.clone()); displayMonitor.notifyListeners(evt); } catch (Exception e) { e.printStackTrace(); } } } /** RemoteDisplayImpl to this for use with remote DisplayListeners */ private RemoteDisplayImpl rd = null; /** * Notify this instance's {@link DisplayListener}s. * * @param id type of DisplayEvent that is to be sent * @param x the horizontal x coordinate for the mouse location in * the display component * @param y the vertical y coordinate for the mouse location in * the display component * @throws VisADException if a VisAD failure occurs. * @throws RemoteException if a Java RMI failure occurs. */ public void notifyListeners(int id, int x, int y) throws VisADException, RemoteException { notifyListeners(null, id, x, y); // notifyListeners(new DisplayEvent(this, id, x, y)); } /** * Notify this instance's {@link DisplayListener}s. * * @param evt The {@link DisplayEvent} to be passed to the * {@link DisplayListener}s. * @throws VisADException if a VisAD failure occurs. * @throws RemoteException if a Java RMI failure occurs. */ public void notifyListeners(DisplayEvent evt) throws VisADException, RemoteException { synchronized (eventStatus) { if (!eventStatus[evt.getId()]) return; // ignore disabled events } notifyListeners(evt, 0, 0, 0); } /** * Notify this instance's {@link DisplayListener}s. This method creates a runnable that actually * does the notification. It invokes the runnable directly if its in the Swing event dispatch thread. * Else, it invokes the runnable in a separate thread. * * @param evt The {@link DisplayEvent} to be passed to the * {@link DisplayListener}s. If this is null then construct the * event from the other parameters * @param id type of DisplayEvent that is to be sent * @param x the horizontal x coordinate for the mouse location in * the display component * @param y the vertical y coordinate for the mouse location in * the display component */ private void notifyListeners(final DisplayEvent evt, final int id, final int x, final int y) { Runnable runnable = new Runnable() { public void run() { try { DisplayEvent displayEvent = evt; if (displayEvent == null) { displayEvent = new DisplayEvent(DisplayImpl.this, id, x, y); } for (Enumeration listeners = ((Vector)ListenerVector.clone()).elements(); listeners.hasMoreElements(); ) { DisplayListener listener = (DisplayListener)listeners.nextElement(); if (listener instanceof Remote) { if (rd == null) { rd = new RemoteDisplayImpl(DisplayImpl.this); } listener.displayChanged(displayEvent.cloneButDisplay(rd)); } else { listener.displayChanged( displayEvent.cloneButDisplay(DisplayImpl.this)); } } } catch (Exception e) { e.printStackTrace(); } } }; if (SwingUtilities.isEventDispatchThread()) { runnable.run(); } else { SwingUtilities.invokeLater(runnable); } } /** * add a DisplayListener * @param listener DisplayListener to add */ public void addDisplayListener(DisplayListener listener) { ListenerVector.addElement(listener); } /** * remove a DisplayListener * @param listener DisplayListener to remove */ public void removeDisplayListener(DisplayListener listener) { ListenerVector.removeElement(listener); } /** * @return the java.awt.Component (e.g., JPanel or AppletPanel) * this DisplayImpl uses; returns null for an offscreen * DisplayImpl */ public Component getComponent() { return component; } /** * set the java.awt.Component this DisplayImpl uses * @param c Component to set */ public void setComponent(Component c) { if (c != null) { // lazy initialization if (componentListener == null) { componentListener = new ComponentChangedListener(this); } c.addComponentListener(componentListener); } // in case setComponent is called multiple times if (component != null) { if (componentListener != null) { component.removeComponentListener(componentListener); } } component = c; } /** * request auto-scaling of ScalarMap ranges the next time * Data are transformed into scene graph elements */ public void reAutoScale() { initialize = true; // printStack("reAutoScale"); } /** * if auto is true, re-apply auto-scaling of ScalarMap ranges * every time Display is triggered * @param a flag indicating whether to always re-apply auto-scaling */ public void setAlwaysAutoScale(boolean a) { always_initialize = a; } /** * request all linked Data to be re-transformed into scene graph * elements */ public void reDisplayAll() { redisplay_all = true; // printStack("reDisplayAll"); notifyAction(); } // CTR - begin code for slaved displays /** Internal list of slaves linked to this display. */ private Vector Slaves = new Vector(); /** * link a slave display to this * @param display RemoteSlaveDisplay to link */ public void addSlave(RemoteSlaveDisplay display) { if (!Slaves.contains(display)) Slaves.add(display); } /** * remove a link between a slave display and this * @param display RemoteSlaveDisplay to remove */ public void removeSlave(RemoteSlaveDisplay display) { if (Slaves.contains(display)) Slaves.remove(display); } /** * remove all links to slave displays */ public void removeAllSlaves() { Slaves.removeAllElements(); } /** * @return flag indicating whether there are any slave displays * linked to this display */ public boolean hasSlaves() { return !Slaves.isEmpty(); } /** * update all linked slave displays with the given image * @param img BufferedImage to send to all linked slave displays */ public void updateSlaves(BufferedImage img) { // extract pixels from image int width = img.getWidth(); int height = img.getHeight(); int type = img.getType(); int[] pixels = new int[width * height]; img.getRGB(0, 0, width, height, pixels, 0, width); // encode pixels with RLE int[] encoded = Convert.encodeRLE(pixels); synchronized (Slaves) { // send encoded pixels to each slave for (int i = 0; i < Slaves.size(); i++) { RemoteSlaveDisplay d = (RemoteSlaveDisplay)Slaves.elementAt(i); try { d.sendImage(encoded, width, height, type); } catch (java.rmi.ConnectException exc) { // remote slave client has died; remove it from list Slaves.remove(i--); } catch (RemoteException exc) { } } } } /** * update all linked slave display with the given message * @param message String to send to all linked slave displays */ public void updateSlaves(String message) { synchronized (Slaves) { // send message to each slave for (int i = 0; i < Slaves.size(); i++) { RemoteSlaveDisplay d = (RemoteSlaveDisplay)Slaves.elementAt(i); try { d.sendMessage(message); } catch (java.rmi.ConnectException exc) { // remote slave client has died; remove it from list Slaves.remove(i--); } catch (RemoteException exc) { } } } } // CTR - end code for slaved displays /** Enabled status flag for each DisplayEvent type. */ private final boolean[] eventStatus = { true, // (not used) true, // MOUSE_PRESSED true, // FRAME_DONE true, // TRANSFORM_DONE true, // MOUSE_PRESSED_LEFT true, // MOUSE_PRESSED_CENTER true, // MOUSE_PRESSED_RIGHT true, // MOUSE_RELEASED true, // MOUSE_RELEASED_LEFT true, // MOUSE_RELEASED_CENTER true, // MOUSE_RELEASED_RIGHT true, // MAP_ADDED true, // MAPS_CLEARED true, // REFERENCE_ADDED true, // REFERENCE_REMOVED true, // DESTROYED true, // KEY_PRESSED true, // KEY_RELEASED false, // MOUSE_DRAGGED false, // MOUSE_ENTERED false, // MOUSE_EXITED false, // MOUSE_MOVED false, // WAIT_ON false, // WAIT_OFF true, // MAP_REMOVED false, // COMPONENT_RESIZED }; /** * Enables reporting of a DisplayEvent of a given type * when it occurs in this display. * * @param id DisplayEvent type to enable. Valid types are: * <UL> * <LI>DisplayEvent.FRAME_DONE * <LI>DisplayEvent.TRANSFORM_DONE * <LI>DisplayEvent.MOUSE_PRESSED * <LI>DisplayEvent.MOUSE_PRESSED_LEFT * <LI>DisplayEvent.MOUSE_PRESSED_CENTER * <LI>DisplayEvent.MOUSE_PRESSED_RIGHT * <LI>DisplayEvent.MOUSE_RELEASED_LEFT * <LI>DisplayEvent.MOUSE_RELEASED_CENTER * <LI>DisplayEvent.MOUSE_RELEASED_RIGHT * <LI>DisplayEvent.MAP_ADDED * <LI>DisplayEvent.MAPS_CLEARED * <LI>DisplayEvent.REFERENCE_ADDED * <LI>DisplayEvent.REFERENCE_REMOVED * <LI>DisplayEvent.DESTROYED * <LI>DisplayEvent.KEY_PRESSED * <LI>DisplayEvent.KEY_RELEASED * <LI>DisplayEvent.MOUSE_DRAGGED * <LI>DisplayEvent.MOUSE_ENTERED * <LI>DisplayEvent.MOUSE_EXITED * <LI>DisplayEvent.MOUSE_MOVED * <LI>DisplayEvent.WAIT_ON * <LI>DisplayEvent.WAIT_OFF * <LI>DisplayEvent.MAP_REMOVED * <LI>DisplayEvent.COMPONENT_RESIZED * </UL> */ public void enableEvent(int id) { if (id < 1 || id >= eventStatus.length) return; synchronized (eventStatus) { eventStatus[id] = true; } } /** * Disables reporting of a DisplayEvent of a given type * when it occurs in this display. * * @param id DisplayEvent type to disable. Valid types are: * <UL> * <LI>DisplayEvent.FRAME_DONE * <LI>DisplayEvent.TRANSFORM_DONE * <LI>DisplayEvent.MOUSE_PRESSED * <LI>DisplayEvent.MOUSE_PRESSED_LEFT * <LI>DisplayEvent.MOUSE_PRESSED_CENTER * <LI>DisplayEvent.MOUSE_PRESSED_RIGHT * <LI>DisplayEvent.MOUSE_RELEASED_LEFT * <LI>DisplayEvent.MOUSE_RELEASED_CENTER * <LI>DisplayEvent.MOUSE_RELEASED_RIGHT * <LI>DisplayEvent.MAP_ADDED * <LI>DisplayEvent.MAPS_CLEARED * <LI>DisplayEvent.REFERENCE_ADDED * <LI>DisplayEvent.REFERENCE_REMOVED * <LI>DisplayEvent.DESTROYED * <LI>DisplayEvent.KEY_PRESSED * <LI>DisplayEvent.KEY_RELEASED * <LI>DisplayEvent.MOUSE_DRAGGED * <LI>DisplayEvent.MOUSE_ENTERED * <LI>DisplayEvent.MOUSE_EXITED * <LI>DisplayEvent.MOUSE_MOVED * <LI>DisplayEvent.WAIT_ON * <LI>DisplayEvent.WAIT_OFF * <LI>DisplayEvent.MAP_REMOVED * <LI>DisplayEvent.COMPONENT_RESIZED * </UL> */ public void disableEvent(int id) { if (id < 1 || id >= eventStatus.length) return; synchronized (eventStatus) { eventStatus[id] = false; } } /** * @param id DisplayEvent type * @return flag indicating whether a DisplayEvent of a given * type is eported when it occurs in this display. */ public boolean isEventEnabled(int id) { if (id < 1 || id >= eventStatus.length) { return false; } else { synchronized (eventStatus) { return eventStatus[id]; } } } /** * Link a reference to this Display. * This method may only be invoked after all links to * {@link visad.ScalarMap ScalarMaps} * have been made. * * @param ref data reference * * @exception VisADException if there was a problem with one or more * parameters. * @exception RemoteException if there was a problem adding the * data reference to the remote display. */ public void addReference(ThingReference ref) throws VisADException, RemoteException { if (!(ref instanceof DataReference)) { throw new ReferenceException("DisplayImpl.addReference: ref " + "must be DataReference"); } if (displayRenderer == null) return; addReference((DataReference)ref, null); } /** * Replace remote reference with local reference. * * @param rDpy Remote display. * @param ref Local reference which will replace the previous * reference. * * @exception VisADException if there was a problem with one or more * parameters. * @exception RemoteException if there was a problem adding the * data reference to the remote display. * * @see visad.DisplayImpl#addReference(visad.ThingReference) */ public void replaceReference(RemoteDisplay rDpy, ThingReference ref) throws VisADException, RemoteException { if (!(ref instanceof DataReference)) { throw new ReferenceException("DisplayImpl.replaceReference: ref " + "must be DataReference"); } if (displayRenderer == null) return; replaceReference(rDpy, (DataReference)ref, null); } /** * Add a link to a DataReference object * * @param link The link to the DataReference. * * @exception VisADException If referenced data is null * or if a link already exists. * @exception RemoteException If a link could not be made * within the remote display. */ void addLink(DataDisplayLink link) throws VisADException, RemoteException { if (displayRenderer == null) return; addLink(link, true); } /** * Add a link to a DataReference object * * @param link The link to the DataReference. * @param syncRemote <tt>true</tt> if this is not just * a local link. * * @exception VisADException If referenced data is null * or if a link already exists. * @exception RemoteException If a link could not be made * within the remote display. */ private void addLink(DataDisplayLink link, boolean syncRemote) throws VisADException, RemoteException { if (displayRenderer == null) return; // System.out.println("addLink " + getName() + " " + // link.getData().getType()); // IDV super.addLink((ReferenceActionLink)link); if (syncRemote) { notifyListeners( new DisplayReferenceEvent(this, DisplayEvent.REFERENCE_ADDED, link)); } } /** * Link a reference to this Display. * <tt>ref</tt> must be a local * {@link visad.DataReferenceImpl DataReferenceImpl}. * The {@link visad.ConstantMap ConstantMap} array applies only * to the rendering reference. * * @param ref data reference * @param constant_maps array of {@link visad.ConstantMap ConstantMaps} * associated with the data reference * * @exception VisADException if there was a problem with one or more * parameters. * @exception RemoteException if there was a problem adding the * data reference to the remote display. * * @see <a href="http://www.ssec.wisc.edu/~billh/guide.html#6.1">Section 6.1 of the Developer's Guide</a> */ public void addReference(DataReference ref, ConstantMap[] constant_maps) throws VisADException, RemoteException { if (!(ref instanceof DataReferenceImpl)) { throw new RemoteVisADException("DisplayImpl.addReference: requires " + "DataReferenceImpl"); } if (displayRenderer == null) return; if (findReference(ref) != null) { throw new TypeException("DisplayImpl.addReference: link already exists"); } DataRenderer renderer = displayRenderer.makeDefaultRenderer(); DataDisplayLink[] links = {new DataDisplayLink(ref, this, this, constant_maps, renderer, getLinkId())}; addLink(links[0]); renderer.setLinks(links, this); synchronized (mapslock) { RendererVector.addElement(renderer); } initialize |= computeInitialize(); // printStack("addReference"); notifyAction(); } /** * Replace remote reference with local reference. * * @param rDpy Remote display. * @param ref Local reference which will replace the previous * reference. * @param constant_maps array of {@link visad.ConstantMap ConstantMaps} * associated with the data reference * * @exception VisADException if there was a problem with one or more * parameters. * @exception RemoteException if there was a problem adding the * data reference to the remote display. * * @see visad.DisplayImpl#addReference(visad.DataReference, visad.ConstantMap[]) */ public void replaceReference(RemoteDisplay rDpy, DataReference ref, ConstantMap[] constant_maps) throws VisADException, RemoteException { if (displayRenderer == null) return; replaceReferences( rDpy, null, new DataReference[] {ref}, new ConstantMap[][] { constant_maps }); } /** * decide whether an autoscale is needed * * @return */ private boolean computeInitialize() { boolean init = false; for (Iterator iter = ((java.util.List)MapVector.clone()).iterator(); !init && iter.hasNext(); init |= ((ScalarMap)iter.next()).doInitialize()) {} if (!init) { AnimationControl control = (AnimationControl)getControl(AnimationControl.class); if (control != null) { init |= (control.getSet() == null && control.getComputeSet()); } } return init; } /** * Link a RemoteDataReference to this Display. * The {@link visad.ConstantMap ConstantMap} array applies only * to the rendering reference. * For use by addReference() method of RemoteDisplay that adapts this. * * @param ref remote data reference * @param display RemoteDisplay adapting this * @param constant_maps array of {@link visad.ConstantMap ConstantMaps} * associated with the data reference * * @exception VisADException if there was a problem with one or more * parameters. * @exception RemoteException if there was a problem adding the * data reference to the remote display. * * @see <a href="http://www.ssec.wisc.edu/~billh/guide.html#6.1">Section 6.1 of the Developer's Guide</a> */ void adaptedAddReference(RemoteDataReference ref, RemoteDisplay display, ConstantMap[] constant_maps) throws VisADException, RemoteException { if (findReference(ref) != null) { throw new TypeException("DisplayImpl.adaptedAddReference: " + "link already exists"); } if (displayRenderer == null) return; DataRenderer renderer = displayRenderer.makeDefaultRenderer(); DataDisplayLink[] links = {new DataDisplayLink(ref, this, display, constant_maps, renderer, getLinkId())}; addLink(links[0]); renderer.setLinks(links, this); synchronized (mapslock) { RendererVector.addElement(renderer); } initialize |= computeInitialize(); // printStack("adaptedAddReference"); notifyAction(); } /** * Link a reference to this Display using a non-default renderer. * <tt>ref</tt> must be a local * {@link visad.DataReferenceImpl DataReferenceImpl}. * This is a method of {@link visad.DisplayImpl DisplayImpl} and * {@link visad.RemoteDisplayImpl RemoteDisplayImpl} rather * than {@link visad.Display Display} * * @param renderer logic to render this data * @param ref data reference * * @exception VisADException if there was a problem with one or more * parameters. * @exception RemoteException if there was a problem adding the * data reference to the remote display. * * @see <a href="http://www.ssec.wisc.edu/~billh/guide.html#6.1">Section 6.1 of the Developer's Guide</a> */ public void addReferences(DataRenderer renderer, DataReference ref) throws VisADException, RemoteException { addReferences(renderer, new DataReference[] {ref}, null); } /** * Replace remote reference with local reference using * non-default renderer. * * @param rDpy Remote display. * @param renderer logic to render this data * @param ref Local reference which will replace the previous * reference. * * @exception VisADException if there was a problem with one or more * parameters. * @exception RemoteException if there was a problem adding the * data reference to the remote display. * * @see visad.DisplayImpl#addReferences(visad.DataRenderer, visad.DataReference) */ public void replaceReferences(RemoteDisplay rDpy, DataRenderer renderer, DataReference ref) throws VisADException, RemoteException { replaceReferences(rDpy, renderer, new DataReference[] {ref}, null); } /** * Link a reference to this Display using a non-default renderer. * <tt>ref</tt> must be a local * {@link visad.DataReferenceImpl DataReferenceImpl}. * This is a method of {@link visad.DisplayImpl DisplayImpl} and * {@link visad.RemoteDisplayImpl RemoteDisplayImpl} rather * than {@link visad.Display Display} * * @param renderer logic to render this data * @param ref data reference * @param constant_maps array of {@link visad.ConstantMap ConstantMaps} * associated with the data reference * * @exception VisADException if there was a problem with one or more * parameters. * @exception RemoteException if there was a problem adding the * data reference to the remote display. * * @see <a href="http://www.ssec.wisc.edu/~billh/guide.html#6.1">Section 6.1 of the Developer's Guide</a> */ public void addReferences(DataRenderer renderer, DataReference ref, ConstantMap[] constant_maps) throws VisADException, RemoteException { addReferences(renderer, new DataReference[] {ref}, new ConstantMap[][] { constant_maps }); } /** * Replace remote reference with local reference using * non-default renderer. * * @param rDpy Remote display. * @param renderer logic to render this data * @param ref Local reference which will replace the previous * reference. * @param constant_maps array of {@link visad.ConstantMap ConstantMaps} * associated with the data reference * * @exception VisADException if there was a problem with one or more * parameters. * @exception RemoteException if there was a problem adding the * data reference to the remote display. * * @see visad.DisplayImpl#addReferences(visad.DataRenderer, visad.DataReference, visad.ConstantMap[]) */ public void replaceReferences(RemoteDisplay rDpy, DataRenderer renderer, DataReference ref, ConstantMap[] constant_maps) throws VisADException, RemoteException { replaceReferences( rDpy, renderer, new DataReference[] {ref}, new ConstantMap[][] { constant_maps }); } /** * Link references to this display using a non-default renderer. * <tt>refs</tt> must be local * {@link visad.DataReferenceImpl DataReferenceImpls}. * This is a method of {@link visad.DisplayImpl DisplayImpl} and * {@link visad.RemoteDisplayImpl RemoteDisplayImpl} rather * than {@link visad.Display Display} * * @param renderer logic to render this data * @param refs array of data references * * @exception VisADException if there was a problem with one or more * parameters. * @exception RemoteException if there was a problem adding the * data references to the remote display. * * @see <a href="http://www.ssec.wisc.edu/~billh/guide.html#6.1">Section 6.1 of the Developer's Guide</a> */ public void addReferences(DataRenderer renderer, DataReference[] refs) throws VisADException, RemoteException { addReferences(renderer, refs, null); } /** * Replace remote references with local references. * * @param rDpy Remote display. * @param renderer logic to render this data * @param refs array of local data references * * @exception VisADException if there was a problem with one or more * parameters. * @exception RemoteException if there was a problem adding the * data references to the remote display. * * @see visad.DisplayImpl#addReferences(visad.DataRenderer, visad.DataReference[]) */ public void replaceReferences(RemoteDisplay rDpy, DataRenderer renderer, DataReference[] refs) throws VisADException, RemoteException { replaceReferences(rDpy, renderer, refs, null); } /** * Link references to this display using the non-default renderer. * <tt>refs</tt> must be local * {@link visad.DataReferenceImpl DataReferenceImpls}. * The <tt>maps[i]</tt> array applies only to rendering <tt>refs[i]</tt>. * This is a method of {@link visad.DisplayImpl DisplayImpl} and * {@link visad.RemoteDisplayImpl RemoteDisplayImpl} rather * than {@link visad.Display Display} * * @param renderer logic to render this data * @param refs array of data references * @param constant_maps array of {@link visad.ConstantMap ConstantMaps} * associated with data references * * @exception VisADException if there was a problem with one or more * parameters. * @exception RemoteException if there was a problem adding the * data references to the remote display. * * @see <a href="http://www.ssec.wisc.edu/~billh/guide.html#6.1">Section 6.1 of the Developer's Guide</a> */ public void addReferences(DataRenderer renderer, DataReference[] refs, ConstantMap[][] constant_maps) throws VisADException, RemoteException { addReferences(renderer, refs, constant_maps, true); } /** * Link references to this display using the non-default renderer. * <tt>refs</tt> must be local * {@link visad.DataReferenceImpl DataReferenceImpls}. * The <tt>maps[i]</tt> array applies only to rendering <tt>refs[i]</tt>. * This is a method of {@link visad.DisplayImpl DisplayImpl} and * {@link visad.RemoteDisplayImpl RemoteDisplayImpl} rather * than {@link visad.Display Display} * * @param renderer logic to render this data * @param refs array of data references * @param constant_maps array of {@link visad.ConstantMap ConstantMaps} * associated with data references. * @param syncRemote <tt>true</tt> if this data should be forwarded * to the remote display. * * @exception VisADException if there was a problem with one or more * parameters * @exception RemoteException if there was a problem adding the * data references to the remote display. * * @see <a href="http://www.ssec.wisc.edu/~billh/guide.html#6.1">Section 6.1 of the Developer's Guide</a> */ private void addReferences(DataRenderer renderer, DataReference[] refs, ConstantMap[][] constant_maps, boolean syncRemote) throws VisADException, RemoteException { if (displayRenderer == null) return; // N.B. This method is called by all replaceReference() methods if (refs.length < 1) { throw new DisplayException("DisplayImpl.addReferences: must have at " + "least one DataReference"); } if (constant_maps != null && refs.length != constant_maps.length) { throw new DisplayException("DisplayImpl.addReferences: constant_maps " + "length must match refs length"); } if (!displayRenderer.legalDataRenderer(renderer)) { throw new DisplayException("DisplayImpl.addReferences: illegal " + "DataRenderer class"); } DataDisplayLink[] links = new DataDisplayLink[refs.length]; for (int i = 0; i < refs.length; i++) { if (!(refs[i] instanceof DataReferenceImpl)) { throw new RemoteVisADException("DisplayImpl.addReferences: requires " + "DataReferenceImpl"); } if (findReference(refs[i]) != null) { throw new TypeException("DisplayImpl.addReferences: link already exists"); } if (constant_maps == null) { links[i] = new DataDisplayLink(refs[i], this, this, null, renderer, getLinkId()); } else { links[i] = new DataDisplayLink(refs[i], this, this, constant_maps[i], renderer, getLinkId()); } addLink(links[i], syncRemote); } renderer.setLinks(links, this); synchronized (mapslock) { RendererVector.addElement(renderer); } initialize |= computeInitialize(); // printStack("addReferences"); notifyAction(); } /** * Replace remote references with local references. * * @param rDpy Remote display. * @param renderer logic to render this data * @param refs array of data references * @param constant_maps array of {@link visad.ConstantMap ConstantMaps} * associated with data references. * * @exception VisADException if there was a problem with one or more * parameters. * @exception RemoteException if there was a problem adding the * data references to the remote display. * * @see visad.DisplayImpl#addReferences(visad.DataRenderer, visad.DataReference[], visad.ConstantMap[][]) */ public void replaceReferences(RemoteDisplay rDpy, DataRenderer renderer, DataReference[] refs, ConstantMap[][] constant_maps) throws VisADException, RemoteException { if (displayRenderer == null) return; if (renderer == null) { renderer = displayRenderer.makeDefaultRenderer(); } removeAllReferences(); addReferences(renderer, refs, constant_maps, false); copyRefLinks(rDpy, refs); } /** * Link references to this display using the non-default renderer. * <tt>refs</tt> may be a mix of local * {@link visad.DataReferenceImpl DataReferenceImpls} and * {@link visad.RemoteDataReference RemoteDataReferences}. * The <tt>maps[i]</tt> array applies only to rendering <tt>refs[i]</tt>. * For use by addReferences() method of RemoteDisplay that adapts this. * * @param renderer logic to render this data * @param refs array of data references * @param display RemoteDisplay adapting this * @param constant_maps array of {@link visad.ConstantMap ConstantMaps} * associated with data references. * * @exception VisADException if there was a problem with one or more * parameters * @exception RemoteException if there was a problem adding the * data references to the remote display. * * @see <a href="http://www.ssec.wisc.edu/~billh/guide.html#6.1">Section 6.1 of the Developer's Guide</a> */ void adaptedAddReferences(DataRenderer renderer, DataReference[] refs, RemoteDisplay display, ConstantMap[][] constant_maps) throws VisADException, RemoteException { if (displayRenderer == null) return; if (refs.length < 1) { throw new DisplayException("DisplayImpl.addReferences: must have at " + "least one DataReference"); } if (constant_maps != null && refs.length != constant_maps.length) { throw new DisplayException("DisplayImpl.addReferences: constant_maps " + "length must match refs length"); } if (!displayRenderer.legalDataRenderer(renderer)) { throw new DisplayException("DisplayImpl.addReferences: illegal " + "DataRenderer class"); } DataDisplayLink[] links = new DataDisplayLink[refs.length]; for (int i = 0; i < refs.length; i++) { if (findReference(refs[i]) != null) { throw new TypeException("DisplayImpl.addReferences: link already exists"); } if (refs[i] instanceof DataReferenceImpl) { // refs[i] is local if (constant_maps == null) { links[i] = new DataDisplayLink(refs[i], this, this, null, renderer, getLinkId()); } else { links[i] = new DataDisplayLink(refs[i], this, this, constant_maps[i], renderer, getLinkId()); } } else { // refs[i] is remote if (constant_maps == null) { links[i] = new DataDisplayLink(refs[i], this, display, null, renderer, getLinkId()); } else { links[i] = new DataDisplayLink(refs[i], this, display, constant_maps[i], renderer, getLinkId()); } } addLink(links[i]); } renderer.setLinks(links, this); synchronized (mapslock) { RendererVector.addElement(renderer); } initialize |= computeInitialize(); // printStack("adaptedAddReferences"); notifyAction(); } /** * remove link to ref, which must be a local DataReferenceImpl; * if ref was added as part of a DataReference array passed to * addReferences(), remove links to all of them * @param ref ThingReference to remove * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public void removeReference(ThingReference ref) throws VisADException, RemoteException { if (!(ref instanceof DataReferenceImpl)) { throw new RemoteVisADException("ActionImpl.removeReference: requires " + "DataReferenceImpl"); } adaptedDisplayRemoveReference((DataReference)ref); notifyListeners(new DisplayEvent(this, DisplayEvent.REFERENCE_REMOVED)); } /** * remove DataDisplayLinks from this DisplayImpl * @param links array of DataDisplayLinks to remove * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ void removeLinks(DataDisplayLink[] links) throws RemoteException, VisADException { if (displayRenderer == null) return; for (int i = links.length - 1; i >= 0; i--) { if (links[i] != null) { links[i].clearMaps(); } } super.removeLinks(links); } /** * remove link to a DataReference; * uses by removeReference() method of RemoteActionImpl that * adapts this ActionImpl; * because DataReference array input to adaptedAddReferences * may be a mix of local and remote, we tolerate either here * @param ref DataReference to remove * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ void adaptedDisplayRemoveReference(DataReference ref) throws VisADException, RemoteException { if (displayRenderer == null) return; DataDisplayLink link = (DataDisplayLink)findReference(ref); // don't throw an Exception if link is null: users may try to // remove all DataReferences added by a call to addReferences if (link == null) return; DataRenderer renderer = link.getRenderer(); DataDisplayLink[] links = renderer.getLinks(); synchronized (mapslock) { renderer.clearAVControls(); renderer.clearScene(); RendererVector.removeElement(renderer); } removeLinks(links); } /** * remove all links to DataReferences. * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public void removeAllReferences() throws VisADException, RemoteException { if (displayRenderer == null) return; Vector temp = (Vector)RendererVector.clone(); synchronized (mapslock) { Iterator renderers = temp.iterator(); while(renderers.hasNext()) { DataRenderer renderer = (DataRenderer)renderers.next(); renderer.clearAVControls(); DataDisplayLink[] links = renderer.getLinks(); renderers.remove(); removeLinks(links); renderer.clearScene(); } RendererVector.removeAllElements(); initialize = true; // printStack("removeAllReferences"); notifyListeners(new DisplayEvent(this, DisplayEvent.REFERENCE_REMOVED)); } } /** * trigger possible re-transform of linked Data * used by Controls to notify this DisplayImpl that they * have changed */ public void controlChanged() { notifyAction(); } /** * over-ride ActionImpl.checkTicks() to always return true, * since DisplayImpl always runs doAction to find out if any * linked Data needs to be re-transformed * @return true */ public boolean checkTicks() { return true; } /** * has this display been destroyed * * @return has this display been destroyed */ public boolean isDestroyed() { return destroyed; } /** * destroy this display: clear all references to objects * (so they can be garbage collected), stop all Threads * and remove all links * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public void destroy() throws VisADException, RemoteException { if (destroyed) return; destroyed = true; VisADException thrownVE = null; RemoteException thrownRE = null; if (mapslock == null) mapslock = new Object(); synchronized (mapslock) { stop(); if (displayActivity != null) { displayActivity.destroy(); } // tell everybody we're going away notifyListeners(new DisplayEvent(this, DisplayEvent.DESTROYED)); // remove all listeners synchronized (ListenerVector) { ListenerVector.removeAllElements(); } try { removeAllReferences(); } catch (RemoteException re) { thrownRE = re; } catch (VisADException ve) { thrownVE = ve; } try { clearMaps(); } catch (RemoteException re) { thrownRE = re; } catch (VisADException ve) { thrownVE = ve; } AnimationControl control = (AnimationControl)getControl(AnimationControl.class); if (control != null) { control.stop(); } if (thrownVE != null) { throw thrownVE; } if (thrownRE != null) { throw thrownRE; } // get rid of dangling references /* done in clearMaps() verify (RendererVector == null) MapVector.removeAllElements(); ConstantMapVector.removeAllElements(); RealTypeVector.removeAllElements(); */ DisplayRealTypeVector.removeAllElements(); ControlVector.removeAllElements(); RendererSourceListeners.removeAllElements(); RmtSrcListeners.removeAllElements(); MessageListeners.removeAllElements(); ListenerVector.removeAllElements(); Slaves.removeAllElements(); displayRenderer = null; // this disables most DisplayImpl methods if (component != null) { component.removeComponentListener(componentListener); } componentListener = null; component = null; mouse = null; displayMonitor = null; displaySync = null; displayActivity = null; printer = null; rd = null; widgetPanel = null; } // end synchronized (mapslock) } /** * Check if any Data need re-transform, and if so, do it. * Check if auto-scaling is needed for any ScalarMaps, and * if so, do it. This method does the real work of DisplayImpl. * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public void doAction() throws VisADException, RemoteException { if (displayRenderer == null) return; if (mapslock == null) return; // put a try/finally block around the setWaitFlag(true), so that we unset // the flag before exiting even if an Exception or Error is thrown try { // System.out.println("DisplayImpl call setWaitFlag(true)"); displayRenderer.setWaitFlag(true); synchronized (mapslock) { if (RendererVector == null || displayRenderer == null) { // System.out.println("DisplayImpl call setWaitFlag(false)"); if (displayRenderer != null) displayRenderer.setWaitFlag(false); return; } // set tickFlag-s in changed Control-s // clone MapVector to avoid need for synchronized access Vector tmap = (Vector)MapVector.clone(); Enumeration maps = tmap.elements(); while(maps.hasMoreElements()) { ScalarMap map = (ScalarMap)maps.nextElement(); map.setTicks(); } // set ScalarMap.valueIndex-s and valueArrayLength int n = getDisplayScalarCount(); int[] scalarToValue = new int[n]; for (int i = 0; i < n; i++) scalarToValue[i] = -1; valueArrayLength = 0; maps = tmap.elements(); while(maps.hasMoreElements()) { ScalarMap map = ((ScalarMap)maps.nextElement()); DisplayRealType dreal = map.getDisplayScalar(); map.setValueIndex(valueArrayLength); valueArrayLength++; } // set valueToScalar and valueToMap arrays valueToScalar = new int[valueArrayLength]; valueToMap = new int[valueArrayLength]; for (int i = 0; i < tmap.size(); i++) { ScalarMap map = (ScalarMap)tmap.elementAt(i); DisplayRealType dreal = map.getDisplayScalar(); valueToScalar[map.getValueIndex()] = getDisplayScalarIndex(dreal); valueToMap[map.getValueIndex()] = i; } // invoke each DataRenderer (to prepare associated Data objects // for transformation) // clone RendererVector to avoid need for synchronized access Vector temp = ((Vector)RendererVector.clone()); Enumeration renderers = temp.elements(); boolean go = false; if (initialize) { renderers = temp.elements(); while(!go && renderers.hasMoreElements()) { DataRenderer renderer = (DataRenderer)renderers.nextElement(); go |= renderer.checkAction(); } } /* System.out.println("initialize = " + initialize + " go = " + go + " redisplay_all = " + redisplay_all); */ if (redisplay_all) { go = true; // System.out.println("redisplay_all = " + redisplay_all + " go = " + go); redisplay_all = false; } if (!initialize || go) { boolean lastinitialize = initialize; displayRenderer.prepareAction(temp, tmap, go, initialize); // WLH 10 May 2001 boolean anyBadMap = false; maps = tmap.elements(); while(maps.hasMoreElements()) { ScalarMap map = ((ScalarMap)maps.nextElement()); if (map.badRange()) { anyBadMap = true; // System.out.println("badRange " + map); } } renderers = temp.elements(); boolean badScale = false; while(renderers.hasMoreElements()) { DataRenderer renderer = (DataRenderer)renderers.nextElement(); boolean badthis = renderer.getBadScale(anyBadMap); badScale |= badthis; /* if (badthis) { DataDisplayLink[] links = renderer.getLinks(); System.out.println("badthis " + links[0].getThingReference().getName()); } */ } initialize = badScale; if (always_initialize) initialize = true; if (initialize && !lastinitialize) { displayRenderer.prepareAction(temp, tmap, go, initialize); } boolean transform_done = false; // System.out.println("DisplayImpl.doAction transform"); // int i = 0; boolean any_exceptions = false; renderers = temp.elements(); while(renderers.hasMoreElements()) { // System.out.println("DisplayImpl invoke renderer.doAction " + i); // i++; DataRenderer renderer = (DataRenderer)renderers.nextElement(); boolean this_transform = renderer.doAction(); transform_done |= this_transform; any_exceptions |= !renderer.getExceptionVector().isEmpty(); /* if (this_transform) { DataDisplayLink[] links = renderer.getLinks(); System.out.println("transform " + getName() + " " + links[0].getThingReference().getName()); } */ } if (transform_done) { // System.out.println(getName() + " invoked " + i + " renderers"); AnimationControl control = (AnimationControl)getControl(AnimationControl.class); if (control != null) { control.init(); } synchronized (ControlVector) { Enumeration controls = ControlVector.elements(); while(controls.hasMoreElements()) { Control cont = (Control)controls.nextElement(); if (ValueControl.class.isInstance(cont)) { ((ValueControl)cont).init(); } } } } if (transform_done || any_exceptions) { notifyListeners(DisplayEvent.TRANSFORM_DONE, 0, 0); } } // clear tickFlag-s in Control-s maps = tmap.elements(); while(maps.hasMoreElements()) { ScalarMap map = (ScalarMap)maps.nextElement(); map.resetTicks(); } } // end synchronized (mapslock) } finally { // System.out.println("DisplayImpl call setWaitFlag(false)"); if (displayRenderer != null) displayRenderer.setWaitFlag(false); } } /** * @return the default DisplayRenderer for this DisplayImpl */ protected abstract DisplayRenderer getDefaultDisplayRenderer(); /** * @return the DisplayRenderer associated with this DisplayImpl */ public DisplayRenderer getDisplayRenderer() { return displayRenderer; } /** * Returns a clone of the list of DataRenderer-s. A clone is returned * to avoid concurrent access problems by the Display thread. * @return A clone of the list of DataRenderer-s. * @see #getRenderers() */ public Vector getRendererVector() { return (Vector)RendererVector.clone(); } /** * @return the number of DisplayRealTypes in ScalarMaps * linked to this DisplayImpl */ public int getDisplayScalarCount() { return DisplayRealTypeVector.size(); } /** * get the DisplayRealType with the given index * @param index index into Vector of DisplayRealTypes * @return the indexed DisplayRealType */ public DisplayRealType getDisplayScalar(int index) { return (DisplayRealType)DisplayRealTypeVector.elementAt(index); } /** * get the index for the given DisplayRealType * @param dreal DisplayRealType to search for * @return the index of dreal in Vector of DisplayRealTypes */ public int getDisplayScalarIndex(DisplayRealType dreal) { int dindex; synchronized (DisplayRealTypeVector) { DisplayTupleType tuple = dreal.getTuple(); if (tuple != null) { int n = tuple.getDimension(); for (int i = 0; i < n; i++) { try { DisplayRealType ereal = (DisplayRealType)tuple.getComponent(i); int eindex = DisplayRealTypeVector.indexOf(ereal); if (eindex < 0) { DisplayRealTypeVector.addElement(ereal); } } catch (VisADException e) { } } } dindex = DisplayRealTypeVector.indexOf(dreal); if (dindex < 0) { DisplayRealTypeVector.addElement(dreal); dindex = DisplayRealTypeVector.indexOf(dreal); } } return dindex; } /** * @return the number of ScalarTypes in ScalarMaps * linked to this DisplayImpl */ public int getScalarCount() { return RealTypeVector.size(); } /** * get the ScalarType with the given index * @param index index into Vector of ScalarTypes * @return the indexed ScalarType */ public ScalarType getScalar(int index) { return (ScalarType)RealTypeVector.elementAt(index); } /** * get the index for the given ScalarType * @param real ScalarType to search for * @return the index of real in Vector of ScalarTypes * @throws RemoteException an RMI error occurred */ public int getScalarIndex(ScalarType real) throws RemoteException { return RealTypeVector.indexOf(real); } /** * add a ScalarMap to this Display, assuming a local source * @param map ScalarMap to add * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public void addMap(ScalarMap map) throws VisADException, RemoteException { addMap(map, VisADEvent.LOCAL_SOURCE); } /** * add a ScalarMap to this Display * @param map ScalarMap to add * @param remoteId remote source for collab, or VisADEvent.LOCAL_SOURCE * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public void addMap(ScalarMap map, int remoteId) throws VisADException, RemoteException { if (displayRenderer == null) return; synchronized (mapslock) { int index; if (!RendererVector.isEmpty()) { ScalarType st = map.getScalar(); if (st != null) { Vector temp = (Vector)RendererVector.clone(); Iterator renderers = temp.iterator(); while(renderers.hasNext()) { DataRenderer renderer = (DataRenderer)renderers.next(); DataDisplayLink[] links = renderer.getLinks(); for (int i = 0; i < links.length; i++) { if (MathType.findScalarType(links[i].getType(), st)) { /* WLH relax addMap() & clearMap() 17 Dec 2002 throw new DisplayException("DisplayImpl.addMap(): " + "ScalarType may not occur in any DataReference"); */ DataReference ref = links[i].getDataReference(); if (ref != null) ref.incTick(); } } } } } DisplayRealType type = map.getDisplayScalar(); if (!displayRenderer.legalDisplayScalar(type)) { throw new BadMappingException("DisplayImpl.addMap: " + map.getDisplayScalar() + " illegal for this DisplayRenderer"); } if ((Display.LineWidth.equals(type) || Display.PointSize.equals(type) || Display.PointMode.equals(type) || Display.LineStyle.equals(type) || Display.TextureEnable.equals(type) || Display.MissingTransparent.equals(type) || Display.PolygonMode.equals(type) || Display.CurvedSize.equals(type) || Display.ColorMode.equals(type) || Display.PolygonOffset.equals(type) || Display.PolygonOffsetFactor.equals(type) || Display.AdjustProjectionSeam.equals(type) || Display.Texture3DMode.equals(type) || Display.CacheAppearances.equals(type) || Display.MergeGeometries.equals(type)) && !(map instanceof ConstantMap)) { throw new BadMappingException("DisplayImpl.addMap: " + map.getDisplayScalar() + " for ConstantMap only"); } // System.out.println("addMap " + getName() + " " + map.getScalar() + // " -> " + map.getDisplayScalar()); // IDV map.setDisplay(this); if (map instanceof ConstantMap) { synchronized (ConstantMapVector) { Enumeration maps = ConstantMapVector.elements(); while(maps.hasMoreElements()) { ConstantMap map2 = (ConstantMap)maps.nextElement(); if (map2.getDisplayScalar().equals(map.getDisplayScalar())) { throw new BadMappingException("Display.addMap: two ConstantMaps " + "have the same DisplayScalar"); } } ConstantMapVector.addElement(map); } if (!RendererVector.isEmpty()) { reDisplayAll(); // WLH 2 April 2002 } } else { // !(map instanceof ConstantMap) // add to RealTypeVector and set ScalarIndex ScalarType real = map.getScalar(); DisplayRealType dreal = map.getDisplayScalar(); synchronized (MapVector) { Enumeration maps = MapVector.elements(); while(maps.hasMoreElements()) { ScalarMap map2 = (ScalarMap)maps.nextElement(); if (real.equals(map2.getScalar()) && dreal.equals(map2.getDisplayScalar()) && !dreal.equals(Display.Shape)) { throw new BadMappingException("Display.addMap: two ScalarMaps " + "with the same RealType & DisplayRealType"); } if (dreal.equals(Display.Animation) && map2.getDisplayScalar().equals(Display.Animation)) { throw new BadMappingException("Display.addMap: two RealTypes " + "are mapped to Animation"); } } MapVector.addElement(map); needWidgetRefresh = true; } synchronized (RealTypeVector) { index = RealTypeVector.indexOf(real); if (index < 0) { RealTypeVector.addElement(real); index = RealTypeVector.indexOf(real); } } map.setScalarIndex(index); map.setControl(); // WLH 18 June 2002 if (!RendererVector.isEmpty() && map.doInitialize()) { reAutoScale(); } } // end !(map instanceof ConstantMap) addDisplayScalar(map); notifyListeners( new DisplayMapEvent(this, DisplayEvent.MAP_ADDED, map, remoteId)); // make sure we monitor all changes to this ScalarMap map.addScalarMapListener(displayMonitor); } } /** * remove a ScalarMap from this Display, assuming a local source * @param map ScalarMap to remove * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public void removeMap(ScalarMap map) throws VisADException, RemoteException { removeMap(map, VisADEvent.LOCAL_SOURCE); } /** * remove a ScalarMap from this Display * @param map ScalarMap to add * @param remoteId remote source for collab, or VisADEvent.LOCAL_SOURCE * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public void removeMap(ScalarMap map, int remoteId) throws VisADException, RemoteException { if (displayRenderer == null) return; // System.out.println("removeMap " + getName() + " " + map.getScalar() + // " -> " + map.getDisplayScalar()); // IDV synchronized (mapslock) { // can have multiple equals() maps to Shape, so test for == int index = MapVector.indexOf(map); while(index >= 0 && map != MapVector.elementAt(index)) { index = MapVector.indexOf(map, index + 1); } if (index < 0) { throw new BadMappingException("Display.removeMap: " + map + " not " + "in Display " + getName()); } //Remove the control from the ControlVector Control control = map.getControl(); synchronized (ControlVector) { if (control != null && ControlVector.contains(control)) { ControlVector.remove(control); control.removeControlListener((ControlListener)displayMonitor); control.setInstanceNumber(-1); control.setIndex(-1); for (int i = 0; i < ControlVector.size(); i++) { Control ctl = (Control)ControlVector.get(i); ctl.setIndex(i); } } } MapVector.removeElementAt(index); ScalarType real = map.getScalar(); if (real != null) { Enumeration maps = MapVector.elements(); boolean any = false; while(maps.hasMoreElements()) { ScalarMap map2 = (ScalarMap)maps.nextElement(); if (real.equals(map2.getScalar())) any = true; } if (!any) { // if real is not used by any other ScalarMap, remove it // and adjust ScalarIndex of all other ScalarMaps RealTypeVector.removeElement(real); maps = MapVector.elements(); while(maps.hasMoreElements()) { ScalarMap map2 = (ScalarMap)maps.nextElement(); ScalarType real2 = map2.getScalar(); int index2 = RealTypeVector.indexOf(real2); if (index2 < 0) { throw new BadMappingException("Display.removeMap: impossible 1"); } map2.setScalarIndex(index2); } } // end if (!any) } // end if (real != null) // trigger events if (map instanceof ConstantMap) { if (!RendererVector.isEmpty()) { reDisplayAll(); } } else { // !(map instanceof ConstantMap) if (!RendererVector.isEmpty()) { ScalarType st = map.getScalar(); if (st != null) { // not necessary for !(map instanceof ConstantMap) Vector temp = (Vector)RendererVector.clone(); Iterator renderers = temp.iterator(); while(renderers.hasNext()) { DataRenderer renderer = (DataRenderer)renderers.next(); DataDisplayLink[] links = renderer.getLinks(); for (int i = 0; i < links.length; i++) { if (MathType.findScalarType(links[i].getType(), st)) { DataReference ref = links[i].getDataReference(); if (ref != null) ref.incTick(); } } } } } // add DRM 2003-02-21 if (map.getAxisScale() != null) { DisplayRenderer displayRenderer = getDisplayRenderer(); displayRenderer.clearScale(map.getAxisScale()); Enumeration maps = MapVector.elements(); while(maps.hasMoreElements()) { ScalarMap map2 = (ScalarMap)maps.nextElement(); AxisScale axisScale = map2.getAxisScale(); if (axisScale != null) { displayRenderer.clearScale(axisScale); axisScale.setAxisOrdinal(-1); } } maps = MapVector.elements(); while(maps.hasMoreElements()) { ScalarMap map2 = (ScalarMap)maps.nextElement(); AxisScale axisScale = map2.getAxisScale(); if (axisScale != null) { map2.makeScale(); } } } needWidgetRefresh = true; } // end !(map instanceof ConstantMap) notifyListeners( new DisplayMapEvent(this, DisplayEvent.MAP_REMOVED, map, remoteId)); map.nullDisplay(); // ?? } // end synchronized (mapslock) } /** * add a ScalarType from a ScalarMap from this Display * @param map ScalarMap whose ScalarType to add */ void addDisplayScalar(ScalarMap map) { int index; if (displayRenderer == null) return; DisplayRealType dreal = map.getDisplayScalar(); synchronized (DisplayRealTypeVector) { DisplayTupleType tuple = dreal.getTuple(); if (tuple != null) { int n = tuple.getDimension(); for (int i = 0; i < n; i++) { try { DisplayRealType ereal = (DisplayRealType)tuple.getComponent(i); int eindex = DisplayRealTypeVector.indexOf(ereal); if (eindex < 0) { DisplayRealTypeVector.addElement(ereal); } } catch (VisADException e) { } } } index = DisplayRealTypeVector.indexOf(dreal); if (index < 0) { DisplayRealTypeVector.addElement(dreal); index = DisplayRealTypeVector.indexOf(dreal); } } map.setDisplayScalarIndex(index); } /** * remove all ScalarMaps linked this display; * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ public void clearMaps() throws VisADException, RemoteException { if (displayRenderer == null) return; // System.out.println("clearMaps " + getName() + "\n"); // IDV synchronized (mapslock) { if (!RendererVector.isEmpty()) { /* WLH relax addMap() & clearMap() 17 Dec 2002 throw new DisplayException("DisplayImpl.clearMaps: RendererVector " + "must be empty"); */ reDisplayAll(); } Enumeration maps; synchronized (MapVector) { maps = MapVector.elements(); while(maps.hasMoreElements()) { ScalarMap map = (ScalarMap)maps.nextElement(); map.nullDisplay(); map.removeScalarMapListener(displayMonitor); } MapVector.removeAllElements(); needWidgetRefresh = true; } synchronized (ConstantMapVector) { maps = ConstantMapVector.elements(); while(maps.hasMoreElements()) { ConstantMap map = (ConstantMap)maps.nextElement(); map.nullDisplay(); map.removeScalarMapListener(displayMonitor); } ConstantMapVector.removeAllElements(); } synchronized (ControlVector) { // clear Control-s associated with this Display maps = ControlVector.elements(); while(maps.hasMoreElements()) { Control ctl = (Control)maps.nextElement(); ctl.removeControlListener((ControlListener)displayMonitor); ctl.setInstanceNumber(-1); } ControlVector.removeAllElements(); // one each GraphicsModeControl and ProjectionControl always exists Control control = (Control)getGraphicsModeControl(); if (control != null) addControl(control); control = (Control)getProjectionControl(); if (control != null) addControl(control); // don't forget RendererControl control = (Control)displayRenderer.getRendererControl(); if (control != null) addControl(control); } // clear RealType-s from RealTypeVector // removeAllElements is synchronized RealTypeVector.removeAllElements(); synchronized (DisplayRealTypeVector) { // clear DisplayRealType-s from DisplayRealTypeVector DisplayRealTypeVector.removeAllElements(); // put system intrinsic DisplayRealType-s in DisplayRealTypeVector for (int i = 0; i < DisplayRealArray.length; i++) { DisplayRealTypeVector.addElement(DisplayRealArray[i]); } } displayRenderer.clearAxisOrdinals(); displayRenderer.setAnimationString(new String[] {null, null}); } notifyListeners(new DisplayEvent(this, DisplayEvent.MAPS_CLEARED)); } /** * @return clone of Vector of ScalarMaps linked to this DisplayImpl * (doesn't include ConstantMaps) */ public Vector getMapVector() { return (Vector)MapVector.clone(); } /** * @return clone of Vector of ConstantMaps linked to this DisplayImpl */ public Vector getConstantMapVector() { return (Vector)ConstantMapVector.clone(); } /** * Get the instance number of this <CODE>Control</CODE> * in the internal <CODE>ControlVector</CODE>. * * @param ctl <CODE>Control</CODE> to look for. * * @return Instance number (<CODE>-1</CODE> if not found.) */ private int getInstanceNumber(Control ctl) { Class ctlClass = ctl.getClass(); int num = 0; Enumeration en = ControlVector.elements(); while(en.hasMoreElements()) { Control c = (Control)en.nextElement(); if (ctlClass.isInstance(c)) { if (ctl == c) { return num; } num++; } } return -1; } /** * Return the ID used to identify the collaborative connection to * the specified remote display.<br> * <br> * <b>WARNING!</b> Due to limitations in the Java RMI implementation, * this only works with an exact copy of the RemoteDisplay used to * create the collaboration link. * * @param rmtDpy the specified remote display. * @return <tt>DisplayMonitor.UNKNOWN_LISTENER_ID</tt> if not found; * otherwise, returns the ID. * @throws RemoteException an RMI error occurred */ public int getConnectionID(RemoteDisplay rmtDpy) throws RemoteException { if (displayMonitor == null) return DisplayMonitor.UNKNOWN_LISTENER_ID; return displayMonitor.getConnectionID(rmtDpy); } /** * add a Control to this DisplayImpl * @param control Control to add */ public void addControl(Control control) { if (displayRenderer == null) return; if (control != null && !ControlVector.contains(control)) { ControlVector.addElement(control); control.setIndex(ControlVector.indexOf(control)); control.setInstanceNumber(getInstanceNumber(control)); control.addControlListener((ControlListener)displayMonitor); } } /** * get a linked Control with the given Class; * only called for Control objects associated with 'single' * DisplayRealTypes * @param c sub-Class of Control to search for * @return linked Control with Class c, or null */ public Control getControl(Class c) { return getControl(c, 0); } /** * get ordinal instance of linked Control object of the * specified class * @param c sub-Class of Control to search for * @param inst ordinal instance number * @return linked Control with Class c, or null */ public Control getControl(Class c, int inst) { return getControls(c, null, inst); } /** * get all linked Control objects of the specified Class * @param c sub-Class of Control to search for * @return Vector of linked Controls with Class c */ public Vector getControls(Class c) { Vector v = new Vector(); getControls(c, v, -1); return v; } /** * Internal method which does the bulk of the work for both * <CODE>getControl()</CODE> and <CODE>getControls()</CODE>. * If <CODE>v</CODE> is non-null, adds all <CODE>Control</CODE>s * of the specified <CODE>Class</CODE> to that <CODE>Vector</CODE>. * Otherwise, returns <CODE>inst</CODE> instance of the * <CODE>Control</CODE> matching the specified <CODE>Class</CODE>, * or <CODE>null</CODE> if no <CODE>Control</CODE> matching the * criteria is found. * * @param ctlClass * @param v * @param inst * * @return */ private Control getControls(Class ctlClass, Vector v, int inst) { if (displayRenderer == null) return null; if (ctlClass == null) { return null; } GraphicsModeControl gmc = getGraphicsModeControl(); if (ctlClass.isInstance(gmc)) { if (v == null) { return gmc; } v.addElement(gmc); } else { synchronized (ControlVector) { Enumeration en = ControlVector.elements(); while(en.hasMoreElements()) { Control c = (Control)en.nextElement(); if (ctlClass.isInstance(c)) { if (v != null) { v.addElement(c); } else if (c.getInstanceNumber() == inst) { return c; } } } } } return null; } /** * @return the total number of controls used by this display */ public int getNumberOfControls() { return ControlVector.size(); } /** * @return clone of Vector of Controls linked to this DisplayImpl * @deprecated - DisplayImpl shouldn't expose itself at this level */ public Vector getControlVector() { return (Vector)ControlVector.clone(); } /** whether the Control widget panel needs to be reconstructed */ private boolean needWidgetRefresh = true; /** this Display's associated panel of Control widgets */ private JPanel widgetPanel = null; /** * get a GUI component containing this Display's Control widgets; * create the widgets as necessary * @return Container of widget panel */ public Container getWidgetPanel() { if (displayRenderer == null) return null; if (needWidgetRefresh) { synchronized (MapVector) { // construct widget panel if needed if (widgetPanel == null) { widgetPanel = new JPanel(); widgetPanel.setLayout(new BoxLayout(widgetPanel, BoxLayout.Y_AXIS)); } else widgetPanel.removeAll(); if (getLinks().size() > 0) { // GraphicsModeControl widget GMCWidget gmcw = new GMCWidget(getGraphicsModeControl()); addToWidgetPanel(gmcw, false); } for (int i = 0; i < MapVector.size(); i++) { ScalarMap sm = (ScalarMap)MapVector.elementAt(i); DisplayRealType drt = sm.getDisplayScalar(); try { double[] a = new double[2]; double[] b = new double[2]; double[] c = new double[2]; boolean scale = sm.getScale(a, b, c); if (scale) { // ScalarMap range widget RangeWidget rw = new RangeWidget(sm); addToWidgetPanel(rw, true); } } catch (VisADException exc) { } try { if (drt.equals(Display.RGB) || drt.equals(Display.RGBA)) { // ColorControl widget try { LabeledColorWidget lw = new LabeledColorWidget(sm); addToWidgetPanel(lw, true); } catch (VisADException exc) { } catch (RemoteException exc) { } } else if (drt.equals(Display.SelectValue)) { // ValueControl widget VisADSlider vs = new VisADSlider(sm); vs.setAlignmentX(JPanel.CENTER_ALIGNMENT); addToWidgetPanel(vs, true); } else if (drt.equals(Display.SelectRange)) { // RangeControl widget SelectRangeWidget srw = new SelectRangeWidget(sm); addToWidgetPanel(srw, true); } else if (drt.equals(Display.IsoContour)) { // ContourControl widget ContourWidget cw = new ContourWidget(sm); addToWidgetPanel(cw, true); } else if (drt.equals(Display.Animation)) { // AnimationControl widget AnimationWidget aw = new AnimationWidget(sm); addToWidgetPanel(aw, true); } } catch (VisADException exc) { } catch (RemoteException exc) { } } } needWidgetRefresh = false; } return widgetPanel; } /** * add a component to the widget panel * * @param c * @param divide */ private void addToWidgetPanel(Component c, boolean divide) { if (displayRenderer == null) return; if (divide) widgetPanel.add(new Divider()); widgetPanel.add(c); } /** * @return length of valueArray passed to ShadowType.doTransform() */ public int getValueArrayLength() { return valueArrayLength; } /** * @return int[] array mapping from valueArray indices to * ScalarType Vector indices */ public int[] getValueToScalar() { return valueToScalar; } /** * @return int[] array mapping from valueArray indices to * ScalarMap Vector indices */ public int[] getValueToMap() { return valueToMap; } /** * @return the ProjectionControl associated with this DisplayImpl */ public abstract ProjectionControl getProjectionControl(); /** * @return the GraphicsModeControl associated with this DisplayImpl */ public abstract GraphicsModeControl getGraphicsModeControl(); /** * wait for millis milliseconds * @param millis number of milliseconds to wait * @deprecated Use <CODE>new visad.util.Delay(millis)</CODE> instead. */ public static void delay(int millis) { new visad.util.Delay(millis); } /** * print a stack dump with the given message * @param message String to print with stack dump */ public static void printStack(String message) { try { throw new DisplayException("printStack: " + message); } catch (DisplayException e) { e.printStackTrace(); } } /** * test for equality between this and the given Object * given their complexity, its reasonable that DisplayImpl * objects are only equal to themselves * @param obj Object to test for equality with this * @return flag indicating whether this is equal to obj */ public boolean equals(Object obj) { return (obj == this); } /** * Returns the list of DataRenderer-s. NOTE: The actual list is returned * rather than a copy. If a copy is desired, then use * <code>getRendererVector()</code>. * @return The list of DataRenderer-s. * @see #getRendererVector() */ public Vector getRenderers() { return (Vector)RendererVector.clone(); } /** * Return the API used for this display * * @return the mode being used (UNKNOWN, JPANEL, APPLETFRAME, * OFFSCREEN, TRANSFORM_ONLY) * @throws VisADException */ public int getAPI() throws VisADException { throw new VisADException("No API specified"); } /** * @return the <CODE>DisplayMonitor</CODE> associated with this * <CODE>Display</CODE>. */ public DisplayMonitor getDisplayMonitor() { return displayMonitor; } /** * @return the <CODE>DisplaySync</CODE> associated with this * <CODE>Display</CODE>. */ public DisplaySync getDisplaySync() { return displaySync; } /** * set given MouseBehavior * @param m MouseBehavior to set */ public void setMouseBehavior(MouseBehavior m) { mouse = m; } /** * @return the MouseBehavior used for this Display */ public MouseBehavior getMouseBehavior() { return mouse; } /** * make projection matrix from given arguments * @param rotx rotation about x axis * @param roty rotation about y axis * @param rotz rotation about z axis * @param scale linear scale factor * @param transx translation along x axis * @param transy translation along y axis * @param transz translation along z axis * * @return projection matrix */ public double[] make_matrix(double rotx, double roty, double rotz, double scale, double transx, double transy, double transz) { if (mouse != null) { return mouse.make_matrix( rotx, roty, rotz, scale, transx, transy, transz); } else { return null; } } /** * multiply matrices * @param a first operand matrix * @param b second operand matrix * @return product matrix */ public double[] multiply_matrix(double[] a, double[] b) { if (mouse != null && a != null && b != null) { return mouse.multiply_matrix(a, b); } else { return null; } } /** * get a BufferedImage of this Display, without synchronizing * (assume the application has made sure Data have been * transformed and rendered) * @return a captured image of this Display */ public BufferedImage getImage() { return getImage(false); } /** * get a BufferedImage of this Display * @param sync if true, ensure that all linked Data have been * transformed and rendered * @return a captured image of this Display */ public BufferedImage getImage(boolean sync) { if (displayRenderer == null) return null; Thread thread = Thread.currentThread(); String name = thread.getName(); if (thread.equals(getCurrentActionThread()) || name.startsWith("J3D-Renderer") || name.startsWith("AWT-EventQueue")) { throw new VisADError("cannot call getImage() from Thread: " + name); } if (sync) new Syncher(this); return displayRenderer.getImage(); } /** * @return a String representation of this Display */ public String toString() { return toString(""); } /** * @param pre String added to start of each line * @return a String representation of this Display * indented by pre (a string of blanks) */ public String toString(String pre) { String s = pre + "Display\n"; Enumeration maps = MapVector.elements(); while(maps.hasMoreElements()) { ScalarMap map = (ScalarMap)maps.nextElement(); s = s + map.toString(pre + " "); } maps = ConstantMapVector.elements(); while(maps.hasMoreElements()) { ConstantMap map = (ConstantMap)maps.nextElement(); s = s + map.toString(pre + " "); } return s; } /** * * * @throws Throwable */ protected void finalize() throws Throwable { if (!destroyed) destroy(); } /** * Class used to ensure that all linked Data have been * transformed and rendered, used by getImage() */ public class Syncher extends Object implements DisplayListener { /** */ private ProjectionControl control; /** */ int count; /** * construct a Syncher for the given DisplayImpl * @param display DisplayImpl for this Syncher */ Syncher(DisplayImpl display) { try { synchronized (this) { control = display.getProjectionControl(); count = -1; display.disableAction(); display.addDisplayListener(this); display.reDisplayAll(); display.enableAction(); this.wait(); } } catch (InterruptedException e) { } display.removeDisplayListener(this); } /** * process DisplayEvent * @param e DisplayEvent to process * * @throws RemoteException * @throws VisADException */ public void displayChanged(DisplayEvent e) throws VisADException, RemoteException { if (e.getId() == DisplayEvent.TRANSFORM_DONE) { count = 2; control.setMatrix(control.getMatrix()); } else if (e.getId() == DisplayEvent.FRAME_DONE) { if (count > 0) { control.setMatrix(control.getMatrix()); count--; } else if (count == 0) { synchronized (this) { this.notify(); } count--; } } } } /** * Return the Printable object to be used by a PrinterJob. This can * be used as follows: * <pre> * PrinterJob printJob = PrinterJob.getPrinterJob(); * PageFormat pf = printJob.defaultPage(); * printJob.setPrintable(display.getPrintable(), pf); * if (printJob.printDialog()) { * try { * printJob.print(); * } * catch (Exception pe) { * pe.printStackTrace(); * } * } * </pre> * * @return printable object */ public Printable getPrintable() { if (printer == null) printer = new Printable() { public int print(Graphics g, PageFormat pf, int pi) throws PrinterException { if (pi >= 1) { return Printable.NO_SUCH_PAGE; } BufferedImage image = DisplayImpl.this.getImage(); g.drawImage( image, (int)pf.getImageableX(), (int)pf.getImageableY(), DisplayImpl.this.component); return Printable.PAGE_EXISTS; } }; return printer; } /** * handle DisconnectException for the given ReferenceActionLink * @param raLink ReferenceActionLink with DisconnectException */ void handleRunDisconnectException(ReferenceActionLink raLink) { if (!(raLink instanceof DataDisplayLink)) { return; } DataDisplayLink link = (DataDisplayLink)raLink; } /** * Notify this Display that a connection to a remote server has failed * @param renderer DataRenderer with failure * @param link DataDisplayLink with failure */ public void connectionFailed(DataRenderer renderer, DataDisplayLink link) { try { removeLinks(new DataDisplayLink[] {link}); } catch (VisADException ve) { ve.printStackTrace(); } catch (RemoteException re) { re.printStackTrace(); } if (renderer != null) { DataDisplayLink[] links = renderer.getLinks(); if (links.length <= 1) { deleteRenderer(renderer); } } Enumeration en = RmtSrcListeners.elements(); while(en.hasMoreElements()) { RemoteSourceListener l = (RemoteSourceListener)en.nextElement(); l.dataSourceLost(link.getName()); } } /** * Inform <tt>listener</tt> of deleted {@link DataRenderer}s. * * @param listener Object to add. */ public void addRendererSourceListener(RendererSourceListener listener) { RendererSourceListeners.addElement(listener); } /** * Remove <tt>listener</tt> from the {@link DataRenderer} deletion list. * * @param listener Object to remove. */ public void removeRendererSourceListener(RendererSourceListener listener) { RendererSourceListeners.removeElement(listener); } /** * Stop using a {@link DataRenderer}. * * @param renderer Renderer to delete */ private void deleteRenderer(DataRenderer renderer) { RendererVector.removeElement(renderer); Enumeration en = RendererSourceListeners.elements(); while(en.hasMoreElements()) { ((RendererSourceListener)en.nextElement()).rendererDeleted(renderer); } } /** * @deprecated * * @param listener */ public void addDataSourceListener(RemoteSourceListener listener) { addRemoteSourceListener(listener); } /** * @deprecated * * @param listener */ public void removeDataSourceListener(RemoteSourceListener listener) { removeRemoteSourceListener(listener); } /** * Inform <tt>listener</tt> of changes in the availability * of remote data/collaboration sources. * * @param listener Object to send change notifications. */ public void addRemoteSourceListener(RemoteSourceListener listener) { RmtSrcListeners.addElement(listener); } /** * Remove <tt>listener</tt> from the remote source notification list. * * @param listener Object to be removed. */ public void removeRemoteSourceListener(RemoteSourceListener listener) { RmtSrcListeners.removeElement(listener); } /** * Inform {@link RemoteSourceListener}s that the specified collaborative * connection has been lost.<br> * <br> * <b>WARNING!</b> This should only be called from within the * visad.collab package! * * @param id ID of lost connection. */ public void lostCollabConnection(int id) { Enumeration en = RmtSrcListeners.elements(); while(en.hasMoreElements()) { ((RemoteSourceListener)en.nextElement()).collabSourceLost(id); } } /** * Forward messages to the specified <tt>listener</tt> * * @param listener New message receiver. */ public void addMessageListener(MessageListener listener) { MessageListeners.addElement(listener); } /** * Remove <tt>listener</tt> from the message list. * * @param listener Object to remove. */ public void removeMessageListener(MessageListener listener) { MessageListeners.removeElement(listener); } /** * Send a message to all </tt>MessageListener</tt>s. * * @param msg Message being sent. * * @throws RemoteException */ public void sendMessage(MessageEvent msg) throws RemoteException { RemoteException exception = null; Enumeration en = MessageListeners.elements(); while(en.hasMoreElements()) { MessageListener l = (MessageListener)en.nextElement(); try { l.receiveMessage(msg); } catch (RemoteException re) { if (visad.collab.CollabUtil.isDisconnectException(re)) { // remote side disconnected; forget about it MessageListeners.removeElement(l); } else { // save this exception for later exception = re; } } } if (exception != null) { throw exception; } } /** * set aspect ratio of XAxis, YAxis & ZAxis in ScalarMaps rather * than matrix (i.e., don't distort text fonts); called by * ProjectionControl.setAspectCartesian() * @param aspect ratios; 3 elements for Java3D, 2 for Java2D * @throws VisADException a VisAD error occurred * @throws RemoteException an RMI error occurred */ void setAspectCartesian(double[] aspect) throws VisADException, RemoteException { if (displayRenderer == null) return; if (mapslock == null) return; synchronized (mapslock) { // clone MapVector to avoid need for synchronized access Vector tmap = (Vector)MapVector.clone(); Enumeration maps = tmap.elements(); while(maps.hasMoreElements()) { ScalarMap map = (ScalarMap)maps.nextElement(); map.setAspectCartesian(aspect); } tmap = (Vector)ConstantMapVector.clone(); maps = tmap.elements(); while(maps.hasMoreElements()) { ConstantMap map = (ConstantMap)maps.nextElement(); map.setAspectCartesian(aspect); } // resize box getDisplayRenderer().setBoxAspect(aspect); // reAutoScale(); ?? reDisplayAll(); } // end synchronized (mapslock) } /** * Add a busy/idle activity handler. * * @param ah Activity handler. * * @throws VisADException If the handler couldn't be added. */ public void addActivityHandler(ActivityHandler ah) throws VisADException { if (displayRenderer == null) return; if (displayActivity == null) { displayActivity = new DisplayActivity(this); } displayActivity.addHandler(ah); } /** * Remove a busy/idle activity handler. * * @param ah Activity handler. * * @throws VisADException If the handler couldn't be removed. */ public void removeActivityHandler(ActivityHandler ah) throws VisADException { if (displayRenderer == null) return; if (displayActivity == null) { displayActivity = new DisplayActivity(this); } displayActivity.removeHandler(ah); } /** * Indicate to activity monitor that the Display is busy. */ public void updateBusyStatus() { if (displayActivity != null) { displayActivity.updateBusyStatus(); } } /** Class for listening to component events */ private class ComponentChangedListener extends ComponentAdapter { /** the listener's display */ DisplayImpl display; /** * Create a listener for the display * * @param d */ public ComponentChangedListener(DisplayImpl d) { display = d; } /** * Invoked when the component has been resized. * @param ce ComponentEvent fired. */ public void componentShown(ComponentEvent ce) {} /** * Invoked when the component has been made invisible. * @param ce ComponentEvent fired. */ public void componentHidden(ComponentEvent ce) {} /** * Invoked when the component has been moved. * @param ce ComponentEvent fired. */ public void componentMoved(ComponentEvent ce) {} /** * Invoked when the component has been resized. * @param ce ComponentEvent fired. */ public void componentResized(ComponentEvent ce) { Component component = ce.getComponent(); Dimension d = component.getSize(); try { notifyListeners( new DisplayEvent(display, DisplayEvent.COMPONENT_RESIZED, d.width, d.height)); } catch (VisADException ve) { System.err.println("Couldn't notify listeners of resize event"); } catch (RemoteException re) { System.err.println( "Couldn't notify listeners of remote resize event"); } } } /** * Can be optionally implemented by graphics-api dependent DisplayImpls. * * @param renderer * @param mode */ public void setDepthBufferOffset(DataRenderer renderer, GraphicsModeControl mode) { } }