// // SpreadSheet.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.awt.event.*; import java.awt.print.*; import java.io.*; import java.lang.reflect.*; import java.net.*; import java.rmi.*; import java.rmi.registry.*; import java.util.*; import javax.swing.*; import javax.swing.border.*; import visad.*; import visad.data.Form; import visad.data.netcdf.Plain; import visad.data.tiff.TiffForm; import visad.data.visad.VisADForm; import visad.formula.*; import visad.util.*; /** * SpreadSheet is a user interface for VisAD that supports * multiple 3-D displays (FancySSCells). */ public class SpreadSheet extends GUIFrame implements AdjustmentListener, DisplayListener, KeyListener, ItemListener, MouseListener, MouseMotionListener, SSCellListener { /** * Starting width of the application, in percentage of screen size. */ protected static final int WIDTH_PERCENT = 60; /** * Starting width of the application, in percentage of screen size. */ protected static final int HEIGHT_PERCENT = 80; /** * Minimum VisAD display width, including display border. */ protected static final int MIN_VIS_WIDTH = 120; /** * Minimum VisAD display height, including display border. */ protected static final int MIN_VIS_HEIGHT = 120; /** * Default VisAD display width. */ protected static final int DEFAULT_VIS_WIDTH = 250; /** * Default VisAD display height. */ protected static final int DEFAULT_VIS_HEIGHT = 250; /** * Spreadsheet cell letter order. */ protected static final String Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /** * Vertical cell label's width. */ protected static final int LABEL_WIDTH = 30; /** * Horizontal cell label's height. */ protected static final int LABEL_HEIGHT = 20; /** * Whether connection status messages are printed about clones. */ protected static final boolean SHOW_CONNECT_MESSAGES = true; /** * Header for first line of spreadsheet files. */ protected static final String SSFileHeader = "# VisAD Visualization SpreadSheet spreadsheet file"; /** * Argument classes for constructing an SSCell. */ protected static final Class[] cellArgs = { String.class, FormulaManager.class, RemoteServer.class, boolean.class, String.class, Frame.class }; /** * Constructor used to create SSCells for SpreadSheets. */ protected static Constructor cellConstructor; /** * Whether Java3D is possible on this JVM. */ protected static boolean Possible3D; /** * Whether Java3D is enabled on this JVM. */ protected static boolean CanDo3D; /** * Whether the HDF-5 native library is present on this JVM. */ protected static boolean CanDoHDF5; /** * Whether this JVM supports saving JPEG images with JPEGImageEncoder. */ protected static boolean CanDoJPEG; /** * Whether this JVM supports JPython scripting. */ protected static boolean CanDoPython; /** * Whether spreadsheet should have toolbar buttons. */ protected static boolean BugFix; /** * File dialog. */ protected JFileChooser SSFileDialog; /** * Base title. */ protected String bTitle; /** * Number of display columns. */ protected int NumVisX; /** * Number of display rows. */ protected int NumVisY; /** * Formula manager. */ protected FormulaManager fm; /** * Server name, if any. */ protected String serverName; /** * Server address for a cloned sheet, if any. */ protected String cloneAddress; /** * Server for spreadsheet cells, if any. */ protected RemoteServerImpl rsi = null; /** * Whether spreadsheet is a clone of another spreadsheet. */ protected boolean IsRemote = false; /** * Whether spreadsheet is a slaved clone of another spreadsheet. */ protected boolean IsSlave = false; /** * ID number for this collaborative spreadsheet. */ protected double CollabID = 0; /** * Row and column information needed for spreadsheet cloning. */ protected RemoteDataReference RemoteColRow; /** * Remote clone's copy of CanDo3D. */ protected RemoteDataReference RemoteCanDo3D; /** * Flag marking whether spreadsheet's cells * automatically switch dimensions when needed. */ protected boolean AutoSwitch = true; /** * Flag marking whether spreadsheet's cells * automatically detect mappings. */ protected boolean AutoDetect = true; /** * Flag marking whether spreadsheet's cells * automatically show controls. */ protected boolean AutoShowControls = true; /** * Panel that contains actual VisAD displays. */ protected Panel DisplayPanel; /** * Panel containing the scrolling pane. */ protected JPanel ScrollPanel; /** * Scrolling pane, in case sheet gets too small. */ protected ScrollPane SCPane; /** * View port for horizontal cell labels. */ protected JViewport HorizLabels; /** * View port for vertical cell labels. */ protected JViewport VertLabels; /** * Array of panels for horizontal labels. */ protected JPanel[] HorizLabel; /** * Array of panels for vertical labels. */ protected JPanel[] VertLabel; /** * Array of horizontal yellow sizing boxes. */ protected JComponent[] HorizDrag; /** * Array of vertical yellow sizing boxes. */ protected JComponent[] VertDrag; /** * Panel containing horizontal labels and sizing boxes. */ protected JPanel HorizPanel; /** * Panel containing vertical labels and sizing boxes. */ protected JPanel VertPanel; /** * Array of spreadsheet cells. */ protected FancySSCell[][] DisplayCells = null; /** * Formula bar. */ protected JComboBox FormulaBox; /** * Formula editor. */ protected ComboBoxEditor FormulaEditor; /** * Formula text field. */ protected JTextField FormulaText; /** * Formula action listener. */ protected ActionListener FormulaListener; /** * Tool bar. */ protected JToolBar Toolbar; /** * Submenus. */ protected JMenu FileExport; /** * Menu items. */ protected JMenuItem FileSave1, FileSave2, FileSave3, FileSave4, FileSave5, FileSnap, EditPaste, EditClear, CellDel, CellPrint, CellEdit, CellReset, CellShow, LayAddCol, LayDelCol, LayDelRow; /** * Checkbox menu items. */ protected JCheckBoxMenuItem CellDim3D3D, CellDim2D2D, CellDim2D3D, AutoSwitchBox, AutoDetectBox, AutoShowBox; /** * Toolbar buttons. */ protected JButton ToolSave, ToolPaste, Tool3D, Tool2D, ToolJ2D, ToolMap, ToolShow, ToolReset, FormulaAdd, FormulaDel; /** * Column of currently selected cell. */ protected int CurX = 0; /** * Row of currently selected cell. */ protected int CurY = 0; /** * Contents of clipboard. */ protected String Clipboard = null; /** * Current spreadsheet file. */ protected File CurrentFile = null; /** * Object for preventing simultaneous GUI manipulation. */ protected Object Lock = new Object(); /** * Waits the specified number of milliseconds. */ public static void snooze(long ms) { try { Thread.sleep(ms); } catch (InterruptedException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } } /** * Gateway into VisAD Visualization SpreadSheet user interface. */ public static void main(String[] argv) { String usage = "\n" + "Usage: java [-mx###m] visad.ss.SpreadSheet [cols rows]\n" + " [-file filename] [-gui] [-no3d] [-debug] [-bugfix]\n" + " [-server name] [-client address] [-slave address]\n\n" + "###\n" + " Maximum megabytes of memory to use.\n" + "cols\n" + " Number of columns in this SpreadSheet.\n" + "rows\n" + " Number of rows in this SpreadSheet.\n" + "-file filename\n" + " Load the given filename at launch. If file is a\n" + " spreadsheet file, the layout is configured accordingly.\n" + " If file is data, it is loaded into cell A1.\n" + "-gui\n" + " Pop up an options window so that the user can\n" + " select SpreadSheet settings graphically.\n" + "-no3d\n" + " Disable Java3D.\n" + "-debug\n" + " Print stack traces for all errors.\n" + "-bugfix\n" + " Disable toolbar. For some systems, will prevent\n" + " lockups on spreadsheet start.\n" + "-server name\n" + " Initialize this SpreadSheet as an RMI server\n" + " with the given name.\n" + "-client address\n" + " Initialize this SpreadSheet as a clone of\n" + " the server at the given RMI address.\n" + "-slave address\n" + " Initialize this SpreadSheet as a slaved clone\n" + " of the server at the given RMI address."; int cols = 2; int rows = 2; String dfile = null; String servname = null; String clonename = null; boolean guiOptions = false; int len = argv.length; if (len > 0) { int ix = 0; // parse command line flags while (ix < len) { if (argv[ix].charAt(0) == '-') { if (argv[ix].equals("-file")) { if (ix < len - 1) dfile = argv[++ix]; } else if (argv[ix].equals("-server")) { if (clonename != null) { System.out.println("A spreadsheet cannot be both a server " + "and a clone!"); System.out.println(usage); System.exit(3); } else if (ix < len - 1) servname = argv[++ix]; else { System.out.println("You must specify a server name after " + "the '-server' flag!"); System.out.println(usage); System.exit(4); } } else if (argv[ix].equals("-client") || argv[ix].equals("-slave")) { if (servname != null) { System.out.println("A spreadsheet cannot be both a server " + "and a clone!"); System.out.println(usage); System.exit(3); } else if (ix < len - 1) { clonename = argv[ix + 1]; if (argv[ix].equals("-slave")) clonename = "slave:" + clonename; ix++; } else { System.out.println("You must specify a server after " + "the '" + argv[ix] + "' flag!"); System.out.println(usage); System.exit(5); } } else if (argv[ix].equals("-gui")) guiOptions = true; else if (argv[ix].equals("-bugfix")) BugFix = true; else if (argv[ix].equals("-no3d")) BasicSSCell.disable3D(); else if (argv[ix].equals("-debug")) { BasicSSCell.DEBUG = true; FormulaVar.DEBUG = true; } else { // unknown flag if (!argv[ix].equals("-help")) { System.out.println("Unknown option: " + argv[ix]); } System.out.println(usage); System.exit(1); } } else { // parse number of rows and columns boolean success = true; if (ix < len - 1) { try { cols = Integer.parseInt(argv[ix]); rows = Integer.parseInt(argv[ix + 1]); ix++; if (rows < 1 || cols < 1 || cols > Letters.length()) { success = false; } } catch (NumberFormatException exc) { success = false; } if (!success) { System.out.println("Invalid number of columns and rows: " + argv[ix] + " x " + argv[ix + 1]); System.out.println(usage); System.exit(2); } } else { System.out.println("Unknown option: " + argv[ix]); System.out.println(usage); System.exit(1); } } ix++; } } final SpreadSheet ss = new SpreadSheet(WIDTH_PERCENT, HEIGHT_PERCENT, cols, rows, servname, clonename, "VisAD SpreadSheet", null, guiOptions); if (dfile != null) { File f = new File(dfile); String line = null; try { BufferedReader fin = new BufferedReader(new FileReader(f)); line = fin.readLine(); fin.close(); } catch (IOException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); System.out.println("Could not read file " + dfile); } if (line != null) { final boolean ssfile = line.equals(SSFileHeader); final String filename = dfile; Util.invoke(false, BasicSSCell.DEBUG, new Runnable() { public void run() { try { if (ssfile) { // file is a spreadsheet file ss.openFile(filename); } else { // file is a data file ss.DisplayCells[0][0].addDataSource(filename); } } catch (Exception exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); System.out.println("Could not load file " + filename + " into the SpreadSheet"); } } }); } } } // --- CONSTRUCTORS --- /** * Constructor with option selection dialog at default values. */ public SpreadSheet() { this(WIDTH_PERCENT, HEIGHT_PERCENT, 2, 2, null, null, "VisAD SpreadSheet", null, true); } /** * Constructor with default formula manager and no option selection dialog. */ public SpreadSheet(int sWidth, int sHeight, int cols, int rows, String server, String clone, String sTitle) { this(sWidth, sHeight, cols, rows, server, clone, sTitle, null, false); } /** * Constructor with no option selection dialog. */ public SpreadSheet(int sWidth, int sHeight, int cols, int rows, String server, String clone, String sTitle, FormulaManager fm) { this(sWidth, sHeight, cols, rows, server, clone, sTitle, fm, false); } /** * Main constructor. */ public SpreadSheet(int sWidth, int sHeight, int cols, int rows, String server, String clone, String sTitle, FormulaManager fm, boolean chooseOptions) { super(true); bTitle = sTitle; NumVisX = cols; NumVisY = rows; this.fm = fm; Possible3D = BasicSSCell.possible3D(); CanDo3D = BasicSSCell.canDo3D(); MappingDialog.initDialog(); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { quitProgram(); } }); setBackground(Color.white); // parse clone address boolean slave = clone != null && clone.startsWith("slave:"); if (slave) clone = clone.substring(6); if (clone != null) { int slash = clone.lastIndexOf("/"); if (slash < 0) slash = clone.lastIndexOf(":"); server = clone.substring(slash + 1); clone = clone.substring(0, slash); } serverName = server; cloneAddress = clone; IsSlave = slave; // test whether HDF-5 native library is present CanDoHDF5 = Util.canDoHDF5(); if (!CanDoHDF5 && BasicSSCell.DEBUG) { System.err.println("Warning: HDF-5 library not found"); } // test whether JPEG codec is present CanDoJPEG = Util.canDoJPEG(); if (!CanDoJPEG && BasicSSCell.DEBUG) { System.err.println("Warning: JPEG codec not found"); } // test whether JPython is present CanDoPython = Util.canDoPython(); if (!CanDoPython && BasicSSCell.DEBUG) { System.err.println("Warning: JPython not found"); } // create file chooser dialog SSFileDialog = new JFileChooser(System.getProperty("user.dir")); SSFileDialog.addChoosableFileFilter( new ExtensionFileFilter("ss", "SpreadSheet files")); if (chooseOptions) { // get settings from option selection dialog getOptions(NumVisX, NumVisY, serverName, cloneAddress, IsSlave); } clone = cloneAddress == null ? null : cloneAddress + "/" + serverName; server = cloneAddress == null ? serverName : null; // determine information for spreadsheet cloning RemoteServer rs = null; String[][] cellNames = null; if (clone != null) { // CLIENT: initialize // connect to server boolean success = true; if (SHOW_CONNECT_MESSAGES) { System.out.print("Connecting to " + clone + " "); } // connection loop while (cellNames == null && success) { // wait a second before trying to connect snooze(1000); if (SHOW_CONNECT_MESSAGES) System.out.print("."); try { // look up server rs = (RemoteServer) Naming.lookup("//" + clone); // determine whether server supports Java3D RemoteCanDo3D = rs.getDataReference("CanDo3D"); Real bit = (Real) RemoteCanDo3D.getData(); if (bit.getValue() == 0) { CanDo3D = false; BasicSSCell.disable3D(); } // extract cell name information RemoteColRow = rs.getDataReference("ColRow"); cellNames = getNewCellNames(); } catch (UnmarshalException exc) { // fatal RMI error, probably a version difference; display error box if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Unable to clone the spreadsheet at " + clone + ". The server is using an incompatible version of Java", null, "Failed to clone spreadsheet"); success = false; } catch (MalformedURLException exc) { // server name is invalid; display error box if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Unable to clone the spreadsheet at " + clone + ". The server name is not valid", null, "Failed to clone spreadsheet"); success = false; } catch (VisADException exc) { // fatal error of some other type; display error box if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Unable to clone the spreadsheet at " + clone + ". An error occurred while downloading the necessary data", exc, "Failed to clone spreadsheet"); success = false; } catch (NotBoundException exc) { // server is not ready yet; try again if (BasicSSCell.DEBUG) exc.printStackTrace(); } catch (NullPointerException exc) { // server is not ready yet; try again if (BasicSSCell.DEBUG) exc.printStackTrace(); } catch (RemoteException exc) { // server is not ready yet; try again if (BasicSSCell.DEBUG) exc.printStackTrace(); } } if (success) { if (SHOW_CONNECT_MESSAGES) System.out.println(" done"); bTitle = bTitle + " [" + (IsSlave ? "slaved" : "collaborative") + " mode: " + clone + "]"; IsRemote = true; } else { if (SHOW_CONNECT_MESSAGES) System.out.println(" failed"); IsSlave = false; rs = null; } } // set up the content pane JPanel pane = new JPanel(); pane.setBackground(Color.white); pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS)); setContentPane(pane); // set up file menu addMenuItem("File", "Import data...", "loadDataSet", 'i'); FileExport = addSubMenu("File", "Export data", 'e', false); FileSave1 = addMenuItem("Export data", "netCDF...", "exportDataSetNetcdf", 'n', true); FileSave3 = addMenuItem("Export data", "HDF-5...", "exportDataSetHDF5", 'h', CanDoHDF5); FileSave4 = addMenuItem("Export data", "TIFF...", "exportDataSetTIFF", 't', true); FileSave2 = addMenuItem("Export data", "Serialized...", "exportDataSetSerial", 's', true); FileSave5 = addMenuItem("Export data", "Binary...", "exportDataSetBinary", 'b', true); addMenuSeparator("File"); FileSnap = addMenuItem("File", "Take JPEG snapshot...", "captureImageJPEG", 'j', false); addMenuSeparator("File"); addMenuItem("File", "Exit", "quitProgram", 'x'); // set up edit menu addMenuItem("Edit", "Cut", "cutCell", 't', !IsRemote); addMenuItem("Edit", "Copy", "copyCell", 'c', !IsRemote); EditPaste = addMenuItem("Edit", "Paste", "pasteCell", 'p', false); EditClear = addMenuItem("Edit", "Clear", "clearCell", 'l', false); // set up setup menu addMenuItem("Setup", "New spreadsheet file", "newFile", 'n'); addMenuItem("Setup", "Open spreadsheet file...", "openFile", 'o', !IsRemote); addMenuItem("Setup", "Save spreadsheet file", "saveFile", 's', !IsRemote); addMenuItem("Setup", "Save spreadsheet file as...", "saveAsFile", 'a', !IsRemote); // set up cell menu CellDim3D3D = new JCheckBoxMenuItem("3-D (Java3D)", CanDo3D); addMenuItem("Cell", CellDim3D3D, "setDim3D", '3', CanDo3D); CellDim2D2D = new JCheckBoxMenuItem("2-D (Java2D)", !CanDo3D); addMenuItem("Cell", CellDim2D2D, "setDimJ2D", 'j', true); CellDim2D3D = new JCheckBoxMenuItem("2-D (Java3D)", false); addMenuItem("Cell", CellDim2D3D, "setDim2D", '2', CanDo3D); addMenuSeparator("Cell"); addMenuItem("Cell", "Add data object", "formulaAdd", 'a'); CellDel = addMenuItem("Cell", "Delete data object", "formulaDel", 'd', false); addMenuSeparator("Cell"); CellPrint = addMenuItem("Cell", "Print cell...", "printCurrentCell", 'p', false); addMenuSeparator("Cell"); CellEdit = addMenuItem("Cell", "Edit mappings...", "createMappings", 'e', false); CellReset = addMenuItem("Cell", "Reset orientation", "resetOrientation", 'r', false); CellShow = addMenuItem("Cell", "Show controls", "showControls", 's', false); // set up layout menu LayAddCol = addMenuItem("Layout", "Add column", "addColumn", 'c'); addMenuItem("Layout", "Add row", "addRow", 'r'); LayDelCol = addMenuItem("Layout", "Delete column", "deleteColumn", 'l', NumVisX > 1); LayDelRow = addMenuItem("Layout", "Delete row", "deleteRow", 'w', NumVisY > 1); addMenuSeparator("Layout"); addMenuItem("Layout", "Tile cells", "tileCells", 't'); // set up options menu if (!CanDo3D) AutoSwitch = false; AutoSwitchBox = new JCheckBoxMenuItem("Auto-switch to 3-D", AutoSwitch && !IsRemote); addMenuItem("Options", AutoSwitchBox, "optionsSwitch", '3', CanDo3D && !IsRemote); AutoDetectBox = new JCheckBoxMenuItem("Auto-detect mappings", AutoDetect && !IsRemote); addMenuItem("Options", AutoDetectBox, "optionsDetect", 'm', !IsRemote); AutoShowBox = new JCheckBoxMenuItem("Auto-display controls", AutoShowControls && !IsSlave); addMenuItem("Options", AutoShowBox, "optionsDisplay", 'c', !IsSlave); // set up toolbar if (!BugFix) { Toolbar = new JToolBar(); Toolbar.setBackground(Color.lightGray); Toolbar.setBorder(new EtchedBorder()); Toolbar.setFloatable(false); pane.add(Toolbar); // file menu toolbar icons addToolbarButton("open", "Import data", "loadDataSet", true, Toolbar); ToolSave = addToolbarButton("save", "Export data to netCDF", "exportDataSetNetcdf", false, Toolbar); Toolbar.addSeparator(); // edit menu toolbar icons addToolbarButton("cut", "Cut", "cutCell", !IsRemote, Toolbar); addToolbarButton("copy", "Copy", "copyCell", !IsRemote, Toolbar); ToolPaste = addToolbarButton("paste", "Paste", "pasteCell", false, Toolbar); Toolbar.addSeparator(); // cell menu toolbar icons Tool3D = addToolbarButton("3d", "3-D (Java3D)", "setDim3D", false, Toolbar); ToolJ2D = addToolbarButton("j2d", "2-D (Java2D)", "setDimJ2D", CanDo3D, Toolbar); Tool2D = addToolbarButton("2d", "2-D (Java3D)", "setDim2D", CanDo3D, Toolbar); Toolbar.addSeparator(); ToolMap = addToolbarButton("mappings", "Edit mappings", "createMappings", false, Toolbar); ToolReset = addToolbarButton("reset", "Reset orientation", "resetOrientation", false, Toolbar); ToolShow = addToolbarButton("show", "Show controls", "showControls", false, Toolbar); Toolbar.addSeparator(); // layout menu toolbar icon addToolbarButton("tile", "Tile cells", "tileCells", true, Toolbar); Toolbar.add(Box.createHorizontalGlue()); // HACK - limit toolbar height Toolbar.setMaximumSize(new Dimension(Toolbar.getMaximumSize().width, Toolbar.getPreferredSize().height)); } // set up formula bar JPanel formulaPanel = new JPanel(); formulaPanel.setPreferredSize(new Dimension(Integer.MAX_VALUE, 25)); formulaPanel.setBackground(Color.lightGray); formulaPanel.setLayout(new BoxLayout(formulaPanel, BoxLayout.X_AXIS)); formulaPanel.setBorder(new EtchedBorder()); pane.add(formulaPanel); pane.add(Box.createRigidArea(new Dimension(0, 6))); if (!BugFix) { FormulaAdd = addToolbarButton("add", "Add data", "formulaAdd", true, formulaPanel); FormulaDel = addToolbarButton("del", "Remove data", "formulaDel", true, formulaPanel); } FormulaBox = new JComboBox(); formulaPanel.add(FormulaBox); FormulaBox.setLightWeightPopupEnabled(false); FormulaBox.setEditable(true); FormulaEditor = FormulaBox.getEditor(); FormulaListener = new ActionListener() { public void actionPerformed(ActionEvent e) { String newItem = ((String) FormulaEditor.getItem()).trim(); try { int id = 0; int type = BasicSSCell.UNKNOWN_SOURCE; boolean notify = true; int index1 = newItem.indexOf("d", 2); int index2 = newItem.indexOf(":"); if (index1 > 0 && index2 > 0 && index1 < index2) { String cellName = newItem.substring(0, index1); BasicSSCell ssCell = BasicSSCell.getSSCellByName(cellName); int dataId = 0; try { dataId = Integer.parseInt(newItem.substring(index1 + 1, index2)); } catch (NumberFormatException exc) { if (BasicSSCell.DEBUG && BasicSSCell.DEBUG_LEVEL >= 3) { exc.printStackTrace(); } } if (ssCell == DisplayCells[CurX][CurY] && dataId > 0) { // user is 'editing' the data object String varName = newItem.substring(0, index2); String source = ssCell.getDataSource(varName); if (source != null) { ssCell.removeData(varName); String oldItem = null; for (int i=0; i<FormulaBox.getItemCount(); i++) { String item = (String) FormulaBox.getItemAt(i); if (item.startsWith(varName + ":")) { oldItem = item; break; } } if (oldItem != null) { // remove old item from FormulaBox FormulaBox.removeItem(oldItem); } } id = dataId; newItem = newItem.substring(index2 + 1).trim(); } } String varName = DisplayCells[CurX][CurY].addDataSource( id, newItem, type, notify); String itemString = varName + ": " + newItem; FormulaBox.addItem(itemString); FormulaBox.setSelectedItem(itemString); FormulaText.getCaret().setVisible(true); // BIG HAMMER HACK } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage( "Unable to compute data object from \"" + newItem + "\"", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage( "Unable to compute data object from \"" + newItem + "\"", exc, "VisAD SpreadSheet error"); } } }; FormulaEditor.addActionListener(FormulaListener); FormulaText = (JTextField) FormulaEditor.getEditorComponent(); // set up horizontal spreadsheet cell labels JPanel horizShell = new JPanel(); horizShell.setBackground(Color.white); horizShell.setLayout(new BoxLayout(horizShell, BoxLayout.X_AXIS)); horizShell.add(Box.createRigidArea(new Dimension(LABEL_WIDTH + 6, 0))); pane.add(horizShell); HorizPanel = new JPanel() { public Dimension getPreferredSize() { Dimension d = super.getPreferredSize(); return new Dimension(d.width, LABEL_HEIGHT); } }; HorizPanel.setBackground(Color.white); constructHorizontalLabels(); JViewport hl = new JViewport() { public Dimension getMinimumSize() { return new Dimension(0, LABEL_HEIGHT); } public Dimension getPreferredSize() { return new Dimension(0, LABEL_HEIGHT); } public Dimension getMaximumSize() { return new Dimension(Integer.MAX_VALUE, LABEL_HEIGHT); } }; HorizLabels = hl; HorizLabels.setView(HorizPanel); horizShell.add(HorizLabels); horizShell.add(new JComponent() { public Dimension getMinimumSize() { return new Dimension(6 + SCPane.getVScrollbarWidth(), 0); } public Dimension getPreferredSize() { return new Dimension(6 + SCPane.getVScrollbarWidth(), 0); } public Dimension getMaximumSize() { return new Dimension(6 + SCPane.getVScrollbarWidth(), 0); } }); // set up window's main panel JPanel mainPanel = new JPanel(); mainPanel.setBackground(Color.white); mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.X_AXIS)); pane.add(mainPanel); pane.add(Box.createRigidArea(new Dimension(0, 6))); // set up vertical spreadsheet cell labels JPanel vertShell = new JPanel(); vertShell.setBackground(Color.white); vertShell.setLayout(new BoxLayout(vertShell, BoxLayout.Y_AXIS)); mainPanel.add(Box.createRigidArea(new Dimension(6, 0))); mainPanel.add(vertShell); VertPanel = new JPanel() { public Dimension getPreferredSize() { Dimension d = super.getPreferredSize(); return new Dimension(LABEL_WIDTH, d.height); } }; VertPanel.setBackground(Color.white); constructVerticalLabels(); JViewport vl = new JViewport() { public Dimension getMinimumSize() { return new Dimension(LABEL_WIDTH, 0); } public Dimension getPreferredSize() { return new Dimension(LABEL_WIDTH, 0); } public Dimension getMaximumSize() { return new Dimension(LABEL_WIDTH, Integer.MAX_VALUE); } }; VertLabels = vl; VertLabels.setView(VertPanel); vertShell.add(VertLabels); vertShell.add(new JComponent() { public Dimension getMinimumSize() { return new Dimension(0, SCPane.getHScrollbarHeight()); } public Dimension getPreferredSize() { return new Dimension(0, SCPane.getHScrollbarHeight()); } public Dimension getMaximumSize() { return new Dimension(0, SCPane.getHScrollbarHeight()); } }); // set up scroll pane's panel ScrollPanel = new JPanel(); ScrollPanel.setBackground(Color.white); ScrollPanel.setLayout(new BoxLayout(ScrollPanel, BoxLayout.X_AXIS)); mainPanel.add(ScrollPanel); mainPanel.add(Box.createRigidArea(new Dimension(6, 0))); // set up scroll pane for VisAD Displays SCPane = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS) { public Dimension getPreferredSize() { return new Dimension(0, 0); } }; Adjustable hadj = SCPane.getHAdjustable(); Adjustable vadj = SCPane.getVAdjustable(); hadj.setBlockIncrement(MIN_VIS_WIDTH); hadj.setUnitIncrement(MIN_VIS_WIDTH/4); hadj.addAdjustmentListener(this); vadj.setBlockIncrement(MIN_VIS_HEIGHT); vadj.setUnitIncrement(MIN_VIS_HEIGHT/4); vadj.addAdjustmentListener(this); ScrollPanel.add(SCPane); // set up display panel DisplayPanel = new Panel(); DisplayPanel.setBackground(Color.darkGray); SCPane.add(DisplayPanel); // BIG HAMMER HACK String os = System.getProperty("os.name"); if (!os.startsWith("Windows")) { // Windows does not need the hack addKeyListener(this); SCPane.addKeyListener(this); ScrollPanel.addKeyListener(this); DisplayPanel.addKeyListener(this); FormulaBox.addFocusListener(new FocusAdapter() { public void focusGained(FocusEvent e) { SCPane.requestFocus(); } }); } DataReferenceImpl lColRow = null; if (server != null) { // SERVER: initialize boolean success = true; boolean registryStarted = false; while (true) { try { rsi = new RemoteServerImpl(); Naming.rebind("///" + server, rsi); break; } catch (java.rmi.ConnectException exc) { if (!registryStarted) { try { LocateRegistry.createRegistry(Registry.REGISTRY_PORT); registryStarted = true; } catch (RemoteException rexc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Unable to autostart rmiregistry. " + "Please start rmiregistry before launching the " + "SpreadSheet in server mode", null, "Failed to initialize RemoteServer"); success = false; } } else { displayErrorMessage("Unable to export cells as RMI addresses. " + "Make sure you are running rmiregistry before launching the " + "SpreadSheet in server mode", null, "Failed to initialize RemoteServer"); success = false; } } catch (MalformedURLException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Unable to export cells as RMI addresses. " + "The name \"" + server + "\" is not valid", null, "Failed to initialize RemoteServer"); success = false; } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Unable to export cells as RMI addresses", exc, "Failed to initialize RemoteServer"); success = false; } if (!success) break; } // set up info for spreadsheet cloning try { // set flag for whether server has Java3D enabled DataReferenceImpl lCanDo3D = new DataReferenceImpl("CanDo3D"); RemoteCanDo3D = new RemoteDataReferenceImpl(lCanDo3D); RemoteCanDo3D.setData(new Real(CanDo3D ? 1 : 0)); rsi.addDataReference((RemoteDataReferenceImpl) RemoteCanDo3D); // set up remote reference for conveying cell layout information lColRow = new DataReferenceImpl("ColRow"); RemoteColRow = new RemoteDataReferenceImpl(lColRow); rsi.addDataReference((RemoteDataReferenceImpl) RemoteColRow); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Unable to export cells as RMI addresses. " + "An error occurred setting up the necessary data", exc, "Failed to initialize RemoteServer"); success = false; } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Unable to export cells as RMI addresses. " + "A remote error occurred setting up the necessary data", exc, "Failed to initialize RemoteServer"); success = false; } if (success) bTitle = bTitle + " (" + server + ")"; else rsi = null; } // construct spreadsheet cells if (rs == null) constructSpreadsheetCells(null); else { NumVisX = cellNames.length; NumVisY = cellNames[0].length; reconstructLabels(cellNames, null, null); constructSpreadsheetCells(cellNames, rs); } if (rsi != null) synchColRow(); CollabID = DisplayCells[0][0].getRemoteId(); if (rsi != null || IsRemote) { // update spreadsheet when remote row and column information changes final RemoteServer frs = rs; CellImpl lColRowCell = new CellImpl() { public void doAction() { // extract new cell information if (getColRowID() != CollabID) { Util.invoke(true, BasicSSCell.DEBUG, new Runnable() { public void run() { // update is coming from a different sheet String[][] cellNamesx = getNewCellNames(); if (cellNamesx == null) { if (BasicSSCell.DEBUG) System.out.println("Warning: " + "could not obtain new spreadsheet dimensions!"); return; } int oldNVX = NumVisX; int oldNVY = NumVisY; NumVisX = cellNamesx.length; NumVisY = cellNamesx[0].length; if (NumVisX != oldNVX || NumVisY != oldNVY) { // reconstruct spreadsheet cells and labels reconstructSpreadsheet(cellNamesx, null, null, frs); if (!IsRemote) synchColRow(); } } }); } } }; try { RemoteCellImpl rColRowCell = new RemoteCellImpl(lColRowCell); rColRowCell.addReference(RemoteColRow); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Remote cell error (1)", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { try { lColRowCell.addReference(lColRow); } catch (VisADException exc2) { if (BasicSSCell.DEBUG) exc2.printStackTrace(); displayErrorMessage("Remote cell error (2)", exc2, "VisAD SpreadSheet error"); } catch (RemoteException exc2) { if (BasicSSCell.DEBUG) exc2.printStackTrace(); displayErrorMessage("Remote cell error (3)", exc2, "VisAD SpreadSheet error"); } } } // display window on screen setTitle(bTitle); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); int appWidth = (int) (0.01 * sWidth * screenSize.width); int appHeight = (int) (0.01 * sHeight * screenSize.height); setSize(appWidth, appHeight); Util.centerWindow(this); setVisible(true); // wait for frame to lay itself out, then tile cells snooze(500); FormulaText.getCaret().setVisible(true); // BIG HAMMER HACK tileCells(); } // --- FILE MENU --- /** * Imports a data set. */ public void loadDataSet() { DisplayCells[CurX][CurY].loadDataDialog(); } /** * Exports a data set to netCDF format. */ public void exportDataSetNetcdf() { try { exportDataSet(new Plain()); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Error initializing netCDF export", exc, "VisAD SpreadSheet error"); } } /** * Exports a data set to serialized data format. */ public void exportDataSetSerial() { exportDataSet(new VisADForm()); } /** * Exports a data set to HDF-5 format. */ public void exportDataSetHDF5() { Form hdf5form = null; try { ClassLoader cl = ClassLoader.getSystemClassLoader(); Class hdf5form_class = cl.loadClass("visad.data.hdf5.HDF5Form"); hdf5form = (Form) hdf5form_class.newInstance(); } catch (Exception exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Error initializing HDF-5 export", exc, "VisAD SpreadSheet error"); } if (hdf5form != null) exportDataSet(hdf5form); } /** * Exports a data set to TIFF format. */ public void exportDataSetTIFF() { exportDataSet(new TiffForm()); } /** * Exports a data set to VisAD binary data format. */ public void exportDataSetBinary() { exportDataSet(new VisADForm(true)); } /** * Exports a data set using the given form. */ public void exportDataSet(Form form) { String item = (String) FormulaBox.getSelectedItem(); String varName = item.substring(0, item.indexOf(":")); DisplayCells[CurX][CurY].saveDataDialog(varName, form); } /** * Captures the display of the current cell and saves it as a JPEG image. */ public void captureImageJPEG() { DisplayCells[CurX][CurY].captureDialog(); } /** * Does any necessary clean-up, then quits the program. */ public void quitProgram() { // hide frames try { DisplayCells[CurX][CurY].hideWidgetFrame(); } catch (NullPointerException exc) { } setVisible(false); // wait for files to finish saving Thread t = new Thread() { public void run() { boolean b = BasicSSCell.isSaving(); JFrame f = null; if (b) { // display "please wait" message in new frame f = new JFrame("Please wait"); f.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); JPanel p = new JPanel(); f.setContentPane(p); p.setBorder(new EmptyBorder(10, 20, 10, 20)); p.setLayout(new BorderLayout()); p.add("Center", new JLabel("Please wait while the VisAD " + "SpreadSheet finishes saving files...")); f.setResizable(false); f.pack(); Dimension sSize = Toolkit.getDefaultToolkit().getScreenSize(); Dimension fSize = f.getSize(); f.setLocation(sSize.width/2 - fSize.width/2, sSize.height/2 - fSize.height/2); f.setVisible(true); } while (BasicSSCell.isSaving()) snooze(200); if (b) { f.setCursor(Cursor.getDefaultCursor()); f.setVisible(false); } // destroy all spreadsheet cells intelligently and cleanly boolean[][] alive = new boolean[NumVisX][NumVisY]; for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) alive[i][j] = true; } int aliveCount = NumVisX * NumVisY; while (aliveCount > 0) { for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) { if (alive[i][j] && !DisplayCells[i][j].othersDepend()) { try { DisplayCells[i][j].destroyCell(); alive[i][j] = false; aliveCount--; } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot destroy cell (1)", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot destroy cell (2)", exc, "VisAD SpreadSheet error"); } } } } } System.exit(0); } }; t.start(); } // --- EDIT MENU --- /** * Moves a cell from the screen to the clipboard. */ public void cutCell() { if (DisplayCells[CurX][CurY].confirmClear()) { copyCell(); clearCell(false); } } /** * Copies a cell from the screen to the clipboard. */ public void copyCell() { Clipboard = DisplayCells[CurX][CurY].getSaveString(); EditPaste.setEnabled(true); if (!BugFix) ToolPaste.setEnabled(true); } /** * Copies a cell from the clipboard to the screen. */ public void pasteCell() { if (Clipboard != null) { try { boolean b = DisplayCells[CurX][CurY].getAutoDetect(); DisplayCells[CurX][CurY].setAutoDetect(false); DisplayCells[CurX][CurY].setSaveString(Clipboard); DisplayCells[CurX][CurY].setAutoDetect(b); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot paste cell", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot paste cell", exc, "VisAD SpreadSheet error"); } } } /** * Clears the mappings and formula of the current cell * if it is safe to do so, or if the user confirms the clear. */ public void clearCell() { clearCell(true); } /** * Clears the mappings and formula of the current cell. */ protected void clearCell(boolean checkSafe) { try { if (checkSafe) DisplayCells[CurX][CurY].smartClear(); else DisplayCells[CurX][CurY].clearCell(); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot clear display mappings", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot clear display mappings", exc, "VisAD SpreadSheet error"); } refreshFormulaBar(); refreshMenuCommands(); } // --- SETUP MENU --- /** * Creates a new spreadsheet file, asking user to confirm first. * @return true if successful. */ public boolean newFile() { return newFile(true); } /** * Creates a new spreadsheet file. * @return true if successful. */ protected boolean newFile(boolean safe) { if (safe) { int ans = JOptionPane.showConfirmDialog(this, "Clear all spreadsheet cells?", "Are you sure?", JOptionPane.YES_NO_OPTION); if (ans != JOptionPane.YES_OPTION) return false; } // hide control widgets DisplayCells[CurX][CurY].hideWidgetFrame(); // clear all cells (in smart order to prevent errors) boolean[][] dirty = new boolean[NumVisX][NumVisY]; for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) dirty[i][j] = true; } int dirtyCount = NumVisX * NumVisY; while (dirtyCount > 0) { for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) { if (dirty[i][j] && !DisplayCells[i][j].othersDepend()) { try { DisplayCells[i][j].clearCell(); dirty[i][j] = false; dirtyCount--; } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot clear cell (1)", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot clear cell (2)", exc, "VisAD SpreadSheet error"); } } } } } CurrentFile = null; setTitle(bTitle); return true; } /** * Opens an existing spreadsheet file chosen by the user. */ public void openFile() { SSFileDialog.setDialogType(JFileChooser.OPEN_DIALOG); if (SSFileDialog.showOpenDialog(this) != JFileChooser.APPROVE_OPTION) { // user has canceled request return; } // make sure file exists File f = SSFileDialog.getSelectedFile(); if (!f.exists()) { displayErrorMessage("The file " + f.getName() + " does not exist", null, "VisAD SpreadSheet error"); return; } openFile(f.getPath()); } /** * Opens the specified spreadsheet file. */ public void openFile(String file) { File f = new File(file); // disable auto-switch, auto-detect and auto-show boolean origSwitch = AutoSwitch; boolean origDetect = AutoDetect; boolean origShow = AutoShowControls; setAutoSwitch(false); setAutoDetect(false); setAutoShowControls(false); // clear all cells newFile(false); // load entire file into buffer int fLen = (int) f.length(); char[] buff = new char[fLen]; try { FileReader fr = new FileReader(f); fr.read(buff, 0, fLen); fr.close(); } catch (IOException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Unable to read the file " + file + " from disk", null, "VisAD SpreadSheet error"); // reset auto-switch, auto-detect and auto-show setAutoSwitch(origSwitch); setAutoDetect(origDetect); setAutoShowControls(origShow); return; } // cut buffer up into lines StringTokenizer st = new StringTokenizer(new String(buff), "\n\r"); int numTokens = st.countTokens(); String[] tokens = new String[numTokens + 1]; for (int i=0; i<numTokens; i++) tokens[i] = st.nextToken(); tokens[numTokens] = null; st = null; // get global information int sizeX = -1, sizeY = -1; int dimX = -1, dimY = -1; Vector colWidths = new Vector(), rowHeights = new Vector(); boolean autoSwitch = true, autoDetect = true, autoShow = true; int tokenNum = 0; String line; int len; int eq; boolean gotGlobal = false; while (!gotGlobal) { line = tokens[tokenNum++]; if (line == null) { displayErrorMessage("The file " + file + " does not contain " + "the required [Global] tag", null, "VisAD SpreadSheet error"); // reset auto-switch, auto-detect and auto-show setAutoSwitch(origSwitch); setAutoDetect(origDetect); setAutoShowControls(origShow); return; } len = line.length(); String trimLine = line.trim(); int trimLen = trimLine.length(); if (trimLine.charAt(0) == '[' && trimLine.charAt(trimLen - 1) == ']') { String sub = trimLine.substring(1, trimLen - 1).trim(); if (!sub.equalsIgnoreCase("global")) { displayErrorMessage("The file " + file + " does not contain the " + "[Global] tag as its first entry", null, "VisAD SpreadSheet error"); // reset auto-switch, auto-detect and auto-show setAutoSwitch(origSwitch); setAutoDetect(origDetect); setAutoShowControls(origShow); return; } // parse global information int endToken = tokenNum; while (tokens[endToken] != null && tokens[endToken].trim().indexOf("[") != 0) { endToken++; } for (int i=tokenNum; i<endToken; i++) { line = tokens[i].trim(); if (line == null) continue; if (line.charAt(0) == '#') { // ignore comments continue; } eq = line.indexOf("="); if (eq < 0) { // ignore worthless lines continue; } String keyword = line.substring(0, eq).trim(); // sheet size if (keyword.equalsIgnoreCase("sheet size") || keyword.equalsIgnoreCase("sheet_size") || keyword.equalsIgnoreCase("sheetsize") || keyword.equalsIgnoreCase("size")) { int x = line.indexOf("x", eq); if (x >= 0) { String sX = line.substring(eq + 1, x).trim(); String sY = line.substring(x + 1).trim(); try { sizeX = Integer.parseInt(sX); sizeY = Integer.parseInt(sY); } catch (NumberFormatException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } } } // cell dimension if (keyword.equalsIgnoreCase("dimension") || keyword.equalsIgnoreCase("dimensions") || keyword.equalsIgnoreCase("dim")) { int x = line.indexOf("x", eq); if (x >= 0) { String sX = line.substring(eq + 1, x).trim(); String sY = line.substring(x + 1).trim(); try { dimX = Integer.parseInt(sX); dimY = Integer.parseInt(sY); } catch (NumberFormatException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } } } // column widths if (keyword.equalsIgnoreCase("columns") || keyword.equalsIgnoreCase("column") || keyword.equalsIgnoreCase("column widths") || keyword.equalsIgnoreCase("column_widths") || keyword.equalsIgnoreCase("columnwidths") || keyword.equalsIgnoreCase("column width") || keyword.equalsIgnoreCase("column_width") || keyword.equalsIgnoreCase("columnwidth")) { StringTokenizer ln = new StringTokenizer(line.substring(eq + 1)); int nt = ln.countTokens(); for (int z=0; z<nt; z++) { int cw = 0; try { cw = Integer.parseInt(ln.nextToken()); } catch (NumberFormatException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } if (cw >= MIN_VIS_WIDTH) colWidths.add(new Integer(cw)); } } // rows heights if (keyword.equalsIgnoreCase("rows") || keyword.equalsIgnoreCase("row") || keyword.equalsIgnoreCase("row heights") || keyword.equalsIgnoreCase("row_heights") || keyword.equalsIgnoreCase("rowheights") || keyword.equalsIgnoreCase("row height") || keyword.equalsIgnoreCase("row_height") || keyword.equalsIgnoreCase("rowheight")) { StringTokenizer ln = new StringTokenizer(line.substring(eq + 1)); int nt = ln.countTokens(); for (int z=0; z<nt; z++) { int rh = 0; try { rh = Integer.parseInt(ln.nextToken()); } catch (NumberFormatException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } if (rh >= MIN_VIS_WIDTH) rowHeights.add(new Integer(rh)); } } // auto switch if (keyword.equalsIgnoreCase("auto switch") || keyword.equalsIgnoreCase("auto_switch") || keyword.equalsIgnoreCase("auto-switch") || keyword.equalsIgnoreCase("autoswitch")) { String val = line.substring(eq + 1).trim(); if (val.equalsIgnoreCase("false") || val.equalsIgnoreCase("F")) { autoSwitch = false; } } // auto detect if (keyword.equalsIgnoreCase("auto detect") || keyword.equalsIgnoreCase("auto_detect") || keyword.equalsIgnoreCase("auto-detect") || keyword.equalsIgnoreCase("autodetect")) { String val = line.substring(eq + 1).trim(); if (val.equalsIgnoreCase("false") || val.equalsIgnoreCase("F")) { autoDetect = false; } } // auto show if (keyword.equalsIgnoreCase("auto show") || keyword.equalsIgnoreCase("auto_show") || keyword.equalsIgnoreCase("auto-show") || keyword.equalsIgnoreCase("autoshow")) { String val = line.substring(eq + 1).trim(); if (val.equalsIgnoreCase("false") || val.equalsIgnoreCase("F")) { autoShow = false; } } } gotGlobal = true; } } // make sure cell dimensions are valid if (dimX < 1 || dimY < 1) { displayErrorMessage("The file " + file + " has an invalid " + "global dimension entry", null, "VisAD SpreadSheet error"); // reset auto-switch, auto-detect and auto-show setAutoSwitch(origSwitch); setAutoDetect(origDetect); setAutoShowControls(origShow); return; } // create label width and height arrays len = colWidths.size(); int[] cw = new int[dimX]; for (int i=0; i<dimX; i++) { if (i < len) cw[i] = ((Integer) colWidths.elementAt(i)).intValue(); else cw[i] = DEFAULT_VIS_WIDTH; } len = rowHeights.size(); int[] rh = new int[dimY]; for (int i=0; i<dimY; i++) { if (i < len) rh[i] = ((Integer) rowHeights.elementAt(i)).intValue(); else rh[i] = DEFAULT_VIS_HEIGHT; } // examine each cell entry String[][] cellNames = new String[dimX][dimY]; String[][] fileStrings = new String[dimX][dimY]; for (int j=0; j<dimY; j++) { for (int i=0; i<dimX; i++) { // find next cell name cellNames[i][j] = null; do { line = tokens[tokenNum++]; if (line == null) { displayErrorMessage("The file " + file + " is incomplete", null, "VisAD SpreadSheet error"); // reset auto-switch, auto-detect and auto-show setAutoSwitch(origSwitch); setAutoDetect(origDetect); setAutoShowControls(origShow); return; } String trimLine = line.trim(); int trimLen = trimLine.length(); if (trimLine.charAt(0) == '[' && trimLine.charAt(trimLen - 1) == ']') { // this line identifies a cell name cellNames[i][j] = trimLine.substring(1, trimLen - 1).trim(); } } while (cellNames[i][j] == null); // find last line of this cell's save string int last = tokenNum + 1; while (tokens[last] != null && tokens[last].trim().indexOf("[") != 0) { last++; } // build this cell's save string String s = ""; for (int l=tokenNum; l<last; l++) s = s + tokens[l] + "\n"; fileStrings[i][j] = s; } } // resize the sheet to the correct size if (sizeX > 0 && sizeY > 0) setSize(sizeX, sizeY); // reconstruct spreadsheet cells and labels NumVisX = dimX; NumVisY = dimY; reconstructSpreadsheet(cellNames, cw, rh, null); synchColRow(); // set each cell's string for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) { try { DisplayCells[i][j].setSaveString(fileStrings[i][j]); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Invalid save string", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Invalid save string", exc, "VisAD SpreadSheet error"); } } } // set auto-switch, auto-detect and auto-show setAutoSwitch(autoSwitch); setAutoDetect(autoDetect); setAutoShowControls(autoShow); // update current file and title CurrentFile = f; setTitle(bTitle + " - " + f.getPath()); // refresh GUI components refreshDisplayMenuItems(); refreshFormulaBar(); refreshMenuCommands(); refreshOptions(); validate(); repaint(); } /** * Saves a spreadsheet file under its current name. */ public void saveFile() { if (CurrentFile == null) saveAsFile(); else { // construct file header StringBuffer sb = new StringBuffer(1024 * NumVisX * NumVisY + 1024); sb.append(SSFileHeader); sb.append("\n"); sb.append("# File "); sb.append(CurrentFile.getName()); sb.append(" written at "); sb.append(Util.getTimestamp()); sb.append("\n\n"); // compile global information sb.append("[Global]\n"); sb.append("sheet size = "); sb.append(getWidth()); sb.append(" x "); sb.append(getHeight()); sb.append("\n"); sb.append("dimension = "); sb.append(NumVisX); sb.append(" x "); sb.append(NumVisY); sb.append("\n"); sb.append("columns ="); for (int j=0; j<NumVisX; j++) { sb.append(" "); sb.append(HorizLabel[j].getSize().width); } sb.append("\n"); sb.append("rows ="); for (int i=0; i<NumVisY; i++) { sb.append(" "); sb.append(VertLabel[i].getSize().height); } sb.append("\n"); sb.append("auto switch = "); sb.append(AutoSwitch); sb.append("\n"); sb.append("auto detect = "); sb.append(AutoDetect); sb.append("\n"); sb.append("auto show = "); sb.append(AutoShowControls); sb.append("\n\n"); // compile cell information for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) { sb.append("["); sb.append(DisplayCells[i][j].getName()); sb.append("]\n"); sb.append(DisplayCells[i][j].getSaveString()); sb.append("\n"); } } // convert information to a character array char[] sc = sb.toString().toCharArray(); try { // write file to disk FileWriter fw = new FileWriter(CurrentFile); fw.write(sc, 0, sc.length); fw.close(); } catch (IOException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Could not save file " + CurrentFile.getName() + ". Make sure there is enough disk space", null, "VisAD SpreadSheet error"); } } } /** * Saves a spreadsheet file under a new name. */ public void saveAsFile() { SSFileDialog.setDialogType(JFileChooser.SAVE_DIALOG); if (SSFileDialog.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) { // user has canceled request return; } // get file and make sure it is valid File f = SSFileDialog.getSelectedFile(); CurrentFile = f; setTitle(bTitle + " - " + f.getPath()); saveFile(); } // --- CELL MENU --- /** * Sets the dimension of the current cell to 3-D (Java3D). */ public void setDim3D() { setDim(BasicSSCell.JAVA3D_3D); } /** * Sets the dimension of the current cell to 2-D (Java2D). */ public void setDimJ2D() { setDim(BasicSSCell.JAVA2D_2D); } /** * Sets the dimension of the current cell to 2-D (Java3D). */ public void setDim2D() { setDim(BasicSSCell.JAVA3D_2D); } /** * Sets the dimension of the current cell. */ protected void setDim(int dim) { try { DisplayCells[CurX][CurY].setDimension(dim); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot alter display dimension", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot alter display dimension", exc, "VisAD SpreadSheet error"); } refreshDisplayMenuItems(); } /** * Creates a hardcopy of the current spreadsheet cell. */ public void printCurrentCell() { if (!DisplayCells[CurX][CurY].hasData()) { displayErrorMessage("The current cell contains no data to be printed", null, "VisAD SpreadSheet error"); return; } final PrinterJob printJob = PrinterJob.getPrinterJob(); DisplayImpl display = DisplayCells[CurX][CurY].getDisplay(); Printable p = display.getPrintable(); printJob.setPrintable(p); if (printJob.printDialog()) { Runnable printImage = new Runnable() { public void run() { try { printJob.print(); } catch (Exception exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot print the current cell", exc, "VisAD SpreadSheet error"); } } }; Thread t = new Thread(printImage); t.start(); } } /** * Specifies mappings from Data to Display. */ public void createMappings() { DisplayCells[CurX][CurY].addMapDialog(); refreshMenuCommands(); } private static double[] matrix3D = {0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 1}; private static double[] matrix2D = {0.65, 0, 0, 0, 0, 0.65, 0, 0, 0, 0, 0.65, 0, 0, 0, 0, 1}; private static double[] matrixJ2D = {1, 0, 0, -1, 0, 0}; /** * Resets the display projection to its original value. */ public void resetOrientation() { DisplayImpl display = DisplayCells[CurX][CurY].getDisplay(); if (display != null) { ProjectionControl pc = display.getProjectionControl(); if (pc != null) { int dim = DisplayCells[CurX][CurY].getDimension(); double[] matrix; if (dim == 1) { // 3-D (Java3D) matrix = matrix3D; } else if (dim == 2) { // 2-D (Java2D) matrix = matrixJ2D; } else { // 2-D (Java3D) matrix = matrix2D; } try { pc.setMatrix(matrix); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot reset orientation", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot reset orientation", exc, "VisAD SpreadSheet error"); } } } } /** * Displays the controls for the currently selected cell. */ public void showControls() { DisplayCells[CurX][CurY].showWidgetFrame(); } // --- DISPLAY MENU --- /** * Adds a column to the spreadsheet. */ public synchronized void addColumn() { JLabel l = (JLabel) HorizLabel[NumVisX - 1].getComponent(0); int maxVisX = Letters.indexOf(l.getText()) + 1; int diffX = Letters.length() - maxVisX; if (diffX > 0) { // re-layout horizontal spreadsheet labels JPanel[] newLabels = new JPanel[NumVisX + 1]; for (int i=0; i<NumVisX; i++) newLabels[i] = HorizLabel[i]; newLabels[NumVisX] = new JPanel(); newLabels[NumVisX].setBorder(new LineBorder(Color.black, 1)); newLabels[NumVisX].setLayout(new BorderLayout()); newLabels[NumVisX].setPreferredSize( new Dimension(DEFAULT_VIS_WIDTH, LABEL_HEIGHT)); String s = String.valueOf(Letters.charAt(maxVisX)); newLabels[NumVisX].add("Center", new JLabel(s, SwingConstants.CENTER)); if (IsRemote) { // let the server handle the actual cell layout HorizLabel = newLabels; synchColRow(); } else { // re-layout horizontal label separators JComponent[] newDrag = new JComponent[NumVisX + 1]; for (int i=0; i<NumVisX; i++) newDrag[i] = HorizDrag[i]; newDrag[NumVisX] = new JComponent() { public void paint(Graphics g) { Dimension d = this.getSize(); g.setColor(Color.black); g.drawRect(0, 0, d.width - 1, d.height - 1); g.setColor(Color.yellow); g.fillRect(1, 1, d.width - 2, d.height - 2); } }; newDrag[NumVisX].setPreferredSize(new Dimension(5, 0)); newDrag[NumVisX].addMouseListener(this); newDrag[NumVisX].addMouseMotionListener(this); HorizPanel.removeAll(); // re-layout spreadsheet cells FancySSCell[][] fcells = new FancySSCell[NumVisX + 1][NumVisY]; DisplayPanel.removeAll(); for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) fcells[i][j] = DisplayCells[i][j]; try { String name = String.valueOf(Letters.charAt(maxVisX)) + String.valueOf(j + 1); FancySSCell f = createCell(name, null); f.addSSCellListener(this); f.addMouseListener(this); f.setAutoSwitch(AutoSwitch); f.setAutoDetect(AutoDetect); f.setAutoShowControls(AutoShowControls); f.setDimension(CanDo3D ? BasicSSCell.JAVA3D_3D : BasicSSCell.JAVA2D_2D); f.addDisplayListener(this); f.setPreferredSize( new Dimension(DEFAULT_VIS_WIDTH, DEFAULT_VIS_HEIGHT)); fcells[NumVisX][j] = f; if (rsi != null) { // add new cell to server fcells[NumVisX][j].addToRemoteServer(rsi); } } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot add the column. Unable to create " + "new displays", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot add the column. A remote error " + "occurred", exc, "VisAD SpreadSheet error"); } } NumVisX++; reconstructHoriz(newLabels, newDrag, fcells); if (diffX == 1) LayAddCol.setEnabled(false); LayDelCol.setEnabled(true); } } } /** * Adds a row to the spreadsheet. */ public void addRow() { JLabel l = (JLabel) VertLabel[NumVisY - 1].getComponent(0); int maxVisY = Integer.parseInt(l.getText()); // re-layout vertical spreadsheet labels JPanel[] newLabels = new JPanel[NumVisY + 1]; JComponent[] newDrag = new JComponent[NumVisY + 1]; for (int i=0; i<NumVisY; i++) newLabels[i] = VertLabel[i]; newLabels[NumVisY] = new JPanel(); newLabels[NumVisY].setBorder(new LineBorder(Color.black, 1)); newLabels[NumVisY].setLayout(new BorderLayout()); newLabels[NumVisY].setPreferredSize( new Dimension(LABEL_WIDTH, DEFAULT_VIS_HEIGHT)); String s = String.valueOf(maxVisY + 1); newLabels[NumVisY].add("Center", new JLabel(s, SwingConstants.CENTER)); if (IsRemote) { // let server handle the actual cell layout VertLabel = newLabels; synchColRow(); } else { // re-layout vertical label separators for (int i=0; i<NumVisY; i++) newDrag[i] = VertDrag[i]; newDrag[NumVisY] = new JComponent() { public void paint(Graphics g) { Dimension d = this.getSize(); g.setColor(Color.black); g.drawRect(0, 0, d.width - 1, d.height - 1); g.setColor(Color.yellow); g.fillRect(1, 1, d.width - 2, d.height - 2); } }; newDrag[NumVisY].setPreferredSize(new Dimension(0, 5)); newDrag[NumVisY].addMouseListener(this); newDrag[NumVisY].addMouseMotionListener(this); VertPanel.removeAll(); // re-layout spreadsheet cells FancySSCell[][] fcells = new FancySSCell[NumVisX][NumVisY + 1]; DisplayPanel.removeAll(); for (int i=0; i<NumVisX; i++) { for (int j=0; j<NumVisY; j++) fcells[i][j] = DisplayCells[i][j]; try { String name = String.valueOf(Letters.charAt(i)) + String.valueOf(maxVisY + 1); FancySSCell f = createCell(name, null); f.addSSCellListener(this); f.addMouseListener(this); f.setAutoSwitch(AutoSwitch); f.setAutoDetect(AutoDetect); f.setAutoShowControls(AutoShowControls); f.setDimension(CanDo3D ? BasicSSCell.JAVA3D_3D : BasicSSCell.JAVA2D_2D); f.addDisplayListener(this); f.setPreferredSize( new Dimension(DEFAULT_VIS_WIDTH, DEFAULT_VIS_HEIGHT)); fcells[i][NumVisY] = f; if (rsi != null) { // add new cell to server fcells[i][NumVisY].addToRemoteServer(rsi); } } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot add the row. Unable to create new " + "displays", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot add the row. A remote error occurred", exc, "VisAD SpreadSheet error"); } } NumVisY++; reconstructVert(newLabels, newDrag, fcells); LayDelRow.setEnabled(true); } } /** * Deletes a column from the spreadsheet. */ public synchronized boolean deleteColumn() { // make sure at least one column will be left if (NumVisX == 1) { displayErrorMessage("This is the last column", null, "Cannot delete column"); return false; } // make sure no cells are dependent on columns about to be deleted for (int j=0; j<NumVisY; j++) { if (DisplayCells[CurX][j].othersDepend()) { displayErrorMessage("Other cells depend on cells from this " + "column. Make sure that no cells depend on this column before " + "attempting to delete it", null, "Cannot delete column"); return false; } } // get column letter to be deleted JLabel label = (JLabel) HorizLabel[CurX].getComponent(0); char letter = label.getText().charAt(0); char last = Letters.charAt(Letters.length() - 1); // re-layout horizontal spreadsheet labels JPanel[] newLabels = new JPanel[NumVisX - 1]; for (int i=0; i<CurX; i++) newLabels[i] = HorizLabel[i]; for (int i=CurX+1; i<NumVisX; i++) newLabels[i - 1] = HorizLabel[i]; if (IsRemote) { // let server handle the actual cell layout HorizLabel = newLabels; synchColRow(); } else { // re-layout horizontal label separators JComponent[] newDrag = new JComponent[NumVisX - 1]; for (int i=0; i<NumVisX-1; i++) newDrag[i] = HorizDrag[i]; HorizPanel.removeAll(); // re-layout spreadsheet cells FancySSCell[][] fcells = new FancySSCell[NumVisX - 1][NumVisY]; DisplayPanel.removeAll(); for (int j=0; j<NumVisY; j++) { for (int i=0; i<CurX; i++) fcells[i][j] = DisplayCells[i][j]; for (int i=CurX+1; i<NumVisX; i++) { fcells[i - 1][j] = DisplayCells[i][j]; } try { DisplayCells[CurX][j].destroyCell(); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot destroy cell (3)", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot destroy cell (4)", exc, "VisAD SpreadSheet error"); } DisplayCells[CurX][j] = null; } NumVisX--; if (CurX > NumVisX - 1) selectCell(NumVisX - 1, CurY); reconstructHoriz(newLabels, newDrag, fcells); if (letter == last) LayAddCol.setEnabled(true); if (NumVisX == 1) LayDelCol.setEnabled(false); } return true; } /** * Deletes a row from the spreadsheet. */ public synchronized boolean deleteRow() { // make sure at least one row will be left if (NumVisY == 1) { displayErrorMessage("This is the last row", null, "Cannot delete row"); return false; } // make sure no cells are dependent on rows about to be deleted for (int i=0; i<NumVisX; i++) { if (DisplayCells[i][CurY].othersDepend()) { displayErrorMessage("Other cells depend on cells from this row. " + "Make sure that no cells depend on this row before attempting " + "to delete it", null, "Cannot delete row"); return false; } } // re-layout vertical spreadsheet labels JPanel[] newLabels = new JPanel[NumVisY - 1]; for (int i=0; i<CurY; i++) newLabels[i] = VertLabel[i]; for (int i=CurY+1; i<NumVisY; i++) newLabels[i - 1] = VertLabel[i]; if (IsRemote) { // let server handle the actual cell layout VertLabel = newLabels; synchColRow(); } else { // re-layout horizontal label separators JComponent[] newDrag = new JComponent[NumVisY]; for (int i=0; i<NumVisY; i++) newDrag[i] = VertDrag[i]; VertPanel.removeAll(); // re-layout spreadsheet cells FancySSCell[][] fcells = new FancySSCell[NumVisX][NumVisY - 1]; DisplayPanel.removeAll(); for (int i=0; i<NumVisX; i++) { for (int j=0; j<CurY; j++) fcells[i][j] = DisplayCells[i][j]; for (int j=CurY+1; j<NumVisY; j++) { fcells[i][j - 1] = DisplayCells[i][j]; } try { DisplayCells[i][CurY].destroyCell(); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot destroy cell (5)", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot destroy cell (6)", exc, "VisAD SpreadSheet error"); } DisplayCells[i][CurY] = null; } NumVisY--; if (CurY > NumVisY - 1) selectCell(CurX, NumVisY - 1); reconstructVert(newLabels, newDrag, fcells); if (NumVisY == 1) LayDelRow.setEnabled(false); } return true; } /** * Resizes all cells to exactly fill the entire pane, if possible. */ public void tileCells() { Dimension paneSize = SCPane.getSize(); int w = paneSize.width - SCPane.getVScrollbarWidth() - 5 * NumVisX; int h = paneSize.height - SCPane.getHScrollbarHeight() - 5 * NumVisY; int wx = w / NumVisX; int hy = h / NumVisY; if (wx < MIN_VIS_WIDTH) wx = MIN_VIS_WIDTH; if (hy < MIN_VIS_HEIGHT) hy = MIN_VIS_HEIGHT; Dimension hSize = new Dimension(wx, LABEL_HEIGHT); Dimension vSize = new Dimension(LABEL_WIDTH, hy); for (int i=0; i<NumVisX; i++) { HorizLabel[i].setSize(hSize); HorizLabel[i].setPreferredSize(hSize); } for (int j=0; j<NumVisY; j++) { VertLabel[j].setSize(vSize); VertLabel[j].setPreferredSize(vSize); } synchLabelAndCellSizes(); } // --- OPTIONS MENU --- /** * Sets auto-dimension switching to match Auto-switch menu item state. */ public void optionsSwitch() { setAutoSwitch(AutoSwitchBox.getState()); } /** * Sets mapping auto-detection to match Auto-detect menu item state. */ public void optionsDetect() { setAutoDetect(AutoDetectBox.getState()); } /** * Sets auto-display of controls to match Auto-display menu item state. */ public void optionsDisplay() { setAutoShowControls(AutoShowBox.getState()); } /** * Toggles auto-dimension switching. */ protected void setAutoSwitch(boolean b) { for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) DisplayCells[i][j].setAutoSwitch(b); } AutoSwitch = b; } /** * Toggles mapping auto-detection. */ protected void setAutoDetect(boolean b) { for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) DisplayCells[i][j].setAutoDetect(b); } AutoDetect = b; } /** * Toggles auto-display of controls. */ protected void setAutoShowControls(boolean b) { for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) DisplayCells[i][j].setAutoShowControls(b); } AutoShowControls = b; } // --- FORMULA BAR BUTTONS --- /** * Prompts the user to type a source for a new data object * for the current cell. */ public void formulaAdd() { FormulaEditor.setItem(""); FormulaEditor.selectAll(); } /** * Deletes the selected data object from the current cell. */ public void formulaDel() { String item = (String) FormulaBox.getSelectedItem(); if (item == null) return; int index = item.indexOf(":"); if (index < 0) return; String varName = item.substring(0, index); try { DisplayCells[CurX][CurY].removeData(varName); FormulaBox.removeItem(item); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot delete data " + varName, exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot delete data " + varName, exc, "VisAD SpreadSheet error"); } } // --- GUI MANAGEMENT --- /** * Refreshes spreadsheet cells. */ protected void refreshCells() { Util.invoke(false, BasicSSCell.DEBUG, new Runnable() { public void run() { for (int i=0; i<NumVisX; i++) { for (int j=0; j<NumVisY; j++) DisplayCells[i][j].refresh(); } } }); } /** * Refreshes check box items in the Options menu. */ protected void refreshOptions() { Util.invoke(false, BasicSSCell.DEBUG, new Runnable() { public void run() { AutoSwitchBox.setState(AutoSwitch); AutoDetectBox.setState(AutoDetect); AutoShowBox.setState(AutoShowControls); } }); } /** * Refreshes the "Show controls" menu option and toolbar button. */ protected void refreshShowControls() { Util.invoke(false, BasicSSCell.DEBUG, new Runnable() { public void run() { boolean b = DisplayCells[CurX][CurY].hasControls(); CellShow.setEnabled(b); if (!BugFix) ToolShow.setEnabled(b); } }); } /** * Enables or disables certain menu items * depending on whether this cell has data. */ protected void refreshMenuCommands() { Util.invoke(false, BasicSSCell.DEBUG, new Runnable() { public void run() { boolean b = DisplayCells[CurX][CurY].hasData(); FileExport.setEnabled(b); FileSnap.setEnabled(b && CanDoJPEG); EditClear.setEnabled(b); CellPrint.setEnabled(b); CellEdit.setEnabled(b); CellReset.setEnabled(b && !IsSlave); if (!BugFix) { ToolSave.setEnabled(b); ToolMap.setEnabled(b); ToolReset.setEnabled(b && !IsSlave); } refreshShowControls(); } }); } /** * Makes sure the formula bar is displaying up-to-date info. */ protected void refreshFormulaBar() { Util.invoke(false, BasicSSCell.DEBUG, new Runnable() { public void run() { if (FormulaBox.getItemCount() > 0) FormulaBox.removeAllItems(); String[] varNames = DisplayCells[CurX][CurY].getVariableNames(); int len = varNames.length; for (int i=0; i<len; i++) { String varName = varNames[i]; String source = DisplayCells[CurX][CurY].getDataSource(varName); FormulaBox.addItem(varName + ": " + source); } boolean b = len > 0; CellDel.setEnabled(b); if (!BugFix) FormulaDel.setEnabled(b); } }); } /** * Updates dimension checkbox menu items and toolbar buttons. */ protected void refreshDisplayMenuItems() { Util.invoke(false, BasicSSCell.DEBUG, new Runnable() { public void run() { // update dimension check marks int dim = DisplayCells[CurX][CurY].getDimension(); boolean j3d3d = (dim == BasicSSCell.JAVA3D_3D); boolean j2d2d = (dim == BasicSSCell.JAVA2D_2D); boolean j3d2d = (dim == BasicSSCell.JAVA3D_2D); CellDim3D3D.setState(j3d3d); CellDim2D2D.setState(j2d2d); CellDim2D3D.setState(j3d2d); if (!BugFix) { Tool3D.setEnabled(!j3d3d && CanDo3D); Tool2D.setEnabled(!j3d2d && CanDo3D); ToolJ2D.setEnabled(!j2d2d); } } }); } // --- COLLABORATION --- /** * Determines whether or not the last remote event was from the server. */ private double getColRowID() { TupleIface t = null; Real id = null; try { t = (TupleIface) RemoteColRow.getData(); id = (Real) t.getComponent(0); return id.getValue(); } catch (NullPointerException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } return Float.NaN; } /** * Gets the latest remote row and column information. */ private String[][] getNewCellNames() { // extract new row and column information TupleIface t = null; TupleIface tc = null; RealTuple tr = null; try { t = (TupleIface) RemoteColRow.getData(); tc = (TupleIface) t.getComponent(1); tr = (RealTuple) t.getComponent(2); } catch (NullPointerException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } if (tc == null || tr == null) return null; int collen = -1; int rowlen = -1; try { collen = tc.getDimension(); rowlen = tr.getDimension(); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } if (rowlen < 1 || collen < 1) return null; String[] colNames = new String[collen]; int[] rowNames = new int[rowlen]; String[][] cellNames = new String[collen][rowlen]; for (int i=0; i<collen; i++) { Text txt = null; try { txt = (Text) tc.getComponent(i); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } if (txt == null) return null; colNames[i] = txt.getValue(); } for (int j=0; j<rowlen; j++) { Real r = null; try { r = (Real) tr.getComponent(j); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } if (r == null) return null; rowNames[j] = (int) r.getValue(); } for (int i=0; i<collen; i++) { for (int j=0; j<rowlen; j++) { cellNames[i][j] = colNames[i] + rowNames[j]; } } return cellNames; } /** * Updates the remote row and column information. */ private void synchColRow() { if (RemoteColRow != null) { synchronized (RemoteColRow) { int xlen = HorizLabel.length; int ylen = VertLabel.length; try { MathType[] m = new MathType[3]; Real id = new Real(CollabID); m[0] = id.getType(); Text[] txt = new Text[xlen]; TextType[] tt = new TextType[xlen]; for (int i=0; i<xlen; i++) { String s = ((JLabel) HorizLabel[i].getComponent(0)).getText(); txt[i] = new Text(s); tt[i] = (TextType) txt[i].getType(); } m[1] = new TupleType(tt); TupleIface tc = new Tuple((TupleType) m[1], txt); Real[] r = new Real[ylen]; for (int j=0; j<ylen; j++) { String s = ((JLabel) VertLabel[j].getComponent(0)).getText(); r[j] = new Real(Integer.parseInt(s)); } RealTuple tr = new RealTuple(r); m[2] = tr.getType(); TupleIface t = new Tuple(new TupleType(m), new Data[] {id, tc, tr}); RemoteColRow.setData(t); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } } } } // --- CELL & LABEL CONSTRUCTION --- /** * Ensures that the cells' preferred sizes match those of the labels. */ private void synchLabelAndCellSizes() { // resize spreadsheet cells for (int j=0; j<NumVisY; j++) { int h = VertLabel[j].getSize().height; for (int i=0; i<NumVisX; i++) { int w = HorizLabel[i].getSize().width; DisplayCells[i][j].setPreferredSize(new Dimension(w, h)); } } // refresh display HorizLabels.validate(); VertLabels.validate(); DisplayPanel.doLayout(); SCPane.validate(); refreshCells(); } private void constructSpreadsheetCells(RemoteServer rs) { String[][] labels = new String[NumVisX][NumVisY]; for (int i=0; i<NumVisX; i++) { for (int j=0; j<NumVisY; j++) { labels[i][j] = "" + Letters.charAt(i) + (j + 1); } } constructSpreadsheetCells(labels, rs); } private void constructSpreadsheetCells(String[][] l, RemoteServer rs) { synchronized (Lock) { DisplayPanel.setLayout(new SSLayout(NumVisX, NumVisY, 5, 5)); DisplayCells = new FancySSCell[NumVisX][NumVisY]; for (int j=0; j<NumVisY; j++) { int ph = VertLabel[j].getPreferredSize().height; for (int i=0; i<NumVisX; i++) { int pw = HorizLabel[i].getPreferredSize().width; try { FancySSCell f = (FancySSCell) BasicSSCell.getSSCellByName(l[i][j]); if (f == null) { f = createCell(l[i][j], rs); f.addSSCellListener(this); f.addMouseListener(this); f.setAutoSwitch(AutoSwitch); f.setAutoDetect(AutoDetect); f.setAutoShowControls(AutoShowControls); if (rs == null) f.setDimension(CanDo3D ? BasicSSCell.JAVA3D_3D : BasicSSCell.JAVA2D_2D); f.addDisplayListener(this); if (rsi != null) { // add new cell to server f.addToRemoteServer(rsi); } } f.setPreferredSize(new Dimension(pw, ph)); DisplayCells[i][j] = f; DisplayPanel.add(DisplayCells[i][j]); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot construct spreadsheet cells. " + "An error occurred", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot construct spreadsheet cells. " + "A remote error occurred", exc, "VisAD SpreadSheet error"); } } } selectCell(0, 0); } } private void constructHorizontalLabels() { constructHorizontalLabels(null, null); } private void constructHorizontalLabels(String[] l, int[] widths) { if (l == null) { l = new String[NumVisX]; for (int i=0; i<NumVisX; i++) l[i] = "" + Letters.charAt(i); } if (widths == null) { widths = new int[NumVisX]; for (int i=0; i<NumVisX; i++) widths[i] = DEFAULT_VIS_WIDTH; } synchronized (Lock) { HorizPanel.setLayout(new SSLayout(2 * NumVisX, 1, 0, 0)); HorizLabel = new JPanel[NumVisX]; HorizDrag = new JComponent[NumVisX]; for (int i=0; i<NumVisX; i++) { HorizLabel[i] = new JPanel(); HorizLabel[i].setBorder(new LineBorder(Color.black, 1)); HorizLabel[i].setLayout(new BorderLayout()); HorizLabel[i].setPreferredSize(new Dimension(widths[i], LABEL_HEIGHT)); HorizLabel[i].add("Center", new JLabel(l[i], SwingConstants.CENTER)); HorizPanel.add(HorizLabel[i]); HorizDrag[i] = new JComponent() { public void paint(Graphics g) { Dimension d = this.getSize(); g.setColor(Color.black); g.drawRect(0, 0, d.width - 1, d.height - 1); g.setColor(Color.yellow); g.fillRect(1, 1, d.width - 2, d.height - 2); } public Dimension getPreferredSize() { return new Dimension(5, LABEL_HEIGHT); } }; HorizDrag[i].setPreferredSize(new Dimension(5, 0)); HorizDrag[i].addMouseListener(this); HorizDrag[i].addMouseMotionListener(this); HorizPanel.add(HorizDrag[i]); } } } private void constructVerticalLabels() { constructVerticalLabels(null, null); } private void constructVerticalLabels(String[] l, int[] heights) { if (l == null) { l = new String[NumVisY]; for (int i=0; i<NumVisY; i++) l[i] = "" + (i+1); } if (heights == null) { heights = new int[NumVisY]; for (int i=0; i<NumVisY; i++) heights[i] = DEFAULT_VIS_HEIGHT; } synchronized (Lock) { VertPanel.setLayout(new SSLayout(1, 2 * NumVisY, 0, 0)); VertLabel = new JPanel[NumVisY]; VertDrag = new JComponent[NumVisY]; for (int i=0; i<NumVisY; i++) { VertLabel[i] = new JPanel(); VertLabel[i].setBorder(new LineBorder(Color.black, 1)); VertLabel[i].setLayout(new BorderLayout()); VertLabel[i].setPreferredSize(new Dimension(LABEL_WIDTH, heights[i])); VertLabel[i].add("Center", new JLabel(l[i], SwingConstants.CENTER)); VertPanel.add(VertLabel[i]); VertDrag[i] = new JComponent() { public void paint(Graphics g) { Dimension d = this.getSize(); g.setColor(Color.black); g.drawRect(0, 0, d.width - 1, d.height - 1); g.setColor(Color.yellow); g.fillRect(1, 1, d.width - 2, d.height - 2); } public Dimension getPreferredSize() { return new Dimension(LABEL_WIDTH, 5); } }; VertDrag[i].setBackground(Color.white); VertDrag[i].setPreferredSize(new Dimension(0, 5)); VertDrag[i].addMouseListener(this); VertDrag[i].addMouseMotionListener(this); VertPanel.add(VertDrag[i]); } } } private void reconstructLabels(String[][] cellNames, int[] w, int[] h) { // reconstruct horizontal labels String[] hLabels = new String[NumVisX]; synchronized (Lock) { HorizPanel.removeAll(); for (int i=0; i<NumVisX; i++) { hLabels[i] = "" + cellNames[i][0].charAt(0); } } constructHorizontalLabels(hLabels, w); // reconstruct vertical labels String[] vLabels = new String[NumVisY]; synchronized (Lock) { VertPanel.removeAll(); for (int j=0; j<NumVisY; j++) vLabels[j] = cellNames[0][j].substring(1); } constructVerticalLabels(vLabels, h); } protected void reconstructSpreadsheet(String[][] cellNames, int[] w, int[] h, RemoteServer rs) { // reconstruct labels reconstructLabels(cellNames, w, h); // reconstruct spreadsheet cells synchronized (Lock) { DisplayPanel.removeAll(); int ox, oy; if (DisplayCells == null) { ox = 0; oy = 0; } else { ox = DisplayCells.length; oy = DisplayCells[0].length; } for (int i=0; i<ox; i++) { for (int j=0; j<oy; j++) { try { String s = DisplayCells[i][j].getName(); boolean kill = true; // only delete cells that are truly gone for (int ii=0; ii<cellNames.length; ii++) { for (int jj=0; jj<cellNames[ii].length; jj++) { if (s.equals(cellNames[ii][jj])) kill = false; } } if (kill) DisplayCells[i][j].destroyCell(); } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot destroy cell (7)", exc, "VisAD SpreadSheet error"); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); displayErrorMessage("Cannot destroy cell (8)", exc, "VisAD SpreadSheet error"); } DisplayCells[i][j] = null; } } } constructSpreadsheetCells(cellNames, rs); synchronized (Lock) { // refresh display HorizPanel.doLayout(); for (int i=0; i<NumVisX; i++) HorizLabel[i].doLayout(); VertPanel.doLayout(); for (int j=0; j<NumVisY; j++) VertLabel[j].doLayout(); SCPane.doLayout(); DisplayPanel.doLayout(); refreshCells(); } } private void reconstructHoriz(JPanel[] newLabels, JComponent[] newDrag, FancySSCell[][] fcells) { synchronized (Lock) { // reconstruct horizontal spreadsheet label layout HorizLabel = newLabels; HorizDrag = newDrag; HorizPanel.setLayout(new SSLayout(2*NumVisX, 1, 0, 0)); for (int i=0; i<NumVisX; i++) { HorizPanel.add(HorizLabel[i]); HorizPanel.add(HorizDrag[i]); } // reconstruct spreadsheet cell layout DisplayCells = fcells; DisplayPanel.setLayout(new SSLayout(NumVisX, NumVisY, 5, 5)); for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) DisplayPanel.add(DisplayCells[i][j]); } // refresh display HorizPanel.validate(); HorizLabels.validate(); HorizLabels.repaint(); for (int i=0; i<NumVisX; i++) HorizLabel[i].validate(); SCPane.validate(); DisplayPanel.repaint(); refreshCells(); } synchColRow(); } private void reconstructVert(JPanel[] newLabels, JComponent[] newDrag, FancySSCell[][] fcells) { synchronized (Lock) { // reconstruct vertical spreadsheet label layout VertLabel = newLabels; VertDrag = newDrag; VertPanel.setLayout(new SSLayout(1, 2*NumVisY, 0, 0)); for (int i=0; i<NumVisY; i++) { VertPanel.add(VertLabel[i]); VertPanel.add(VertDrag[i]); } // reconstruct spreadsheet cell layout DisplayCells = fcells; DisplayPanel.setLayout(new SSLayout(NumVisX, NumVisY, 5, 5)); for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) DisplayPanel.add(DisplayCells[i][j]); } // refresh display VertPanel.validate(); VertLabels.validate(); VertLabels.repaint(); for (int j=0; j<NumVisY; j++) VertLabel[j].validate(); SCPane.validate(); DisplayPanel.repaint(); refreshCells(); } synchColRow(); } // --- EVENT HANDLING --- /** * Handles checkbox menu item changes (dimension checkboxes). */ public void itemStateChanged(ItemEvent e) { String item = (String) e.getItem(); if (item.equals("3-D (Java3D)")) setDim(BasicSSCell.JAVA3D_3D); else if (item.equals("2-D (Java2D)")) setDim(BasicSSCell.JAVA2D_2D); else if (item.equals("2-D (Java3D)")) setDim(BasicSSCell.JAVA3D_2D); else if (item.equals("Auto-switch to 3-D")) { boolean b = e.getStateChange() == ItemEvent.SELECTED; setAutoSwitch(b); } else if (item.equals("Auto-detect mappings")) { boolean b = e.getStateChange() == ItemEvent.SELECTED; setAutoDetect(b); } else if (item.equals("Auto-display controls")) { boolean b = e.getStateChange() == ItemEvent.SELECTED; setAutoShowControls(b); } } /** * Handles scrollbar changes. */ public void adjustmentValueChanged(AdjustmentEvent e) { Adjustable a = e.getAdjustable(); int value = a.getValue(); if (a.getOrientation() == Adjustable.HORIZONTAL) { HorizLabels.setViewPosition(new Point(value, 0)); } else { // a.getOrientation() == Adjustable.VERTICAL VertLabels.setViewPosition(new Point(0, value)); } } /** * Handles display changes. */ public void displayChanged(DisplayEvent e) { if (e.getId() == DisplayEvent.MOUSE_PRESSED && !e.isRemote()) { // highlight cell if it is the source of a local mouse click String name = null; try { Display d = e.getDisplay(); name = d.getName(); if (name.endsWith(".remote")) { // cloned cells need ".remote" stripped from their names name = name.substring(0, name.length() - 7); } } catch (VisADException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } catch (RemoteException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } FancySSCell fcell = (FancySSCell) BasicSSCell.getSSCellByName(name); int ci = -1; int cj = -1; for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) { if (fcell == DisplayCells[i][j]) { ci = i; cj = j; } } } if (BasicSSCell.DEBUG && (ci < 0 || cj < 0)) { System.err.println("Warning: an unknown display change occurred: " + "display (" + name + ") has changed, but there is no " + "corresponding SSCell with that name!"); } selectCell(ci, cj); } } /** * BIG HAMMER HACK. */ private boolean commandKey; /** * BIG HAMMER HACK. */ private boolean shiftHeld; /** * BIG HAMMER HACK. */ public void keyPressed(KeyEvent e) { commandKey = true; int keyCode = e.getKeyCode(); if (keyCode == KeyEvent.VK_ENTER) { // enter pressed; notify action listener FormulaListener.actionPerformed(new ActionEvent(FormulaEditor, 0, "")); } else if (keyCode == KeyEvent.VK_BACK_SPACE) { // backspace pressed; delete text String text = FormulaText.getText(); int start = FormulaText.getSelectionStart(); int end = FormulaText.getSelectionEnd(); if (start != end || start != 0) { int pos = start == end ? start - 1 : start; String pre = text.substring(0, pos); String post = text.substring(end); FormulaText.setText(pre + post); FormulaText.getCaret().setDot(pos); } } else if (keyCode == KeyEvent.VK_SHIFT) { // shift pressed shiftHeld = true; } else if (keyCode == KeyEvent.VK_LEFT) { int pos = FormulaText.getCaretPosition(); if (shiftHeld) { // shift + left arrow pressed; alter selection left if (pos > 0) FormulaText.getCaret().moveDot(pos - 1); } else { // left arrow pressed; move caret left if (pos > 0) FormulaText.getCaret().setDot(pos - 1); } } else if (keyCode == KeyEvent.VK_RIGHT) { int pos = FormulaText.getCaretPosition(); if (shiftHeld) { // shift + right arrow pressed; alter selection right if (pos < FormulaText.getText().length()) { FormulaText.getCaret().moveDot(pos + 1); } } else { // right arrow pressed; move caret right if (pos < FormulaText.getText().length()) { FormulaText.getCaret().setDot(pos + 1); } } } else if (keyCode == KeyEvent.VK_HOME) { if (shiftHeld) { // shift + home pressed; select to beginning of text FormulaText.getCaret().moveDot(0); } else { // home pressed; move to beginning of text FormulaText.getCaret().setDot(0); } } else if (keyCode == KeyEvent.VK_END) { if (shiftHeld) { // shift + end pressed; select to end of text FormulaText.getCaret().moveDot(FormulaText.getText().length()); } else { // end pressed; move to end of text FormulaText.getCaret().setDot(FormulaText.getText().length()); } } else commandKey = false; } /** * BIG HAMMER HACK. */ public void keyReleased(KeyEvent e) { int keyCode = e.getKeyCode(); if (keyCode == KeyEvent.VK_SHIFT) shiftHeld = false; } /** * BIG HAMMER HACK. */ public void keyTyped(KeyEvent e) { char key = e.getKeyChar(); if (!commandKey && !e.isActionKey() && key >= 32) { int start = FormulaText.getSelectionStart(); int end = FormulaText.getSelectionEnd(); String text = FormulaText.getText(); FormulaText.setText(text.substring(0, start) + key + text.substring(end)); FormulaText.getCaret().setDot(start + 1); } e.consume(); } /** * Old x value used with cell resizing logic. */ private int oldX; /** * Old y value used with cell resizing logic. */ private int oldY; /** * Handles mouse presses. */ public void mousePressed(MouseEvent e) { Component c = e.getComponent(); for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) { if (c == DisplayCells[i][j]) { selectCell(i, j); return; } } } oldX = e.getX(); oldY = e.getY(); } /** * Handles cell resizing. */ public void mouseReleased(MouseEvent e) { int x = e.getX(); int y = e.getY(); Component c = e.getComponent(); boolean change = false; for (int j=0; j<NumVisX; j++) { if (c == HorizDrag[j]) { change = true; break; } } for (int j=0; j<NumVisY; j++) { if (c == VertDrag[j]) { change = true; break; } } if (change) synchLabelAndCellSizes(); } /** * Handles cell label resizing. */ public void mouseDragged(MouseEvent e) { Component c = e.getComponent(); int x = e.getX(); int y = e.getY(); for (int j=0; j<NumVisX; j++) { if (c == HorizDrag[j]) { // resize columns (labels) Dimension s = HorizLabel[j].getSize(); int oldW = s.width; s.width += x - oldX; if (s.width < MIN_VIS_WIDTH) s.width = MIN_VIS_WIDTH; HorizLabel[j].setSize(s); HorizLabel[j].setPreferredSize(s); HorizLabels.validate(); return; } } for (int j=0; j<NumVisY; j++) { if (c == VertDrag[j]) { // resize rows (labels) Dimension s = VertLabel[j].getSize(); int oldH = s.height; s.height += y - oldY; if (s.height < MIN_VIS_HEIGHT) s.height = MIN_VIS_HEIGHT; VertLabel[j].setSize(s); VertLabel[j].setPreferredSize(s); VertLabels.validate(); return; } } } /** * Unused MouseListener method. */ public void mouseClicked(MouseEvent e) { } /** * Unused MouseListener method. */ public void mouseEntered(MouseEvent e) { } /** * Unused MouseListener method. */ public void mouseExited(MouseEvent e) { } /** * Unused MouseMotionListener method. */ public void mouseMoved(MouseEvent e) { } /** * Handles changes in a cell's data. */ public void ssCellChanged(SSCellChangeEvent e) { FancySSCell f = (FancySSCell) e.getSSCell(); if (CurX < NumVisX && CurY < NumVisY && DisplayCells[CurX][CurY] == f) { int ct = e.getChangeType(); if (ct == SSCellChangeEvent.DATA_CHANGE) { refreshFormulaBar(); refreshMenuCommands(); } else if (ct == SSCellChangeEvent.DISPLAY_CHANGE) { refreshShowControls(); if (IsSlave) { // slaves cannot send DATA_CHANGE notification refreshFormulaBar(); refreshMenuCommands(); } } else if (ct == SSCellChangeEvent.DIMENSION_CHANGE) { refreshDisplayMenuItems(); } } } // --- SPREADSHEET API --- /** * Sets the SpreadSheet cell class to the given class (which must extend * FancySSCell), used for creating SpreadSheet cells. */ public static void setSSCellClass(Class c) { try { cellConstructor = c.getConstructor(cellArgs); } catch (NoSuchMethodException exc) { if (BasicSSCell.DEBUG) exc.printStackTrace(); } } /** * Selects the specified cell and updates screen info. */ public void selectCell(int x, int y) { if (x < 0) x = 0; if (y < 0) y = 0; if (x >= NumVisX) x = NumVisX - 1; if (y >= NumVisY) y = NumVisY - 1; // update borders of all cells for (int j=0; j<NumVisY; j++) { for (int i=0; i<NumVisX; i++) { boolean selected = x == i && y == j; DisplayCells[i][j].setSelected(selected); DisplayCells[i][j].setAutoShowControls(selected && AutoShowControls); } } // update spreadsheet info CurX = x; CurY = y; FormulaText.getCaret().setVisible(true); // BIG HAMMER HACK refreshFormulaBar(); refreshMenuCommands(); refreshDisplayMenuItems(); } /** * Pops up an option selection dialog for choosing SpreadSheet options. */ protected boolean getOptions(int cols, int rows, String server, String clone, boolean slave) { // Note: When the "Ok" button of this option dialog is pressed, the values // of SpreadSheet fields are altered directly. After calling this method, // another method like constructSpreadsheet() should be called to implement // the user-chosen settings. // set up the initial settings final SSOptions options = new SSOptions(cols, rows, CanDo3D, BugFix, BasicSSCell.DEBUG, server, clone, slave); // set up main content pane final boolean[] success = new boolean[1]; success[0] = false; final JDialog dialog = new JDialog((JFrame) null, "VisAD SpreadSheet Options", true); JPanel pane = new JPanel(); dialog.setContentPane(pane); pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS)); // set up first row of options JPanel row1 = new JPanel(); row1.setLayout(new BoxLayout(row1, BoxLayout.X_AXIS)); JPanel row1Cols = new JPanel(); row1Cols.setLayout(new BoxLayout(row1Cols, BoxLayout.Y_AXIS)); row1Cols.add(new JLabel("Columns")); final JTextField colField = new JTextField("" + cols); Util.adjustTextField(colField); colField.setEnabled(clone == null); row1Cols.add(colField); row1.add(row1Cols); JPanel row1Xs = new JPanel(); row1Xs.setLayout(new BoxLayout(row1Xs, BoxLayout.Y_AXIS)); row1Xs.add(new JLabel(" x ")); row1Xs.add(new JLabel(" x ")); row1.add(row1Xs); JPanel row1Rows = new JPanel(); row1Rows.setLayout(new BoxLayout(row1Rows, BoxLayout.Y_AXIS)); row1Rows.add(new JLabel("Rows")); final JTextField rowField = new JTextField("" + rows); Util.adjustTextField(rowField); rowField.setEnabled(clone == null); row1Rows.add(rowField); row1.add(row1Rows); JPanel row1Boxes = new JPanel(); row1Boxes.setLayout(new BoxLayout(row1Boxes, BoxLayout.Y_AXIS)); final JCheckBox java3d = new JCheckBox("Enable Java3D", CanDo3D); java3d.setEnabled(Possible3D); row1Boxes.add(java3d); final JCheckBox toolBox = new JCheckBox("Enable toolbar", !BugFix); row1Boxes.add(toolBox); final JCheckBox debugBox = new JCheckBox("Debug mode", BasicSSCell.DEBUG); row1Boxes.add(debugBox); row1.add(row1Boxes); pane.add(row1); // set up second row of options JPanel row2 = new JPanel(); row2.setLayout(new BoxLayout(row2, BoxLayout.X_AXIS)); ButtonGroup collabGroup = new ButtonGroup(); final JRadioButton serverChoice = new JRadioButton("Server"); serverChoice.setSelected(server != null && clone == null); collabGroup.add(serverChoice); row2.add(serverChoice); final JRadioButton cloneChoice = new JRadioButton("Clone"); cloneChoice.setSelected(clone != null && !slave); collabGroup.add(cloneChoice); row2.add(cloneChoice); final JRadioButton slaveChoice = new JRadioButton("Slave"); slaveChoice.setSelected(slave); collabGroup.add(slaveChoice); row2.add(slaveChoice); final JRadioButton aloneChoice = new JRadioButton("Stand-alone"); aloneChoice.setSelected(server == null && clone == null); collabGroup.add(aloneChoice); row2.add(aloneChoice); pane.add(row2); // set up third row of options JPanel row3 = new JPanel(); row3.setLayout(new BoxLayout(row3, BoxLayout.X_AXIS)); row3.add(new JLabel("Server name ")); final JTextField name = new JTextField(server == null ? "" : server); Util.adjustTextField(name); name.setEnabled(server != null); row3.add(name); pane.add(row3); // set up fourth row of options JPanel row4 = new JPanel(); row4.setLayout(new BoxLayout(row4, BoxLayout.X_AXIS)); row4.add(new JLabel("Server address ")); final JTextField host = new JTextField(clone == null ? "" : clone); Util.adjustTextField(host); host.setEnabled(clone != null); row4.add(host); pane.add(row4); // set up fifth row of options JPanel row5 = new JPanel(); row5.setLayout(new BoxLayout(row5, BoxLayout.X_AXIS)); boolean first = true; String extras = "Extras: "; if (Possible3D) { extras = extras + (first ? "" : ", ") + "Java3D"; first = false; } if (CanDoHDF5) { extras = extras + (first ? "" : ", ") + "HDF-5"; first = false; } if (CanDoJPEG) { extras = extras + (first ? "" : ", ") + "JPEG snapshot"; first = false; } if (CanDoPython) { extras = extras + (first ? "" : ", ") + "JPython scripting"; first = false; } if (first) extras = extras + "None"; extras = extras + "."; row5.add(new JLabel(extras)); pane.add(row5); // set up button row JPanel buttons = new JPanel(); buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS)); final JButton ok = new JButton("Ok"); dialog.getRootPane().setDefaultButton(ok); buttons.add(ok); final JButton cancel = new JButton("Cancel"); buttons.add(cancel); final JButton quit = new JButton("Quit"); buttons.add(quit); pane.add(buttons); // handle important events ActionListener handler = new ActionListener() { public void actionPerformed(ActionEvent e) { Object o = e.getSource(); if (o == serverChoice) { colField.setEnabled(true); rowField.setEnabled(true); name.setEnabled(true); host.setEnabled(false); } else if (o == cloneChoice || o == slaveChoice) { colField.setEnabled(false); rowField.setEnabled(false); name.setEnabled(true); host.setEnabled(true); } else if (o == aloneChoice) { colField.setEnabled(true); rowField.setEnabled(true); name.setEnabled(false); host.setEnabled(false); } else if (o == ok) { // determine server type boolean serv = serverChoice.isSelected(); boolean clon = cloneChoice.isSelected(); boolean slav = slaveChoice.isSelected(); boolean alon = aloneChoice.isSelected(); if (clon || slav) { // column and row values are irrelevant for client sheets options.cols = 2; options.rows = 2; } else { // get number of columns options.cols = 0; try { options.cols = Integer.parseInt(colField.getText()); } catch (NumberFormatException exc) { } if (options.cols <= 0) { displayErrorMessage(dialog, "The columns field must contain a " + "number greater than zero", null, "Invalid value"); return; } // get number of rows options.rows = 0; try { options.rows = Integer.parseInt(rowField.getText()); } catch (NumberFormatException exc) { } if (options.rows <= 0) { displayErrorMessage(dialog, "The rows field must contain a " + "number greater than zero", null, "Invalid value"); return; } } // get Java3D toggle value options.enable3d = java3d.isSelected(); // get toolbar toggle value options.bugfix = !toolBox.isSelected(); // get debug toggle value options.debug = debugBox.isSelected(); // get server name options.name = alon ? null : name.getText(); // get server address options.address = clon || slav ? host.getText() : null; // get slave toggle value options.slave = slav; // everything ok, assign variables NumVisX = options.cols; NumVisY = options.rows; CanDo3D = options.enable3d; BasicSSCell.DEBUG = options.debug; BugFix = options.bugfix; serverName = options.name; cloneAddress = options.address; IsSlave = options.slave; // return successfully success[0] = true; dialog.setVisible(false); } else if (o == cancel) { success[0] = false; dialog.setVisible(false); } else if (o == quit) { // wow, just up and quit System.exit(0); } } }; serverChoice.addActionListener(handler); cloneChoice.addActionListener(handler); slaveChoice.addActionListener(handler); aloneChoice.addActionListener(handler); ok.addActionListener(handler); cancel.addActionListener(handler); quit.addActionListener(handler); // display dialog dialog.pack(); Util.centerWindow(dialog); dialog.setVisible(true); return success[0]; } /** * Inner class for use with getOptions(). */ public class SSOptions { public int cols; public int rows; public boolean enable3d; public boolean bugfix; public boolean debug; public String name; public String address; public boolean slave; public SSOptions(int c, int r, boolean e, boolean b, boolean d, String n, String a, boolean s) { cols = c; rows = r; enable3d = e; bugfix = b; debug = d; name = n; address = a; slave = s; } } /** * Returns the JToolBar object for other programs to use (e.g., add buttons). */ public JToolBar getToolbar() { return Toolbar; } /** * Returns a new instance of a spreadsheet cell (which must extend * FancySSCell), used when a spreadsheet row or column is added. */ protected FancySSCell createCell(String name, RemoteServer rs) throws VisADException, RemoteException { Object[] args = {name, fm, rs, new Boolean(IsSlave), null, this}; if (cellConstructor == null) setSSCellClass(FancySSCell.class); Object cell = null; try { cell = cellConstructor.newInstance(args); } catch (IllegalAccessException exc) { exc.printStackTrace(); } catch (InstantiationException exc) { exc.printStackTrace(); } catch (InvocationTargetException exc) { if (exc.getTargetException() instanceof java.rmi.StubNotFoundException) { System.err.println( "Your VisAD installation has not properly executed the RMIC\n" + "compiler on the appropriate source files. Please re-run\n" + "\"make compile\" in the VisAD directory. If you are using\n" + "Makefile.WinNT and running JDK 1.2, please double-check that\n" + "you have uncommented the RMIC-related environment variables,\n" + "or else the RMIC-related classes will be placed in the wrong\n" + "directories. A full stack dump follows:\n"); } exc.getTargetException().printStackTrace(); } if (!(cell instanceof FancySSCell)) { System.err.print("Cell constructor failed to " + "produce a FancySSCell, but instead produced: "); if (cell == null) System.err.println("null"); else System.err.println(cell.getClass().getName()); System.exit(3); } return (FancySSCell) cell; } /** * Displays an error in a message dialog. */ protected void displayErrorMessage(String msg, Exception exc, String title) { displayErrorMessage(this, msg, exc, title); } /** * Displays an error in a message dialog. */ protected void displayErrorMessage(Component parent, String msg, Exception exc, String title) { String s = (exc == null ? null : exc.getMessage()); final Component c = parent; final String m = msg + (s == null ? "." : (": " + s)); final String t = title; Util.invoke(false, BasicSSCell.DEBUG, new Runnable() { public void run() { JOptionPane.showMessageDialog(c, m, t, JOptionPane.ERROR_MESSAGE); } }); } /** * Adds a button to a toolbar. */ protected JButton addToolbarButton(String file, String tooltip, String command, boolean enabled, JComponent parent) { URL url = SpreadSheet.class.getResource(file + ".gif"); ImageIcon icon = new ImageIcon(url); if (icon != null) { JButton b = new JButton(icon); b.setAlignmentY(JButton.CENTER_ALIGNMENT); b.setToolTipText(tooltip); b.addActionListener(this); b.setActionCommand(command); b.setEnabled(enabled); if (parent instanceof JPanel) { int w = icon.getIconWidth() + 4; int h = icon.getIconHeight() + 4; b.setPreferredSize(new Dimension(w, h)); } parent.add(b); return b; } else return null; } // --- DEPRECATED METHODS --- /** * @deprecated Use Util.adjustTextField(JTextField) instead. */ public static void adjustTextField(JTextField field) { Util.adjustTextField(field); } /** * @deprecated Use Util.centerWindow(Window) instead. */ public static void centerWindow(Window window) { Util.centerWindow(window); } }