001/* A graphical component displaying an array of records. 002 003 Copyright (c) 1997-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 028 */ 029package ptolemy.actor.gui; 030 031import java.awt.Component; 032import java.awt.Dimension; 033import java.util.Iterator; 034import java.util.LinkedList; 035import java.util.List; 036 037import javax.swing.JPanel; 038import javax.swing.JScrollPane; 039import javax.swing.JTable; 040import javax.swing.table.AbstractTableModel; 041import javax.swing.table.JTableHeader; 042import javax.swing.table.TableCellRenderer; 043import javax.swing.table.TableColumn; 044 045import ptolemy.data.ArrayToken; 046import ptolemy.data.RecordToken; 047import ptolemy.data.StringToken; 048import ptolemy.data.Token; 049import ptolemy.kernel.util.IllegalActionException; 050import ptolemy.kernel.util.InternalErrorException; 051 052/////////////////////////////////////////////////////////////////// 053//// ArrayOfRecordsPane 054 055/** 056 A graphical component that displays the values in an array of records. 057 The data to display is supplied in the form of an ArrayToken. 058 The table is currently not editable, although this may be changed 059 in the future. The table is an instance of Java's JTable class, 060 which is exposed as a public member. The rich interface 061 of the JTable class can be used to customize the display 062 in various ways. 063 064 @author Edward A. Lee 065 @version $Id$ 066 @since Ptolemy II 8.0 067 @Pt.ProposedRating Red (eal) 068 @Pt.AcceptedRating Red (eal) 069 */ 070@SuppressWarnings("serial") 071public class ArrayOfRecordsPane extends JPanel { 072 /** Construct an empty table pane. 073 */ 074 public ArrayOfRecordsPane() { 075 super(); 076 table = new SimpleTable(); 077 // FIXME: As usual with Swing, the scrollpane doesn't appear. 078 // But if we don't do this, then the table headers don't appear! 079 // Go figure... 080 JScrollPane scrollPane = new JScrollPane(table); 081 add(scrollPane); 082 } 083 084 /////////////////////////////////////////////////////////////////// 085 //// public methods //// 086 087 /** Clear the display. */ 088 public void clear() { 089 table.setModel(_emptyTableModel); 090 } 091 092 /** Set the array to display in the table. 093 * This method results in all fields of the records being displayed. 094 * @param array The array of records to display in the table. 095 */ 096 public void display(ArrayToken array) { 097 display(array, null); 098 } 099 100 /** Set the array to display in the table. 101 * @param array The array of records to display in the table. 102 * @param columns The array of strings giving the column names 103 * to display. 104 */ 105 public void display(ArrayToken array, ArrayToken columns) { 106 table.setModel(new ArrayAsTable(array, columns)); 107 table.setTableHeader(new JTableHeader(table.getColumnModel())); 108 _initColumnSizes(table); 109 } 110 111 /////////////////////////////////////////////////////////////////// 112 //// public variables //// 113 114 /** The table representing the matrix. */ 115 public JTable table; 116 117 /////////////////////////////////////////////////////////////////// 118 //// private methods //// 119 120 /** This method picks good column sizes and sets the preferred 121 * size for the table. 122 * It is adapted from the Java Tutorials from Sun Microsystems. 123 */ 124 private void _initColumnSizes(JTable table) { 125 ArrayAsTable model = (ArrayAsTable) table.getModel(); 126 TableColumn column = null; 127 Component component = null; 128 TableCellRenderer headerRenderer = table.getTableHeader() 129 .getDefaultRenderer(); 130 131 int tableWidth = 0; 132 int tableHeight = 0; 133 for (int i = 0; i < model.getColumnCount(); i++) { 134 column = table.getColumnModel().getColumn(i); 135 component = headerRenderer.getTableCellRendererComponent(null, 136 column.getHeaderValue(), false, false, 0, 0); 137 int width = component.getPreferredSize().width; 138 int columnHeight = 0; 139 for (int j = 0; j < model.getRowCount(); j++) { 140 component = table.getDefaultRenderer(model.getColumnClass(i)) 141 .getTableCellRendererComponent(table, 142 model.getValueAt(j, i), false, false, 0, i); 143 int cellWidth = component.getPreferredSize().width; 144 if (cellWidth > width) { 145 width = cellWidth; 146 } 147 columnHeight += component.getPreferredSize().height; 148 } 149 column.setPreferredWidth(width); 150 tableWidth += width; 151 152 if (columnHeight > tableHeight) { 153 tableHeight = columnHeight; 154 } 155 } 156 Dimension tableSize = new Dimension(tableWidth, tableHeight); 157 table.setPreferredSize(tableSize); 158 } 159 160 /////////////////////////////////////////////////////////////////// 161 //// private variables //// 162 163 /** Empty table model. */ 164 private static EmptyTableModel _emptyTableModel = new EmptyTableModel(); 165 166 /////////////////////////////////////////////////////////////////// 167 //// inner class //// 168 169 /** This class provides an implementation of the 170 * TableModel interface for viewing an array of records. 171 */ 172 public static class ArrayAsTable extends AbstractTableModel { 173 // FindBugs suggests making this class static so as to decrease 174 // the size of instances and avoid dangling references. 175 176 /** Construct a table for the specified array to display 177 * all fields in the records contained by the array. 178 * @param array An array of record tokens to display. 179 */ 180 ArrayAsTable(ArrayToken array) { 181 this(array, null); 182 } 183 184 /** Construct a table for the specified array to display 185 * the fields given by <i>columns</i> of records in the specified 186 * <i>array</i>. 187 * @param array An array of record tokens to display. 188 * @param columns An array of string tokens giving the names 189 * of fields to display, or null to display all the fields. 190 */ 191 ArrayAsTable(ArrayToken array, ArrayToken columns) { 192 _array = array; 193 194 if (columns == null) { 195 // Figure out what the column names are. 196 for (int i = 0; i < _array.length(); i++) { 197 RecordToken record = (RecordToken) _array.getElement(i); 198 Iterator labels = record.labelSet().iterator(); 199 while (labels.hasNext()) { 200 String column = (String) labels.next(); 201 if (!_columns.contains(column)) { 202 _columns.add(column); 203 } 204 } 205 } 206 } else { 207 for (int i = 0; i < columns.length(); i++) { 208 StringToken column = (StringToken) columns.getElement(i); 209 _columns.add(column.stringValue()); 210 } 211 } 212 } 213 214 /////////////////////////////////////////////////////////////////// 215 //// public methods //// 216 217 /** Get the column count of the Matrix. 218 * @return the column count. 219 */ 220 @Override 221 public int getColumnCount() { 222 return _columns.size(); 223 } 224 225 /** Get the name of the specified column, which is the column 226 * index as a string. 227 * @param columnIndex The index of the column. 228 * @return The column index as a string. 229 */ 230 @Override 231 public String getColumnName(int columnIndex) { 232 if (columnIndex > _columns.size()) { 233 return ""; 234 } 235 return _columns.get(columnIndex); 236 } 237 238 /** Get the row count of the Matrix. 239 * @return the row count. 240 */ 241 @Override 242 public int getRowCount() { 243 return _array.length(); 244 } 245 246 /** Get the specified entry as a String. 247 * @param row The row number. 248 * @param column The column number. 249 * @return An instance of Token representing the matrix value 250 * at the specified row and column. 251 */ 252 @Override 253 public Object getValueAt(int row, int column) { 254 // There is a bug in JTable, where it happily tries to access 255 // rows and columns that are outside of range. 256 if (row >= _array.length() || column >= _columns.size()) { 257 return ""; 258 } 259 Token element = ((RecordToken) _array.getElement(row)) 260 .get(_columns.get(column)); 261 if (element == null) { 262 return ""; 263 } 264 // Strip off the extra quotation marks if necessary. 265 if (element instanceof StringToken) { 266 return ((StringToken) element).stringValue(); 267 } 268 return element.toString(); 269 } 270 271 /** Remove the specified row from the table. 272 * If the row is out of range, do nothing. 273 * @param row The row to remove, starting with index 0. 274 */ 275 public void removeRow(int row) { 276 if (row < _array.length()) { 277 // Since tokens are immutable, we have to create a whole new token. 278 Token[] newArray = new Token[_array.length() - 1]; 279 for (int i = 0; i < row; i++) { 280 newArray[i] = _array.getElement(i); 281 } 282 for (int i = row + 1; i < _array.length(); i++) { 283 newArray[i - 1] = _array.getElement(i); 284 } 285 try { 286 ArrayToken newToken = new ArrayToken(newArray); 287 _array = newToken; 288 super.fireTableRowsDeleted(row, row); 289 } catch (IllegalActionException e) { 290 throw new InternalErrorException(e); 291 } 292 } 293 } 294 295 /////////////////////////////////////////////////////////////////// 296 //// private members //// 297 298 /** The array for which a Table Model is created. */ 299 private ArrayToken _array = null; 300 301 /** The column names found in the array. */ 302 private List<String> _columns = new LinkedList<String>(); 303 } 304 305 /** This class provides an implementation of the 306 * TableModel interface representing an empty table. 307 * This is used to clear the display. 308 */ 309 private static class EmptyTableModel extends AbstractTableModel { 310 /////////////////////////////////////////////////////////////////// 311 //// public methods //// 312 313 /** Get the column count of the Matrix. 314 * @return Zero. 315 */ 316 @Override 317 public int getColumnCount() { 318 return 0; 319 } 320 321 /** Get the row count of the Matrix. 322 * @return Zero. 323 */ 324 @Override 325 public int getRowCount() { 326 return 0; 327 } 328 329 /** Get the specified entry from the matrix as a String. 330 * @param row The row number. 331 * @param column The column number. 332 * @return An empty String. 333 */ 334 @Override 335 public Object getValueAt(int row, int column) { 336 return ""; 337 } 338 } 339 340 /** Table panel. */ 341 private static class SimpleTable extends JTable { 342 public SimpleTable() { 343 super(); 344 // Adjust column widths automatically. 345 setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); 346 347 // FIXME: Don't hardwire the size here ? 348 // Sadly, as usual with swing, the preferred size of the table has 349 // no effect. Also, the scrollbar does not appear... This is really lame... 350 setPreferredScrollableViewportSize(new Dimension(800, 200)); 351 // This is Java 1.6 specific: 352 // table.setFillsViewportHeight(true); 353 } 354 } 355}