/* * Open Source Physics software is free software as described near the bottom of this code file. * * For additional information and documentation on Open Source Physics please see: * <http://www.opensourcephysics.org/> */ package org.opensourcephysics.display; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.List; import javax.swing.Box; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTabbedPane; import javax.swing.JToolBar; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /** * A panel that displays an ArrayTable. * * @author Douglas Brown * @author Wolfgang Christian * @version 1.0 */ public class ArrayPanel extends JPanel implements PropertyChangeListener, Data { JTabbedPane tabbedPane = new JTabbedPane(); ArrayTable[] tables; JSpinner spinner; JScrollPane scrollpane; Object array; boolean changed; String format = null; int firstRowIndex = 0, firstColIndex = 0; boolean rowNumberVisible = true, editable = true; String[] colNames; private int ID = hashCode(); /** * Constructor ArrayPanel */ public ArrayPanel() { //System.out.println("creating array panel"); } public static ArrayPanel getArrayPanel(Object arrayObj) { ArrayPanel arrayPanel = new ArrayPanel(); arrayPanel.setArray(arrayObj); return arrayPanel; } /** * Gets an array panel for the specified array. * * @param arrayObj the array * @param name the display name for the array * @return the array panel */ public void setArray(Object arrayObj) { if(!canDisplay(arrayObj)) { return; } if(arrayObj instanceof double[]) { setArray((double[]) arrayObj); } else if(arrayObj instanceof double[][]) { setArray((double[][]) arrayObj); } else if(arrayObj instanceof double[][][]) { setArray((double[][][]) arrayObj); } else if(arrayObj instanceof int[]) { setArray((int[]) arrayObj); } else if(arrayObj instanceof int[][]) { setArray((int[][]) arrayObj); } else if(arrayObj instanceof int[][][]) { setArray((int[][][]) arrayObj); } else if(arrayObj instanceof String[]) { setArray((String[]) arrayObj); } else if(arrayObj instanceof String[][]) { setArray((String[][]) arrayObj); } else if(arrayObj instanceof String[][][]) { setArray((String[][][]) arrayObj); } else if(arrayObj instanceof boolean[]) { setArray((boolean[]) arrayObj); } else if(arrayObj instanceof boolean[][]) { setArray((boolean[][]) arrayObj); } else if(arrayObj instanceof boolean[][][]) { setArray((boolean[][][]) arrayObj); } this.array = arrayObj; } /** * Determines if an object is an array that can be displayed. * * @param obj the object * @return true if it can be inspected */ public static boolean canDisplay(Object obj) { if(obj==null) { return false; } if((obj instanceof double[])||(obj instanceof double[][])||(obj instanceof double[][][])||(obj instanceof int[])||(obj instanceof int[][])||(obj instanceof int[][][])||(obj instanceof boolean[])||(obj instanceof boolean[][])||(obj instanceof boolean[][][])||(obj instanceof String[])||(obj instanceof String[][])||(obj instanceof String[][][])) { return true; } return false; } /** * Gets the object being displayed. * * @return */ public Object getArray() { return array; } /** * Listens for cell events (data changes) from ArrayTable. * * @param e the property change event */ public void propertyChange(PropertyChangeEvent e) { // forward event to listeners changed = true; firePropertyChange(e.getPropertyName(), e.getOldValue(), e.getNewValue()); } /** * Sets the same numeric display format for all columns * * @param _format String */ public void setNumericFormat(String _format) { this.format = _format; for(int i = 0; i<tables.length; i++) { tables[i].setNumericFormat(_format); } } /** * Sets the numeric display format for each column * * @param _format String[] */ public void setNumericFormat(String[] _format) { this.format = null; for(int i = 0; i<tables.length; i++) { tables[i].setNumericFormat(_format); } } /** * Sets the display row number flag. Table displays row number. * * @param vis <code>true<\code> if table display row number */ public void setRowNumberVisible(boolean vis) { this.rowNumberVisible = vis; for(int i = 0; i<tables.length; i++) { tables[i].setRowNumberVisible(vis); // refreshes the table } } /** * Sets the column names in the table models. * * @param names */ public void setColumnNames(String[] names) { for(int i = 0; i<tables.length; i++) { tables[i].setColumnNames(names); } } /** * Sets the column names in each table model separately. * * @param names */ public void setColumnNames(String[][] names) { int n = Math.min(tables.length, names.length); // ensure correct dimensions for(int i = 0; i<n; i++) { tables[i].setColumnNames(names[i]); } } // Preferred column width changes by Willy Gerber /** * Sets this column's preferred width of the given column. * The minimum width is set to zero and the maximum width is set to 300. * * @param ncol the column * @param nwidth the preferred width */ public void setPreferredColumnWidth(int ncol, int nwidth) { for(int table = 0; table<tables.length; table++) { javax.swing.table.TableColumn column = tables[table].getColumnModel().getColumn(ncol); column.setMinWidth(0); column.setMaxWidth(300); column.setPreferredWidth(nwidth); } } /** * Sets this column's preferred width of the entire table. * The minimum width is set to zero and the maximum width is set to 300. * * @param nwidth the preferred width */ public void setPreferredColumnWidth(int nwidth) { for(int table = 0; table<tables.length; table++) { for(int col = 0; col<tables[table].getColumnCount(); col++) { javax.swing.table.TableColumn column = tables[table].getColumnModel().getColumn(col); column.setMinWidth(0); column.setMaxWidth(300); column.setPreferredWidth(nwidth); } } } /** * Sets the alignment of the contents of the given column along the X axis. * The alignment constants are defined in the SwingConstants class. * * @param ncol the column * @param align One of the following constants defined in <code>SwingConstants</code>: * <code>LEFT</code>, * <code>CENTER</code> (the default for image-only labels), * <code>RIGHT</code>, * <code>LEADING</code> (the default for text-only labels) or * <code>TRAILING</code>. */ public void setColumnAlignment(int ncol, int align) { for(int table = 0; table<tables.length; table++) { for(int row = 0; row<tables[table].getRowCount(); row++) { javax.swing.table.TableCellRenderer renderer = tables[table].getCellRenderer(row, ncol); ((JLabel) renderer).setHorizontalAlignment(align); } } } /** * Sets the alignment of the contents of all table columns along the X axis. * The alignment constants are defined in the SwingConstants class. * * @param align One of the following constants defined in <code>SwingConstants</code>: * <code>LEFT</code>, * <code>CENTER</code> (the default for image-only labels), * <code>RIGHT</code>, * <code>LEADING</code> (the default for text-only labels) or * <code>TRAILING</code>. */ public void setColumnAlignment(int align) { for(int table = 0; table<tables.length; table++) { for(int row = 0; row<tables[table].getRowCount(); row++) { for(int col = 0; col<tables[table].getColumnCount(); col++) { javax.swing.table.TableCellRenderer renderer = tables[table].getCellRenderer(row, col); ((JLabel) renderer).setHorizontalAlignment(align); } } } } // End of changes by Willy Gerber public int getFirstRowIndex() { return this.firstRowIndex; } /** * Sets the first row's index. * * @param index */ public void setFirstRowIndex(int index) { this.firstRowIndex = index; for(int i = 0; i<tables.length; i++) { tables[i].setFirstRowIndex(index); } } /** * Sets the first column's index. * * @param index */ public void setFirstColIndex(int index) { this.firstColIndex = index; for(int i = 0; i<tables.length; i++) { tables[i].setFirstColIndex(index); } } /** * Sets the column's lock flag. * * @param column int * @param locked boolean */ public void setColumnLock(int columnIndex, boolean locked) { for(int i = 0; i<tables.length; i++) { tables[i].setColumnLock(columnIndex, locked); } } /** * Sets the lock flag for multiple columns. Previously set locks are cleared. * * @param locked boolean array */ public void setColumnLocks(boolean[] locked) { for(int i = 0; i<tables.length; i++) { tables[i].setColumnLocks(locked); } } /** * Sets the editable property for the entire panel. * * @param editable true to allow editing of the cell values */ public void setEditable(boolean _editable) { this.editable = _editable; for(int i = 0; i<tables.length; i++) { tables[i].setEditable(_editable); } } /** * Sets the transposed property for the array. * A transposed array switches its row and column values in the display. * * @param transposed */ public void setTransposed(boolean transposed) { for(int i = 0; i<tables.length; i++) { tables[i].setTransposed(transposed); } } /** * Sets the font for this component. * * @param font the desired <code>Font</code> for this component * @see java.awt.Component#getFont */ public void setFont(Font font){ // Added by Paco super.setFont(font); if (tables!=null) for(int i = 0; i<tables.length; i++) tables[i].setFont(font); } /** * Sets the foreground color of this component. It is up to the * look and feel to honor this property, some may choose to ignore * it. * * @param fg the desired foreground <code>Color</code> * @see java.awt.Component#getForeground */ public void setForeground(Color color){ // Added by Paco super.setForeground(color); if (tables!=null) for(int i = 0; i<tables.length; i++) tables[i].setForeground(color); } /** * Sets the background color of this component. It is up to the * look and feel to honor this property, some may choose to ignore * it. * * @param fg the desired background <code>Color</code> * @see java.awt.Component#getBackground */ public void setBackground(Color color){ // Added by Paco super.setBackground(color); if (tables!=null) for(int i = 0; i<tables.length; i++) tables[i].setBackground(color); } /** * Sets the data foreground color of this component. It is up to the * look and feel to honor this property, some may choose to ignore * it. * * @param fg the desired foreground <code>Color</code> */ public void setDataForeground(Color color){ // Added by Paco if (tables!=null) for(int i = 0; i<tables.length; i++) tables[i].setDataForeground(color); refreshTable(); } /** * Sets the data background color of this component. It is up to the * look and feel to honor this property, some may choose to ignore * it. * * @param fg the desired background <code>Color</code> * @see java.awt.Component#getBackground */ public void setDataBackground(Color color){ // Added by Paco if (tables!=null) for(int i = 0; i<tables.length; i++) tables[i].setDataBackground(color); refreshTable(); } /** * Sets the table's auto resize mode when the table is resized. * * @param mode One of 5 legal values: * AUTO_RESIZE_OFF, * AUTO_RESIZE_NEXT_COLUMN, * AUTO_RESIZE_SUBSEQUENT_COLUMNS, * AUTO_RESIZE_LAST_COLUMN, * AUTO_RESIZE_ALL_COLUMNS */ public void setAutoResizeMode(int mode) { if (tables!=null) for(int i = 0; i<tables.length; i++) tables[i].setAutoResizeMode(mode); refreshTable(); } // ------------------------------- // Getters // ------------------------------- public int getNumColumns() { return tables[0].getColumnCount(); } //------------------------------- // Getters // ------------------------------- /** * Refresh the data in all the tables. */ public void refreshTable() { for(int i = 0; i<tables.length; i++) { tables[i].refreshTable(); } } /** * Sets the <code>Timer</code>'s initial time delay (in milliseconds) * to wait after the timer is started * before firing the first event. * @param delay */ public void setRefreshDelay(int delay) { for(int i = 0; i<tables.length; i++) { tables[i].setRefreshDelay(delay); } } /** * Creates the GUI. */ protected void createGUI() { this.removeAll(); // remove old elements Paco: be careful with this. If you use the ArrayPanel as a normal JPanel // and have added another component, it will be lost! this.setPreferredSize(new Dimension(400, 300)); this.setLayout(new BorderLayout()); scrollpane = new JScrollPane(tables[0]); if(tables.length>1) { // create spinner SpinnerModel model = new SpinnerNumberModel(0, 0, tables.length-1, 1); spinner = new JSpinner(model); JSpinner.NumberEditor editor = new JSpinner.NumberEditor(spinner); editor.getTextField().setFont(tables[0].indexRenderer.getFont()); spinner.setEditor(editor); spinner.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { int i = ((Integer) spinner.getValue()).intValue(); scrollpane.setViewportView(tables[i]); } }); Dimension dim = spinner.getMinimumSize(); spinner.setMaximumSize(dim); add(scrollpane, BorderLayout.CENTER); JToolBar toolbar = new JToolBar(); toolbar.setFloatable(false); toolbar.add(new JLabel(" index ")); //$NON-NLS-1$ toolbar.add(spinner); toolbar.add(Box.createHorizontalGlue()); add(toolbar, BorderLayout.NORTH); } else { scrollpane.createHorizontalScrollBar(); add(scrollpane, BorderLayout.CENTER); } this.validate(); // refresh the display } //_____________________________private constructors___________________________ private void setArray(int[] array) { if(this.array instanceof int[]) { // && ((int[])this.array).length==array.length) { // Paco tables[0].tableModel.setArray(array); return; } tables = new ArrayTable[1]; tables[0] = new ArrayTable(array); tables[0].addPropertyChangeListener("cell", this); //$NON-NLS-1$ tables[0].setFont(getFont()); tables[0].setForeground(getForeground()); tables[0].setBackground(getBackground()); createGUI(); } private void setArray(int[][] array) { if(this.array instanceof int[][]) { //&& ((int[][])this.array).length==array.length && ((int[][])this.array)[0].length==array[0].length) { // Paco tables[0].tableModel.setArray(array); return; } tables = new ArrayTable[1]; tables[0] = new ArrayTable(array); tables[0].addPropertyChangeListener("cell", this); //$NON-NLS-1$ tables[0].setFont(getFont()); tables[0].setForeground(getForeground()); tables[0].setBackground(getBackground()); createGUI(); } private void setArray(int[][][] array) { /* Isn't this too much? Paco if (this.array instanceof int[][][] && ((int[][][])this.array).length==array.length ) { boolean quickChange = true; int[][][] thisArray = (int[][][]) this.array; for (int i=0; i<thisArray.length; i++) { if (thisArray[i].length!=array[i].length || thisArray[i][0].length!=array[i][0].length) { quickChange = false; break; } } if (quickChange) { for (int i=0; i<thisArray.length; i++) tables[i].tableModel.setArray(array[i]); return; } } */ tables = new ArrayTable[array.length]; for(int i = 0; i<tables.length; i++) { tables[i] = new ArrayTable(array[i]); tables[i].addPropertyChangeListener("cell", this); //$NON-NLS-1$ tables[i].setFont(getFont()); tables[i].setForeground(getForeground()); tables[i].setBackground(getBackground()); } createGUI(); } private void setArray(double[] array) { if(this.array instanceof double[]) { // ((double[])this.array).length==array.length) { // Paco added this tables[0].tableModel.setArray(array); return; } tables = new ArrayTable[1]; tables[0] = new ArrayTable(array); tables[0].addPropertyChangeListener("cell", this); //$NON-NLS-1$ tables[0].setFont(getFont()); tables[0].setForeground(getForeground()); tables[0].setBackground(getBackground()); createGUI(); } private void setArray(double[][] array) { if(this.array instanceof double[][]) { //&& ((double[][])this.array).length==array.length && ((double[][])this.array)[0].length==array[0].length) { // Paco tables[0].tableModel.setArray(array); return; } tables = new ArrayTable[1]; tables[0] = new ArrayTable(array); tables[0].addPropertyChangeListener("cell", this); //$NON-NLS-1$ tables[0].setFont(getFont()); tables[0].setForeground(getForeground()); tables[0].setBackground(getBackground()); createGUI(); } private void setArray(double[][][] array) { tables = new ArrayTable[array.length]; for(int i = 0; i<tables.length; i++) { tables[i] = new ArrayTable(array[i]); tables[i].addPropertyChangeListener("cell", this); //$NON-NLS-1$ tables[i].setFont(getFont()); tables[i].setForeground(getForeground()); tables[i].setBackground(getBackground()); } createGUI(); } private void setArray(String[] array) { if(this.array instanceof String[]) { // && ((String[])this.array).length==array.length) { // Paco added this tables[0].tableModel.setArray(array); return; } tables = new ArrayTable[1]; tables[0] = new ArrayTable(array); tables[0].addPropertyChangeListener("cell", this); //$NON-NLS-1$ tables[0].setFont(getFont()); tables[0].setForeground(getForeground()); tables[0].setBackground(getBackground()); createGUI(); } private void setArray(String[][] array) { if(this.array instanceof String[][]) { //&& ((String[][])this.array).length==array.length && ((String[][])this.array)[0].length==array[0].length) { // Paco tables[0].tableModel.setArray(array); return; } tables = new ArrayTable[1]; tables[0] = new ArrayTable(array); tables[0].addPropertyChangeListener("cell", this); //$NON-NLS-1$ tables[0].setFont(getFont()); tables[0].setForeground(getForeground()); tables[0].setBackground(getBackground()); createGUI(); } private void setArray(String[][][] array) { tables = new ArrayTable[array.length]; for(int i = 0; i<tables.length; i++) { tables[i] = new ArrayTable(array[i]); tables[i].addPropertyChangeListener("cell", this); //$NON-NLS-1$ tables[i].setFont(getFont()); tables[i].setForeground(getForeground()); tables[i].setBackground(getBackground()); } createGUI(); } private void setArray(boolean[] array) { if(this.array instanceof boolean[]) { // && ((boolean[])this.array).length==array.length) { // Paco added this tables[0].tableModel.setArray(array); return; } tables = new ArrayTable[1]; tables[0] = new ArrayTable(array); tables[0].addPropertyChangeListener("cell", this); //$NON-NLS-1$ tables[0].setFont(getFont()); tables[0].setForeground(getForeground()); tables[0].setBackground(getBackground()); createGUI(); } private void setArray(boolean[][] array) { if(this.array instanceof boolean[][]) { //&& ((boolean[][])this.array).length==array.length && ((boolean[][])this.array)[0].length==array[0].length) { // Paco tables[0].tableModel.setArray(array); return; } tables = new ArrayTable[1]; tables[0] = new ArrayTable(array); tables[0].addPropertyChangeListener("cell", this); //$NON-NLS-1$ tables[0].setFont(getFont()); tables[0].setForeground(getForeground()); tables[0].setBackground(getBackground()); createGUI(); } private void setArray(boolean[][][] array) { tables = new ArrayTable[array.length]; for(int i = 0; i<tables.length; i++) { tables[i] = new ArrayTable(array[i]); tables[i].addPropertyChangeListener("cell", this); //$NON-NLS-1$ tables[i].setFont(getFont()); tables[i].setForeground(getForeground()); tables[i].setBackground(getBackground()); } createGUI(); } // The following methods were added to implement the Data interface for double[][] arrays. /** * Gets column names from Table Model. * Implementation of Data interface. */ public String[] getColumnNames() { double[][] data = getData2D(); if(data==null) { return null; // no data } int n = data.length; if((colNames==null)||(n!=colNames.length)) { // every array columns should have a name colNames = new String[n]; } String[] modelNames = tables[0].tableModel.columnNames; int stop = (modelNames==null) ? 0 : Math.min(n, modelNames.length-1); for(int i = 0; i<stop; i++) { // assign model names to Data columns colNames[i] = modelNames[i+1]; // skip zero column name because it is the row index } for(int i = stop; i<n; i++) { // assign default column names colNames[i] = "C"+(i+1); //$NON-NLS-1$ } return colNames; } /** * Gets double[][] data from the Table Model and transposes this array if necessary. * Implementation of Data interface. */ public double[][] getData2D() { if((tables==null)||(tables[0]==null)) { return null; } boolean transposed = tables[0].tableModel.transposed; double[][] data = tables[0].tableModel.doubleArray2; if(!transposed&&(data!=null)) { // first index of the array is the DataPanel column int r = data.length; int c = 0; for(int i = 0; i<r; i++) { // find the largest index to set the number of columns c = Math.max(c, data[i].length); } double[][] tdata = new double[c][r]; for(int i = 0; i<r; i++) { int ci = data[i].length; for(int j = 0; j<ci; j++) { tdata[j][i] = data[i][j]; } for(int j = ci; j<c; j++) { tdata[j][i] = Double.NaN; } } return tdata; } return data; } /** * Not used because double[][][] is not used in any OSP Tools. * Implementation of Data interface method. */ public double[][][] getData3D() { return null; } /** * Not used because Data is stored in this object, not in a list of Data objects. * Implementation of Data interface. */ public List<Data> getDataList() { return null; } /** * Not used Data because is stored in 2D arrays. * Implementation of Data interface. */ public ArrayList<Dataset> getDatasets() { return null; } /** * Fill colors for columns are not specified. Client should assign colors. * Implementation of Data interface. */ public Color[] getFillColors() { return null; } /** * Lines colors for columns are not specified. Client should assign colors. * Implementation of Data interface. */ public Color[] getLineColors() { return null; } /** * Gets the Data ID. */ public int getID() { boolean transposed = tables[0].tableModel.transposed; if(transposed) { return ID^0xffff; // exclusive OR to reverse and produce new ID } return ID; } /** * Sets the Data ID. */ public void setID(int id) { ID = id; } } /* * Open Source Physics software is free software; you can redistribute * it and/or modify it under the terms of the GNU General Public License (GPL) as * published by the Free Software Foundation; either version 2 of the License, * or(at your option) any later version. * Code that uses any portion of the code in the org.opensourcephysics package * or any subpackage (subdirectory) of this package must must also be be released * under the GNU GPL license. * * This software 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA * or view the license online at http://www.gnu.org/copyleft/gpl.html * * Copyright (c) 2007 The Open Source Physics project * http://www.opensourcephysics.org */