//
// FancySSCell.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.ss;

import java.awt.*;
import java.io.*;
import java.net.*;
import java.rmi.*;
import javax.swing.*;
import javax.swing.border.*;
import visad.*;
import visad.data.*;
import visad.formula.FormulaManager;
import visad.util.*;

/**
 * FancySSCell is an extension of BasicSSCell with extra options, such
 * as a file loader dialog and a dialog to set up ScalarMaps.
 * It provides an example of GUI extensions to BasicSSCell.
 */
public class FancySSCell extends BasicSSCell implements SSCellListener {

  // --- CONSTANTS ---

  /**
   * Dark red.
   */
  public static final Color DARK_RED = new Color(0.5f, 0f, 0f);

  /**
   * Dark green.
   */
  public static final Color DARK_GREEN = new Color(0f, 0.5f, 0f);

  /**
   * Dark blue.
   */
  public static final Color DARK_BLUE = new Color(0f, 0f, 0.5f);

  /**
   * Dark yellow.
   */
  public static final Color DARK_YELLOW = new Color(0.5f, 0.5f, 0f);

  /**
   * Dark purple.
   */
  public static final Color DARK_PURPLE = new Color(0.5f, 0f, 0.5f);

  /**
   * Dark cyan.
   */
  public static final Color DARK_CYAN = new Color(0f, 0.5f, 0.5f);

  /**
   * Border for cell with no data.
   */
  public static final Border B_EMPTY = new LineBorder(Color.gray, 3);

  /**
   * Border for selected cell.
   */
  public static final Border B_HIGHLIGHT = new LineBorder(Color.yellow, 3);

  /**
   * Border for cell with data from an unknown source.
   */
  public static final Border B_UNKNOWN = new LineBorder(DARK_PURPLE, 3);

  /**
   * Border for cell with data set directly.
   */
  public static final Border B_DIRECT = new LineBorder(DARK_CYAN, 3);

  /**
   * Border for cell with file or URL.
   */
  public static final Border B_URL = new LineBorder(DARK_GREEN, 3);

  /**
   * Border for cell with formula.
   */
  public static final Border B_FORMULA = new LineBorder(DARK_RED, 3);

  /**
   * Border for cell with RMI address.
   */
  public static final Border B_RMI = new LineBorder(DARK_BLUE, 3);

  /**
   * Border for cell with data from a remote source.
   */
  public static final Border B_REMOTE = new LineBorder(DARK_YELLOW, 3);

  /**
   * Border for cell with multiple data objects.
   */
  public static final Border B_MULTI = new CompoundBorder(
    new CompoundBorder(new LineBorder(DARK_RED), new LineBorder(DARK_GREEN)),
    new LineBorder(DARK_BLUE));


  // --- FIELDS ---

  /**
   * File chooser for loading and saving data. This variable is static so
   * that the directory is remembered between each load or save command.
   */
  protected static JFileChooser FileBox = Util.getVisADFileChooser();

  /**
   * Parent frame.
   */
  protected Frame Parent;

  /**
   * Associated JFrame, for use with VisAD Controls.
   */
  protected JFrame WidgetFrame;

  /**
   * Whether this cell is selected.
   */
  protected boolean Selected = false;

  /**
   * Whether this cell should auto-switch to 3-D.
   */
  protected boolean AutoSwitch = true;

  /**
   * Whether this cell should auto-detect mappings for data.
   */
  protected boolean AutoDetect = true;

  /**
   * Whether this cell should auto-display its widget frame.
   */
  protected boolean AutoShowControls = true;

  /**
   * Lock object for mapping auto-detection notification.
   */
  private Object MapLock = new Object();

  /**
   * Counter for mapping auto-detection notification.
   */
  private int MapCount = 0;


  // --- CONSTRUCTORS ---

  /**
   * Constructs a new FancySSCell with the given name.
   */
  public FancySSCell(String name) throws VisADException, RemoteException {
    this(name, null, null, false, null, null);
  }

  /**
   * Constructs a new FancySSCell with the given name and parent Frame.
   */
  public FancySSCell(String name, Frame parent)
    throws VisADException, RemoteException
  {
    this(name, null, null, false, null, parent);
  }

  /**
   * Constructs a new FancySSCell with the given name, formula manager,
   * and parent Frame.
   */
  public FancySSCell(String name, FormulaManager fman, Frame parent)
    throws VisADException, RemoteException
  {
    this(name, fman, null, false, null, parent);
  }

  /**
   * Constructs a new FancySSCell with the given name, remote server,
   * and parent Frame.
   */
  public FancySSCell(String name, RemoteServer rs, Frame parent)
    throws VisADException, RemoteException
  {
    this(name, null, rs, false, null, parent);
  }

  /**
   * Constructs a new FancySSCell with the given name, save string, and
   * parent Frame.
   */
  public FancySSCell(String name, String save, Frame parent)
    throws VisADException, RemoteException
  {
    this(name, null, null, false, save, parent);
  }

  /**
   * Constructs a new FancySSCell with the given name, formula manager,
   * remote server, save string, and parent Frame.
   */
  public FancySSCell(String name, FormulaManager fman, RemoteServer rs,
    String save, Frame parent) throws VisADException, RemoteException
  {
    this(name, fman, rs, false, save, parent);
  }

  /**
   * Constructs a new, possibly slaved, FancySSCell with the given name,
   * formula manager, remote server, save string, and parent Frame.
   */
  public FancySSCell(String name, FormulaManager fman, RemoteServer rs,
    boolean slave, String save, Frame parent) throws VisADException,
    RemoteException
  {
    super(name, fman, rs, slave, save);
    Parent = parent;
    WidgetFrame = new JFrame("Controls (" + Name + ")");
    Util.invoke(false, DEBUG, new Runnable() {
      public void run() {
        setHighlighted(false);
      }
    });
    addSSCellListener(this);
  }


  // --- DATA MANAGEMENT ---

  /**
   * Removes the Data object corresponding to the
   * given variable name from this cell.
   */
  public void removeData(String varName)
    throws VisADException, RemoteException
  {
    super.removeData(varName);
    if (CellData.size() == 0) clearWidgetFrame();
  }

  /**
   * Imports a data object from the given source of unknown type,
   * in a separate thread.
   */
  public void loadDataSource(String source) {
    loadDataSource(source, UNKNOWN_SOURCE);
  }

  /**
   * Imports a data object from the given source of the specified type,
   * in a separate thread.
   */
  public void loadDataSource(String source, int type) {
    final String fsource = source;
    final int ftype = type;
    final BasicSSCell cell = this;
    Runnable load = new Runnable() {
      public void run() {
        try {
          cell.addDataSource(fsource, ftype);
          if (!cell.hasData()) {
            JOptionPane.showMessageDialog(Parent, "Unable to import data",
              "Error importing data", JOptionPane.ERROR_MESSAGE);
          }
        }
        catch (VisADException exc) {
          if (DEBUG) exc.printStackTrace();
          JOptionPane.showMessageDialog(Parent, exc.getMessage(),
            "Error importing data", JOptionPane.ERROR_MESSAGE);
        }
        catch (RemoteException exc) {
          if (DEBUG) exc.printStackTrace();
          JOptionPane.showMessageDialog(Parent, exc.getMessage(),
            "Error importing data", JOptionPane.ERROR_MESSAGE);
        }
      }
    };
    Thread t = new Thread(load);
    t.start();
  }

  /**
   * Imports data from a file selected by the user.
   */
  public void loadDataDialog() {
    // get file name from file dialog
    FileBox.setDialogType(JFileChooser.OPEN_DIALOG);
    if (FileBox.showOpenDialog(Parent) != JFileChooser.APPROVE_OPTION) return;

    // make sure file exists
    File f = FileBox.getSelectedFile();
    if (!f.exists()) {
      JOptionPane.showMessageDialog(Parent, f.getName() + " does not exist",
        "Cannot load file", JOptionPane.ERROR_MESSAGE);
      return;
    }

    // load file
    loadDataSource(f.getAbsolutePath(), URL_SOURCE);
  }

  /**
   * Pops up a dialog box for user to select file where data will be saved.
   */
  private File getSaveFile() {
    if (!hasData()) {
      JOptionPane.showMessageDialog(Parent, "This cell is empty.",
        "Nothing to save", JOptionPane.ERROR_MESSAGE);
      return null;
    }

    // get file name from file dialog
    FileBox.setDialogType(JFileChooser.SAVE_DIALOG);
    if (FileBox.showSaveDialog(Parent) != JFileChooser.APPROVE_OPTION) {
      return null;
    }
    return FileBox.getSelectedFile();
  }

  /**
   * Saves a Data object to a file selected by the user,
   * using the given data form.
   */
  public void saveDataDialog(String varName, Form saveForm) {
    // get file where data should be saved
    final File file = getSaveFile();
    if (file == null) return;

    // start new thread to save the file
    final BasicSSCell cell = this;
    final String fname = varName;
    final Form form = saveForm;
    Runnable saveFile = new Runnable() {
      public void run() {
        String msg = "Could not save the dataset to the file " +
          "\"" + file.getName() + "\" in " + form.getName() + " format. ";
        try {
          cell.saveData(fname, file.getAbsolutePath(), form);
        }
        catch (BadFormException exc) {
          if (DEBUG) exc.printStackTrace();
          msg = msg + "An error occurred: " + exc.getMessage();
          JOptionPane.showMessageDialog(Parent, msg, "Error saving data",
            JOptionPane.ERROR_MESSAGE);
        }
        catch (RemoteException exc) {
          if (DEBUG) exc.printStackTrace();
          msg = msg + "A remote error occurred: " + exc.getMessage();
          JOptionPane.showMessageDialog(Parent, msg, "Error saving data",
            JOptionPane.ERROR_MESSAGE);
        }
        catch (IOException exc) {
          if (DEBUG) exc.printStackTrace();
          msg = msg + "An I/O error occurred: " + exc.getMessage();
          JOptionPane.showMessageDialog(Parent, msg, "Error saving data",
            JOptionPane.ERROR_MESSAGE);
        }
        catch (VisADException exc) {
          if (DEBUG) exc.printStackTrace();
          msg = msg + "An error occurred: " + exc.getMessage();
          JOptionPane.showMessageDialog(Parent, msg, "Error saving data",
            JOptionPane.ERROR_MESSAGE);
        }
      }
    };
    Thread t = new Thread(saveFile);
    t.start();
  }

  /**
   * Blocks until mapping auto-detection is complete.
   */
  public void waitForMaps() {
    synchronized (MapLock) {
      while (MapCount > 0) {
        try { MapLock.wait(); }
        catch (InterruptedException exc) {
          if (DEBUG && DEBUG_LEVEL >= 3) exc.printStackTrace();
        }
      }
    }
  }

  /**
   * Does the work of adding the given DataReference,
   * from the given source of the specified type.
   *
   * @return The newly created SSCellData object.
   */
  protected SSCellData addReferenceImpl(int id, DataReferenceImpl ref,
    ConstantMap[] cmaps, String source, int type, boolean notify,
    boolean checkErrors) throws VisADException, RemoteException
  {
    synchronized (MapLock) { MapCount++; }
    return super.addReferenceImpl(id, ref, cmaps,
      source, type, notify, checkErrors);
  }

  // --- DISPLAY MANAGEMENT ---

  /**
   * Whether the mapping dialog is currently being displayed.
   */
  private boolean mapDialogUp = false;

  /**
   * Asks user to confirm clearing the cell if any other cell depends on it.
   */
  public boolean confirmClear() {
    if (othersDepend()) {
      int ans = JOptionPane.showConfirmDialog(null, "Other cells depend on " +
        "this cell. Are you sure you want to clear it?", "Warning",
        JOptionPane.YES_NO_OPTION);
      if (ans != JOptionPane.YES_OPTION) return false;
    }
    return true;
  }

  /**
   * Clears the cell if no other cell depends on it; otherwise, ask the
   * user "Are you sure?" return true if the cell was cleared.
   */
  public boolean smartClear() throws VisADException, RemoteException {
    if (confirmClear()) {
      clearWidgetFrame();
      clearCell();
      return true;
    }
    else return false;
  }

  /**
   * Permanently destroy this cell, asking user for confirmation first
   * if other cells depend on it; return true if the cell was destroyed.
   */
  public boolean smartDestroy() throws VisADException, RemoteException {
    if (confirmClear()) {
      clearWidgetFrame();
      destroyCell();
      return true;
    }
    else return false;
  }

  /**
   * Switches to 3-D mode if necessary and available.
   */
  public void setMapsAuto(ScalarMap[] maps)
    throws VisADException, RemoteException
  {
    if (AutoSwitch && maps != null) {
      int need = 0;
      for (int i=0; i 0) {
          WidgetFrame.setContentPane(jc);
          WidgetFrame.pack();
          WidgetFrame.setVisible(true);
        }
      }
    });
  }

  /**
   * Hides the widgets for altering controls.
   */
  public void hideWidgetFrame() {
    Util.invoke(false, DEBUG, new Runnable() {
      public void run() {
        WidgetFrame.setVisible(false);
      }
    });
  }

  /**
   * Specifies whether the FancySSCell has a border.
   */
  public void setBorderEnabled(boolean value) {
    if (value) setSelected(Selected);
    else setBorder(null);
  }

  /**
   * Specifies whether the FancySSCell has a highlighted border.
   */
  public void setSelected(boolean value) {
    if (Selected == value) return;
    Selected = value;
    Util.invoke(false, DEBUG, new Runnable() {
      public void run() {
        setHighlighted(Selected);
        if (!Selected) hideWidgetFrame();
        else if (AutoShowControls) showWidgetFrame();
        refresh();
      }
    });
  }

  /**
   * Specifies whether this FancySSCell should auto-switch to 3-D.
   */
  public synchronized void setAutoSwitch(boolean value) {
    AutoSwitch = value;
  }

  /**
   * Specifies whether this FancySSCell should auto-detect its mappings.
   */
  public synchronized void setAutoDetect(boolean value) {
    AutoDetect = value;
  }

  /**
   * Specifies whether this FancySSCell should auto-display its widget frame.
   */
  public synchronized void setAutoShowControls(boolean value) {
    AutoShowControls = value;
  }

  /**
   * Removes all widgets for altering controls and hide widget frame.
   */
  private void clearWidgetFrame() {
    Util.invoke(false, DEBUG, new Runnable() {
      public void run() {
        WidgetFrame.setVisible(false);
        JPanel pane = new JPanel();
        pane.add(new JLabel("No controls"), "CENTER");
        WidgetFrame.setContentPane(pane);
      }
    });
  }

  /**
   * Sets whether this cell is highlighted.
   */
  private void setHighlighted(boolean hl) {
    if (hl) setBorder(B_HIGHLIGHT);
    else {
      int dataCount = getDataCount();
      if (dataCount == 0) {
        // no datasets
        setBorder(B_EMPTY);
      }
      else if (dataCount == 1) {
        // single dataset
        int type = getDataSourceType(getFirstVariableName());
        if (type == DIRECT_SOURCE) setBorder(B_DIRECT);
        else if (type == URL_SOURCE) setBorder(B_URL);
        else if (type == FORMULA_SOURCE) setBorder(B_FORMULA);
        else if (type == RMI_SOURCE) setBorder(B_RMI);
        else if (type == REMOTE_SOURCE) setBorder(B_REMOTE);
        else setBorder(B_UNKNOWN);
      }
      else {
        // multiple datasets
        setBorder(B_MULTI);
      }
    }
  }


  // --- EVENT HANDLING ---

  /**
   * Re-detects mappings when this cell's data changes.
   */
  public void ssCellChanged(SSCellChangeEvent e) {
    int type = e.getChangeType();
    if (type == SSCellChangeEvent.DATA_CHANGE) {
      // refresh border color
      Util.invoke(false, DEBUG, new Runnable() {
        public void run() {
          setHighlighted(Selected);
        }
      });

      if (!IsRemote) {
        // attempt to auto-detect mappings for new data
        Data value = null;
        try {
          value = (Data) fm.getThing(e.getVariableName());
        }
        catch (ClassCastException exc) {
          if (DEBUG) exc.printStackTrace();
        }
        catch (VisADException exc) {
          if (DEBUG) exc.printStackTrace();
        }
        try {
          if (value != null) autoDetectMappings();
        }
        catch (VisADException exc) {
          if (DEBUG) exc.printStackTrace();
        }
        catch (RemoteException exc) {
          if (DEBUG) exc.printStackTrace();
        }
      }
    }
    else if (type == SSCellChangeEvent.DISPLAY_CHANGE) {
      if (IsRemote) {
        // reconstruct controls for cloned display
        if (AutoShowControls) showWidgetFrame();
      }
    }
  }


  // --- MISCELLANEOUS ---

  /**
   * Captures display image and saves to a file selected by the user,
   * in JPEG format.
   */
  public void captureDialog() {
    // get file where captured image should be saved
    final File f = getSaveFile();
    if (f == null) return;

    // start new thread to capture image and save it to the file
    final BasicSSCell cell = this;
    Runnable captureImage = new Runnable() {
      public void run() {
        String msg = "Could not save image snapshot to file \"" + f.getName() +
          "\" in JPEG format. ";
        try {
          cell.captureImage(f);
        }
        catch (VisADException exc) {
          if (DEBUG) exc.printStackTrace();
          msg = msg + "An error occurred: " + exc.getMessage();
          JOptionPane.showMessageDialog(Parent, msg, "Error saving data",
            JOptionPane.ERROR_MESSAGE);
        }
        catch (IOException exc) {
          if (DEBUG) exc.printStackTrace();
          msg = msg + "An I/O error occurred: " + exc.getMessage();
          JOptionPane.showMessageDialog(Parent, msg, "Error saving data",
            JOptionPane.ERROR_MESSAGE);
        }
      }
    };
    Thread t = new Thread(captureImage);
    t.start();
  }


  // --- ACCESSORS ---

  /**
   * Returns whether this FancySSCell auto-switches to 3-D.
   */
  public boolean getAutoSwitch() {
    return AutoSwitch;
  }

  /**
   * Returns whether this FancySSCell auto-detects its mappings.
   */
  public boolean getAutoDetect() {
    return AutoDetect;
  }

  /**
   * Returns whether this FancySSCell auto-displays its widget frame.
   */
  public boolean getAutoShowControls() {
    return AutoShowControls;
  }

  /**
   * Returns whether the cell has any associated controls.
   */
  public boolean hasControls() {
    if (VDisplay == null || CellData.size() == 0) return false;
    Container jc = VDisplay.getWidgetPanel();
    if (jc == null) return false;
    return (jc.getComponentCount() > 0);
  }


  // --- DEPRECATED ---

  /**
   * @deprecated Use loadDataSource(String, RMI_SOURCE) instead.
   */
  public void loadDataRMI(String s) {
    loadDataSource(s, RMI_SOURCE);
  }

  /**
   * @deprecated Use loadDataSource(String, URL_SOURCE) instead.
   */
  public synchronized void loadDataString(String s) {
    loadDataSource(s, URL_SOURCE);
  }

  /**
   * @deprecated Use loadDataSource(String, URL_SOURCE) instead.
   */
  public void loadDataURL(URL u) {
    loadDataSource(u.toString(), URL_SOURCE);
  }

  /**
   * @deprecated Use saveDataDialog(String, Form) instead.
   */
  public void saveDataDialog(boolean netcdf) {
    try {
      Form f;
      if (netcdf) f = new visad.data.netcdf.Plain();
      else f = new visad.data.visad.VisADForm();
      saveDataDialog(getFirstVariableName(), f);
    }
    catch (VisADException exc) {
      if (DEBUG) exc.printStackTrace();
    }
  }

  /**
   * @deprecated Use saveDataDialog(String, Form) instead.
   */
  public void saveDataDialog(Form saveForm) {
    saveDataDialog(getFirstVariableName(), saveForm);
  }

}

 

Ads help maintain this website.