1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.wotonomy.ui.swing.components;
20
21 import java.awt.Color;
22 import java.awt.Component;
23 import java.awt.Cursor;
24 import java.awt.Font;
25 import java.awt.Insets;
26 import java.awt.event.ActionEvent;
27 import java.awt.event.ActionListener;
28 import java.lang.reflect.Method;
29 import java.util.Vector;
30
31 import javax.swing.BorderFactory;
32 import javax.swing.DefaultCellEditor;
33 import javax.swing.JButton;
34 import javax.swing.JCheckBox;
35 import javax.swing.JColorChooser;
36 import javax.swing.JDialog;
37 import javax.swing.JLabel;
38 import javax.swing.JTable;
39 import javax.swing.ListSelectionModel;
40 import javax.swing.SwingUtilities;
41 import javax.swing.border.Border;
42 import javax.swing.table.DefaultTableCellRenderer;
43 import javax.swing.table.TableCellEditor;
44 import javax.swing.table.TableCellRenderer;
45 import javax.swing.table.TableColumn;
46 import javax.swing.table.TableColumnModel;
47 import javax.swing.table.TableModel;
48
49 /***
50 * PropertyEditorTable is a table designed to display and edit the properties
51 * of an object. Because JTable assumes all cells in a column display
52 * the same data type, we have to subclass to determine the class
53 * based on the cell contents.
54 *
55 * @author michael@mpowers.net
56 * @author $Author: cgruber $
57 * @version $Revision: 904 $
58 */
59 public class PropertyEditorTable extends JTable {
60
61
62
63
64
65 /***
66 * Constructs a default JTable which is initialized with a default
67 * data model, a default column model, and a default selection
68 * model.
69 *
70 * @see #createDefaultDataModel
71 * @see #createDefaultColumnModel
72 * @see #createDefaultSelectionModel
73 */
74 public PropertyEditorTable() {
75 super(null, null, null);
76 }
77
78 /***
79 * Constructs a JTable which is initialized with <i>dm</i> as the
80 * data model, a default column model, and a default selection
81 * model.
82 *
83 * @param dm The data model for the table
84 * @see #createDefaultColumnModel
85 * @see #createDefaultSelectionModel
86 */
87 public PropertyEditorTable(TableModel dm) {
88 super(dm, null, null);
89 }
90
91 /***
92 * Constructs a JTable which is initialized with <i>dm</i> as the
93 * data model, <i>cm</i> as the column model, and a default selection
94 * model.
95 *
96 * @param dm The data model for the table
97 * @param cm The column model for the table
98 * @see #createDefaultSelectionModel
99 */
100 public PropertyEditorTable(TableModel dm, TableColumnModel cm) {
101 super(dm, cm, null);
102 }
103
104 /***
105 * Constructs a JTable which is initialized with <i>dm</i> as the
106 * data model, <i>cm</i> as the column model, and <i>sm</i> as the
107 * selection model. If any of the parameters are <b>null</b> this
108 * method will initialize the table with the corresponding
109 * default model. The <i>autoCreateColumnsFromModel</i> flag is set
110 * to false if <i>cm</i> is non-null, otherwise it is set to true
111 * and the column model is populated with suitable TableColumns
112 * for the columns in <i>dm</i>.
113 *
114 * @param dm The data model for the table
115 * @param cm The column model for the table
116 * @param sm The row selection model for the table
117 * @see #createDefaultDataModel
118 * @see #createDefaultColumnModel
119 * @see #createDefaultSelectionModel
120 */
121 public PropertyEditorTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) {
122 super( dm, cm, sm );
123 }
124
125 /***
126 * Constructs a JTable with <i>numRows</i> and <i>numColumns</i> of
127 * empty cells using the DefaultTableModel. The columns will have
128 * names of the form "A", "B", "C", etc.
129 *
130 * @param numRows The number of rows the table holds
131 * @param numColumns The number of columns the table holds
132 */
133 public PropertyEditorTable(int numRows, int numColumns) {
134 super( numRows, numColumns );
135 }
136
137 /***
138 * Constructs a JTable to display the values in the Vector of Vectors,
139 * <i>rowData</i>, with column names, <i>columnNames</i>.
140 * The Vectors contained in <i>rowData</i> should contain the values
141 * for that row. In other words, the value of the cell at row 1,
142 * column 5 can be obtained with the following code:
143 * <p>
144 * <pre>((Vector)rowData.elementAt(1)).elementAt(5);</pre>
145 * <p>
146 * All rows must be of the same length as <i>columnNames</i>.
147 * <p>
148 * @param rowData The data for the new table
149 * @param columnNames Names of each column
150 */
151 public PropertyEditorTable(final Vector rowData, final Vector columnNames) {
152 super( rowData, columnNames );
153 }
154
155 /***
156 * Constructs a JTable to display the values in the two dimensional array,
157 * <i>rowData</i>, with column names, <i>columnNames</i>.
158 * <i>rowData</i> is an Array of rows, so the value of the cell at row 1,
159 * column 5 can be obtained with the following code:
160 * <p>
161 * <pre> rowData[1][5]; </pre>
162 * <p>
163 * All rows must be of the same length as <i>columnNames</i>.
164 * <p>
165 * @param rowData The data for the new table
166 * @param columnNames Names of each column
167 */
168 public PropertyEditorTable(final Object[][] rowData, final Object[] columnNames) {
169 super( rowData, columnNames );
170 }
171
172 /***
173 * Returns the type of the column at the specified view position.
174 *
175 * @return the type of the column at position <I>column</I> in the view
176 * where the first column is column 0.
177 *
178 * Modified mln: now a wrapper for getCellClass()
179 *
180 */
181 public Class getColumnClass(int column) {
182 return getCellClass( 0, column );
183 }
184
185 /***
186 * Returns the type of the cell at the specified view position.
187 *
188 * @return the type of the cell at position <I>row</I>, <I>column</I> in the view
189 * where the first column is column 0.
190 *
191 * Modified mln: new methods
192 *
193 */
194 public Class getCellClass(int row, int column) {
195 TableModel model = getModel();
196 if ( model instanceof PropertyEditorTableModel )
197 return ((PropertyEditorTableModel)model).getCellClass( row, column );
198 else
199 return model.getColumnClass(convertColumnIndexToModel(column));
200 }
201
202 /***
203 * Return an appropriate renderer for the cell specified by this this row and
204 * column. If the TableColumn for this column has a non-null renderer, return that.
205 * If not, find the class of the data in this column (using getColumnClass())
206 * and return the default renderer for this type of data.
207 *
208 * @param row the row of the cell to render, where 0 is the first
209 * @param column the column of the cell to render, where 0 is the first
210 *
211 * Modified mln: calls getCellClass if there's no column model
212 *
213 */
214 public TableCellRenderer getCellRenderer(int row, int column) {
215 TableColumn tableColumn = getColumnModel().getColumn(column);
216 TableCellRenderer renderer = tableColumn.getCellRenderer();
217 if (renderer == null) {
218 renderer = getDefaultRenderer(getCellClass(row, column));
219 }
220 return renderer;
221 }
222
223
224 /***
225 * Return an appropriate editor for the cell specified by this this row and
226 * column. If the TableColumn for this column has a non-null editor, return that.
227 * If not, find the class of the data in this column (using getColumnClass())
228 * and return the default editor for this type of data.
229 *
230 * @param row the row of the cell to edit, where 0 is the first
231 * @param column the column of the cell to edit, where 0 is the first
232 *
233 * Modified mp: calls getCellClass if there's no column model
234 *
235 */
236 public TableCellEditor getCellEditor(int row, int column) {
237 TableColumn tableColumn = getColumnModel().getColumn(column);
238 TableCellEditor editor = tableColumn.getCellEditor();
239 if (editor == null) {
240 editor = getDefaultEditor(getCellClass(row, column));
241 }
242 return editor;
243 }
244
245 protected void createDefaultRenderers() {
246 super.createDefaultRenderers();
247
248
249
250
251
252
253
254
255
256
257
258
259 DefaultTableCellRenderer fontRenderer = new DefaultTableCellRenderer() {
260 public void setValue(Object value) {
261 setText( getFontDescription( (Font) value ) );
262 }
263 };
264 fontRenderer.setHorizontalAlignment(JLabel.RIGHT);
265 setDefaultRenderer(Font.class, fontRenderer);
266
267 setUpColorRenderer( this );
268 setUpMethodRenderer( this );
269 }
270
271 protected String getFontDescription( Font f ) {
272 String s;
273 if ( f != null ) {
274 s = f.getName();
275 if ( f.isBold() ) s += " Bold";
276 if ( f.isItalic() ) s += " Italic";
277 s += " " + f.getSize();
278 } else {
279 s = "";
280 }
281 return s;
282 }
283
284 protected void createDefaultEditors() {
285 super.createDefaultEditors();
286
287
288
289
290
291
292
293
294
295
296
297
298
299 setUpColorEditor( this );
300 setUpMethodEditor( this );
301 }
302
303
304
305
306
307 class ColorRenderer extends JLabel
308 implements TableCellRenderer {
309 Border unselectedBorder = null;
310 Border selectedBorder = null;
311 boolean isBordered = true;
312
313 public ColorRenderer(boolean isBordered) {
314 super();
315 this.isBordered = isBordered;
316 this.setOpaque(true);
317 }
318
319 public Component getTableCellRendererComponent(
320 JTable table, Object color,
321 boolean isSelected, boolean hasFocus,
322 int row, int column) {
323 this.setBackground((Color)color);
324 if (isBordered) {
325 if (isSelected) {
326 if (selectedBorder == null) {
327 selectedBorder = BorderFactory.createMatteBorder(2,5,2,5,
328 table.getSelectionBackground());
329 }
330 this.setBorder(selectedBorder);
331 } else {
332 if (unselectedBorder == null) {
333 unselectedBorder = BorderFactory.createMatteBorder(2,5,2,5,
334 table.getBackground());
335 }
336 this.setBorder(unselectedBorder);
337 }
338 }
339 return this;
340 }
341 }
342
343 private void setUpColorRenderer(JTable table) {
344 table.setDefaultRenderer(Color.class,
345 new ColorRenderer(true));
346 }
347
348
349 private void setUpColorEditor(JTable table) {
350
351 final JButton button = new JButton("") {
352 public void setText(String s) {
353
354 }
355 };
356 button.setBackground(Color.white);
357 button.setBorderPainted(false);
358 button.setMargin(new Insets(0,0,0,0));
359
360
361
362 final ColorEditor colorEditor = new ColorEditor(button);
363 table.setDefaultEditor(Color.class, colorEditor);
364
365
366 final JColorChooser colorChooser = new JColorChooser();
367
368
369
370
371
372 ActionListener okListener = new ActionListener() {
373 public void actionPerformed(ActionEvent e) {
374 colorEditor.currentColor = colorChooser.getColor();
375 }
376 };
377 final JDialog dialog = JColorChooser.createDialog(button,
378 "Pick a Color",
379 true,
380 colorChooser,
381 okListener,
382 null);
383
384
385 button.addActionListener(new ActionListener() {
386 public void actionPerformed(ActionEvent e) {
387 button.setBackground(colorEditor.currentColor);
388 colorChooser.setColor(colorEditor.currentColor);
389
390
391
392 dialog.show();
393 }
394 });
395 }
396
397
398
399
400
401
402
403
404
405 class ColorEditor extends DefaultCellEditor {
406 Color currentColor = null;
407
408 public ColorEditor(JButton b) {
409 super(new JCheckBox());
410
411
412 editorComponent = b;
413 setClickCountToStart(1);
414
415
416 b.addActionListener(new ActionListener() {
417 public void actionPerformed(ActionEvent e) {
418 fireEditingStopped();
419 }
420 });
421 }
422
423 protected void fireEditingStopped() {
424 super.fireEditingStopped();
425 }
426
427 public Object getCellEditorValue() {
428 return currentColor;
429 }
430
431 public Component getTableCellEditorComponent(JTable table,
432 Object value,
433 boolean isSelected,
434 int row,
435 int column) {
436 ((JButton)editorComponent).setText(value.toString());
437 currentColor = (Color)value;
438 return editorComponent;
439 }
440 }
441
442 class MethodRenderer extends JLabel
443 implements TableCellRenderer {
444
445 Method theMethod = null;
446 JTable theTable = null;
447
448 public MethodRenderer() {
449 super();
450 }
451
452 public Component getTableCellRendererComponent(
453 JTable table, Object method,
454 boolean isSelected, boolean hasFocus,
455 int row, int column) {
456 theMethod = (Method) method;
457 theTable = table;
458 setText( " " + theMethod.getReturnType().getName() );
459 return this;
460 }
461 }
462
463 private void setUpMethodRenderer(JTable table) {
464 table.setDefaultRenderer(Method.class,
465 new MethodRenderer());
466 }
467
468
469
470
471
472 class MethodEditor extends DefaultCellEditor {
473 Method theMethod = null;
474 JTable theTable = null;
475
476 public MethodEditor(JButton b) {
477 super(new JCheckBox());
478
479
480 editorComponent = b;
481 setClickCountToStart(1);
482
483
484 b.addActionListener(new ActionListener() {
485 public void actionPerformed(ActionEvent e) {
486 fireEditingStopped();
487 }
488 });
489 }
490
491 protected void fireEditingStopped() {
492 super.fireEditingStopped();
493 }
494
495 public Object getCellEditorValue() {
496 return theMethod;
497 }
498
499 public Component getTableCellEditorComponent(JTable table,
500 Object value,
501 boolean isSelected,
502 int row,
503 int column) {
504 ((JButton)editorComponent).setText(value.toString());
505 theMethod = (Method)value;
506 theTable = table;
507 return editorComponent;
508 }
509 }
510
511
512 private void setUpMethodEditor(PropertyEditorTable table) {
513
514 final JButton button = new JButton("invoking method") {
515 public void setText(String s) {
516
517 }
518 };
519 button.setBackground(Color.white);
520 button.setBorderPainted(false);
521 button.setMargin(new Insets(0,0,0,0));
522
523
524
525 final MethodEditor methodEditor = new MethodEditor(button);
526 table.setDefaultEditor(Method.class, methodEditor);
527
528
529 final PropertyEditorTable theTable = table;
530 button.addActionListener(new ActionListener() {
531 public void actionPerformed(ActionEvent e) {
532
533 Component parent = SwingUtilities.getRoot( theTable );
534 if ( parent == null ) parent = theTable;
535
536 Cursor oldCursor = parent.getCursor();
537 parent.setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) );
538
539 Object result = null;
540 Object inspectedObject = ((PropertyEditorTableModel)
541 methodEditor.theTable.getModel()).inspectedObject;
542 try
543 {
544 methodEditor.theMethod.setAccessible( true );
545 result = methodEditor.theMethod.invoke(
546 inspectedObject, (Object[])null );
547 }
548 catch ( Exception exc )
549 {
550 System.err.println( "PropertyEditorTable.MethodRenderer.actionPerformed: " +
551 "Error occurred: " + exc );
552 }
553 theTable.methodInvoked( inspectedObject, methodEditor.theMethod, result );
554
555 parent.setCursor( oldCursor );
556 }
557 });
558 }
559
560 /***
561 * Called by the method cell editor when a method is invoked.
562 * @param anObject The object upon which the method was invoked.
563 * @param aMethod The method that was invoked.
564 * @param aResult The result of the method invocation; may be null.
565 */
566 public void methodInvoked( Object anObject, Method aMethod, Object aResult )
567 {
568 System.out.println( aMethod.getName() + ": " + aResult );
569 }
570 }
571
572