KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdesktop > jdnc > JNTable


1 /*
2  * $Id: JNTable.java,v 1.13 2005/02/01 16:51:47 kleopatra Exp $
3  *
4  * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
5  * Santa Clara, California 95054, U.S.A. All rights reserved.
6  */

7
8 package org.jdesktop.jdnc;
9
10 import java.awt.Color JavaDoc;
11 import java.awt.Dimension JavaDoc;
12 import java.awt.Font JavaDoc;
13 import java.awt.Insets JavaDoc;
14 import java.awt.event.ActionEvent JavaDoc;
15 import java.awt.event.MouseAdapter JavaDoc;
16 import java.awt.event.MouseEvent JavaDoc;
17 import java.awt.print.PrinterException JavaDoc;
18 import java.beans.PropertyChangeEvent JavaDoc;
19 import java.beans.PropertyChangeListener JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.net.URL JavaDoc;
22
23 import javax.swing.AbstractAction JavaDoc;
24 import javax.swing.Action JavaDoc;
25 import javax.swing.Icon JavaDoc;
26 import javax.swing.JButton JavaDoc;
27 import javax.swing.JCheckBoxMenuItem JavaDoc;
28 import javax.swing.JPopupMenu JavaDoc;
29 import javax.swing.JScrollPane JavaDoc;
30 import javax.swing.JTable JavaDoc;
31 import javax.swing.ScrollPaneConstants JavaDoc;
32 import javax.swing.event.ChangeEvent JavaDoc;
33 import javax.swing.event.ListSelectionEvent JavaDoc;
34 import javax.swing.event.TableColumnModelEvent JavaDoc;
35 import javax.swing.event.TableColumnModelListener JavaDoc;
36 import javax.swing.event.TableModelEvent JavaDoc;
37 import javax.swing.event.TableModelListener JavaDoc;
38 import javax.swing.table.DefaultTableCellRenderer JavaDoc;
39 import javax.swing.table.DefaultTableModel JavaDoc;
40 import javax.swing.table.JTableHeader JavaDoc;
41 import javax.swing.table.TableCellRenderer JavaDoc;
42 import javax.swing.table.TableColumn JavaDoc;
43 import javax.swing.table.TableColumnModel JavaDoc;
44 import javax.swing.table.TableModel JavaDoc;
45
46 import org.jdesktop.swing.JXTable;
47 import org.jdesktop.swing.JXTreeTable;
48 import org.jdesktop.swing.data.DefaultTableModelExt;
49 import org.jdesktop.swing.decorator.FilterPipeline;
50 import org.jdesktop.swing.decorator.Highlighter;
51 import org.jdesktop.swing.decorator.HighlighterPipeline;
52 import org.jdesktop.swing.icon.ColumnControlIcon;
53 import org.jdesktop.swing.table.ColumnHeaderRenderer;
54 import org.jdesktop.swing.table.DefaultTableColumnModelExt;
55 import org.jdesktop.swing.table.TableCellRenderers;
56 import org.jdesktop.swing.table.TableColumnExt;
57 import org.jdesktop.swing.table.TableColumnModelExt;
58
59 /**
60  * High level table component which displays tabular data from a data model
61  * in a scrollable view.
62  *
63  * @author Ramesh Gupta
64  * @author Amy Fowler
65  *
66  * @javabean.class
67  * displayName="Table Component"
68  * name="JNTable"
69  * shortDesctiption="A simplified table component"
70  *
71  * @javabean.icons
72  * color16="/javax/swing/beaninfo/images/JTableColor16.gif"
73  * color32="/javax/swing/beaninfo/images/JTableColor32.gif"
74  */

75 public class JNTable extends JNComponent {
76     /**
77      * The default row height for rows in this JNTable.
78      * TODO It seems like this is ok, but needs to be somewhat influenced by the
79      * laf?
80      */

81     public int DEFAULT_ROW_HEIGHT = 22;
82     /**
83      * The default margin between rows
84      * TODO It seems like this is ok, but needs to be somewhat influenced by the
85      * laf?
86      */

87     public int DEFAULT_ROW_MARGIN = 1;
88     /**
89      *
90      * TODO It seems like this is ok, but needs to be somewhat influenced by the
91      * laf?
92      */

93     public int DEFAULT_VISIBLE_ROW_COUNT = 20;
94     /**
95      * The default margin between columns
96      * TODO It seems like this is ok, but needs to be somewhat influenced by the
97      * laf?
98      */

99     public int DEFAULT_COLUMN_MARGIN = 0;
100
101     /**
102      * If true, then this component should show the column control button
103      */

104     private boolean hasColumnControl = false;
105     /**
106      * If true, then the left-most column becomes a "row header". The row header
107      * will remain locked as the first column when scrolling horizontally
108      */

109     private boolean rowHeaderLocked = false;
110     /**
111      * The JXTable to use to render the row header column
112      */

113     private JXTable rowHeaderTable = null;
114     private TableColumn JavaDoc firstColumn = null;
115
116     /**
117      * The JNTable automatically places the table component into this scrollPane
118      */

119     protected JScrollPane JavaDoc scrollPane;
120     /**
121      * The JXTable being wrapped by this JNTable. This wrapped table is used
122      * as the delegate for doing table functions, such as painting.
123      */

124     protected JXTable jxtable;
125
126     /**
127      * A button that allows the user to select which columns to display, and
128      * which to hide
129      */

130     private ColumnControlButton columnControlButton = null;
131     
132     private ColumnPropertyHighlighter highlighter;
133
134
135     /**
136      * Constructs a JNTable component with a DefaultTableModel instance as the
137      * data model.
138      */

139     public JNTable() {
140         this(new DefaultTableModel JavaDoc());
141     }
142
143     /**
144      * Constructs a JNTable component that displays a row/column view for
145      * the specified data model.
146      *
147      * @param model data model which holds the data for this table
148      * @exception throws IllegalArgumentException if model is null
149      */

150     public JNTable(TableModel JavaDoc model) {
151         this(new JXTable(model));
152     }
153
154     /**
155      * Constructs a JNTable loaded with data from the URL.
156      *
157      * @param a non-null url which represents tabular data
158      */

159     public JNTable(URL JavaDoc url) {
160         setTable(new JXTable());
161         DefaultTableModelExt model = new DefaultTableModelExt(url);
162         setModel(model);
163         try {
164             model.startLoading();
165         } catch (IOException JavaDoc ioe) {
166             sendMessage(ioe.getMessage());
167         }
168         initUI();
169     }
170
171     protected JNTable(JTable JavaDoc jxtable) {
172         setTable((JXTable)jxtable);
173         setModel(jxtable.getModel());
174         initUI();
175     }
176
177     /**
178      * Initializes the JNTable gui
179      */

180     private void initUI() {
181         setRowHeight(DEFAULT_ROW_HEIGHT);
182         setRowMargin(DEFAULT_ROW_MARGIN);
183         scrollPane = new JScrollPane JavaDoc(jxtable);
184         add(scrollPane); // defaults to BorderLayout.CENTER
185
}
186
187     /**
188      * Uses the specified table as the basis for this JNTable
189      *
190      * TODO This method does not currently appear to handle changing
191      * JXTables in midstream appropriately. There is no logic to handle
192      * updating the column control, and there is no logic to handle
193      * updating the RowHeaderColumn, if one is in use.
194      */

195     protected void setTable(JXTable jxtable) {
196         this.jxtable = jxtable;
197         setComponent(jxtable);
198     }
199
200     /**
201      *
202      * @return the internal JXTable instance for this component
203      */

204     public JXTable getTable() {
205         return jxtable;
206     }
207
208     /**
209      * Set the model for the table.
210      *
211      * @param model table model which holds the data for this table
212      * @javabean.property bound="true" shortDescription="Set the model"
213      */

214     public void setModel(TableModel JavaDoc model) {
215         TableModel JavaDoc oldModel = jxtable.getModel();
216         // bit of a hack. Dont call JXTreeTable.setModel()
217
if (!(jxtable instanceof JXTreeTable)) {
218             jxtable.setModel(model);
219             firePropertyChange("model", oldModel, model);
220         }
221         // should the old adapter be unregistered?
222
model.addTableModelListener(new TableModelAdapter());
223     }
224
225     /**
226      *
227      * @return data model which holds the data for this table
228      */

229     public TableModel JavaDoc getModel() {
230         return jxtable.getModel();
231     }
232
233     /**
234      * Adds the specified view column to this table
235      * @param column TableColumnExt which holds state for the view column
236      */

237     public void addColumn(TableColumnExt column) {
238         jxtable.addColumn(column);
239     }
240
241     /**
242      * Removes the specified column from this JNTable
243      */

244     public void removeColumn(TableColumnExt column) {
245         jxtable.removeColumn(column);
246     }
247
248     /**
249      *
250      * @param name String containing the logical name of the column
251      * @return TableColumnExt which holds state for the view column
252      */

253     public TableColumnExt getColumn(String JavaDoc name) {
254         return jxtable.getColumnExt(name);
255     }
256
257     /**
258      * Returns the column which is currently located at the specified column
259      * view index. Note that since columns may be re-ordered in the view,
260      * either programmatically or by the user, a column's view index may
261      * change over time, therefore it is recommended to retrieve column objects
262      * by name when accessing specific columns.
263      * @see #getColumn
264      * @param currentViewIndex integer index which contains the position of the
265      * column
266      * @return TableColumnExt which holds state for the view column
267      */

268     public TableColumnExt getColumn(int currentViewIndex) {
269         return jxtable.getColumnExt(currentViewIndex);
270     }
271
272     /**
273      *
274      * @return Color background color used to render odd rows in the table.
275      */

276     public Color JavaDoc getOddRowBackground() {
277         return highlighter != null? highlighter.getOddRowBackground() : null;
278     }
279
280     /**
281      * Sets the background color for odd rows in the table.
282      * @param color background color used to render odd rows in the table.
283      * @javabean.property
284      * bound="true"
285      * shortDescription="Set the background color for odd rows"
286      */

287     public void setOddRowBackground(Color JavaDoc color) {
288         initHighlighter();
289         Color JavaDoc old = highlighter.getOddRowBackground();
290         highlighter.setOddRowBackground(color);
291         firePropertyChange("oddRowBackground", old, color);
292     }
293
294     /**
295      *
296      * @return Color background color used to render even rows in the table.
297      */

298     public Color JavaDoc getEvenRowBackground() {
299         return highlighter != null? highlighter.getEvenRowBackground() : null;
300     }
301
302     /**
303      * Sets the background color for even rows in the table.
304      * @param color background color used to render even rows in the table.
305      * @javabean.property bound="true" shortDescription="Set the background color for even rows"
306      */

307     public void setEvenRowBackground(Color JavaDoc color) {
308         initHighlighter();
309         Color JavaDoc old = highlighter.getEvenRowBackground();
310         highlighter.setEvenRowBackground(color);
311         firePropertyChange("evenRowBackground", old, color);
312     }
313
314     /**
315      * Initializes the highlighter
316      */

317     private void initHighlighter() {
318         if (highlighter == null) {
319             highlighter = new ColumnPropertyHighlighter();
320
321             HighlighterPipeline pipeline = jxtable.getHighlighters();
322             if (pipeline != null) {
323                 // insert private highlighter in beginning of pipeline
324
pipeline.addHighlighter(highlighter, true);
325             } else {
326                 pipeline = new HighlighterPipeline(new Highlighter[] { highlighter });
327                 jxtable.setHighlighters(pipeline);
328             }
329         }
330     }
331
332     /**
333      *
334      * @return boolean indicating whether the table provides a control which
335      * enables the end user to modify column visibility
336      */

337     public boolean getHasColumnControl() {
338         return hasColumnControl;
339     }
340
341     /**
342      * Sets whether the column control should be made visible. The column
343      * control is a popup which enables or disables the visibility of columns.
344      *
345      * @param hasControl true to turn the column control on; otherwise false
346      * @javabean.property
347      * bound="true"
348      * shortDescription="Toggle the visibility of the column control"
349      */

350     public void setHasColumnControl(boolean hasControl) {
351         boolean old = this.hasColumnControl;
352         if (hasControl) {
353             if (columnControlButton == null) {
354                 // Create cloak button
355
columnControlButton = new ColumnControlButton(new ColumnControlIcon());
356             }
357             // Put button in scrollpane corner
358
scrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, columnControlButton);
359             
360             scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
361         }
362         else {
363             scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
364             
365             try {
366                 scrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, null);
367             }
368             catch (Exception JavaDoc ex) {
369                 // Ignore spurious exception thrown by JScrollPane. This is a Swing bug!
370
}
371         }
372         hasColumnControl = hasControl;
373         firePropertyChange("hasColumnControl", old, hasControl);
374     }
375
376     /**
377      *
378      */

379     public boolean isRowHeaderLocked() {
380         return rowHeaderLocked;
381     }
382
383     /**
384      * Initialization property. Do not set after table has been shown.
385      *
386      * @param lockState
387      */

388     public void setRowHeaderLocked(boolean lockState) {
389         if (rowHeaderLocked != lockState) {
390             if (firstColumn == null) {
391                 firstColumn = initRowHeader();
392             }
393             if (lockState) {
394                 lockRowHeader();
395             }
396             else {
397                 unlockRowHeader();
398             }
399             rowHeaderLocked = lockState;
400         }
401     }
402
403     protected TableColumn JavaDoc initRowHeader() {
404         rowHeaderTable = new JXTable();
405         rowHeaderTable.setAutoCreateColumnsFromModel(false);
406         rowHeaderTable.setModel(jxtable.getModel());
407         
408         TableColumnModel JavaDoc tableColumnModel = jxtable.getColumnModel();
409         TableColumn JavaDoc firstColumn = tableColumnModel.getColumn(0);
410
411         rowHeaderTable.addColumn(firstColumn);
412         rowHeaderTable.setSelectionModel(jxtable.getSelectionModel());
413         rowHeaderTable.setRowHeight(jxtable.getRowHeight());
414         
415         //table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
416
rowHeaderTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
417         rowHeaderTable.getTableHeader().setReorderingAllowed(false);
418         rowHeaderTable.getTableHeader().setResizingAllowed(false);
419         rowHeaderTable.setPreferredScrollableViewportSize(
420             new Dimension JavaDoc(firstColumn.getPreferredWidth() +
421                 rowHeaderTable.getColumnModel().getColumnMargin(), 0));
422
423 // TableColumnModel headerTableColumnModel = rowHeaderTable.getColumnModel();
424
// for (int i = headerTableColumnModel.getColumnCount() - 1; i > 0; i--) {
425
// headerTableColumnModel.removeColumn(headerTableColumnModel.getColumn(i));
426
// }
427

428         return firstColumn;
429     }
430
431     protected void lockRowHeader() {
432         TableColumnModel JavaDoc tableColumnModel = jxtable.getColumnModel();
433         tableColumnModel.removeColumn(firstColumn);
434         scrollPane.setRowHeaderView(rowHeaderTable);
435         scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER,
436                              rowHeaderTable.getTableHeader());
437         jxtable.revalidate();
438     }
439
440     protected void unlockRowHeader() {
441         TableColumnModel JavaDoc tableColumnModel = jxtable.getColumnModel();
442         tableColumnModel.addColumn(firstColumn);
443         int numColumns = tableColumnModel.getColumnCount();
444         tableColumnModel.moveColumn(numColumns - 1, 0);
445         scrollPane.setRowHeaderView(null);
446         jxtable.revalidate();
447     }
448
449     /**
450      *
451      * @return integer containing the height of a table row in pixels
452      */

453     public int getRowHeight() {
454         return jxtable.getRowHeight();
455     }
456
457     /**
458      *
459      * @param value integer containing the height of a table row in pixels
460      * @javabean.property bound="true" shortDescription="Set the row height"
461      */

462     public void setRowHeight(int value) {
463         int old = jxtable.getRowHeight();
464         jxtable.setRowHeight(value);
465         firePropertyChange("rowHeight", old, value);
466     }
467
468     /**
469      *
470      * @return integer containing the number of visible rows which should
471      * be displayed in the scrollable view when the table is first shown
472      */

473     public int getPreferredVisibleRowCount() {
474         return jxtable.getVisibleRowCount();
475     }
476
477     /**
478      * Sets the number of visible rows which should be displayed in the
479      * scrollable view when the table is first shown. If set to a value greater
480      * or equal to 0, then the table will attempt to initialize the viewport size
481      * to display exactly this number of rows. Subsequent size changes to the
482      * user-interface (due to end-user or programmatic resizing) may cause the
483      * number of visible rows to change.
484      *
485      * @param value integer containing the number of visible rows which should
486      * be displayed in the scrollable view when the table is first shown
487      * @javabean.property bound="true" shortDescription="Set the visible number of rows"
488      */

489     public void setPreferredVisibleRowCount(int value) {
490         int old = jxtable.getVisibleRowCount();
491         jxtable.setVisibleRowCount(value);
492         firePropertyChange("preferredVisibleRowCount", old, value);
493     }
494
495     public int getColumnMargin() {
496         return jxtable.getColumnMargin();
497     }
498
499     public void setColumnMargin(int value) {
500         jxtable.setColumnMargin(value);
501     }
502
503     public int getRowMargin() {
504         return jxtable.getRowMargin();
505     }
506
507     public void setRowMargin(int value) {
508         jxtable.setRowMargin(value);
509     }
510
511     public int getSelectionMode() {
512         return jxtable.getSelectionMode();
513     }
514
515     public void setSelectionMode(int mode) {
516         jxtable.setSelectionMode(mode);
517     }
518
519     public boolean getShowHorizontalLines() {
520         return jxtable.getShowHorizontalLines();
521     }
522
523     public void setShowHorizontalLines(boolean value) {
524         jxtable.setShowHorizontalLines(value);
525     }
526
527     public boolean getShowVerticalLines() {
528         return jxtable.getShowVerticalLines();
529     }
530
531     public void setShowVerticalLines(boolean value) {
532         jxtable.setShowVerticalLines(value);
533     }
534
535     public FilterPipeline getFilters() {
536         return jxtable.getFilters();
537     }
538
539     public void setFilters(FilterPipeline pipeline) {
540         jxtable.setFilters(pipeline);
541     }
542
543     public HighlighterPipeline getHighlighters() {
544         return jxtable.getHighlighters();
545     }
546
547     public void setHighlighters(HighlighterPipeline pipeline) {
548         /**@todo(aim) how to reconcile this with internal highligher used for
549          * column-specific properties & alternate row colors?
550          * for now, this will override those.
551          */

552         jxtable.setHighlighters(pipeline);
553     }
554
555     public void setBackground(Color JavaDoc color) {
556         // UI delegate installs colors/fonts in constructor before jxtable is initialized
557
if (jxtable != null) {
558             jxtable.setBackground(color);
559         }
560         super.setBackground(color);
561     }
562
563     public void setForeground(Color JavaDoc color) {
564         // UI delegate installs colors/fonts in constructor before jxtable is initialized
565
if (jxtable != null) {
566             jxtable.setForeground(color);
567         }
568         super.setForeground(color);
569     }
570
571     public void setFont(Font JavaDoc font) {
572         // UI delegate installs colors/fonts in constructor before jxtable is initialized
573
if (jxtable != null) {
574             jxtable.setFont(font);
575         }
576         super.setFont(font);
577     }
578
579     public void setGridColor(Color JavaDoc color) {
580         jxtable.setGridColor(color);
581     }
582
583     /*********************************************************
584      * Convenience methods for setting table header properties
585      *********************************************************/

586
587     /**
588      * Sets the background of the table's column headers to the specified color.
589      * If the table has no header, this method does nothing.
590      * @param headerBackground Color used to render background of table column headers
591      */

592     public void setHeaderBackground(Color JavaDoc headerBackground) {
593         JTableHeader JavaDoc header = jxtable.getTableHeader();
594         if (header != null) {
595             header.setBackground(headerBackground);
596         }
597     }
598
599     /**
600      * @return Color used to render background of table column headers or
601      * null if the table has no header.
602      */

603     public Color JavaDoc getHeaderBackground() {
604         JTableHeader JavaDoc header = jxtable.getTableHeader();
605         if (header != null) {
606             return header.getBackground();
607         }
608         return null;
609     }
610
611     /**
612      * Sets the foreground of the table's column headers to the specified color.
613      * If the table has no header, this method does nothing.
614      * @param headerForeground Color used to render foreground of table column headers
615      */

616     public void setHeaderForeground(Color JavaDoc headerForeground) {
617         JTableHeader JavaDoc header = getTable().getTableHeader();
618         if (header != null) {
619             header.setForeground(headerForeground);
620         }
621     }
622
623     /**
624      * @return Color used to render foreground of table column headers or
625      * null if the table has no header.
626      */

627     public Color JavaDoc getHeaderForeground() {
628         JTableHeader JavaDoc header = getTable().getTableHeader();
629         if (header != null) {
630             return header.getForeground();
631         }
632         return null;
633     }
634
635     /**
636      * Sets the font of the table's column headers to the specified font.
637      * If the table has no header, this method does nothing.
638      * @param headerFont Font used to render text in the table column headers
639      */

640     public void setHeaderFont(Font JavaDoc headerFont) {
641         JTableHeader JavaDoc header = getTable().getTableHeader();
642         if (header != null) {
643             header.setFont(headerFont);
644         }
645     }
646
647     /**
648      * @return Font used to render text in the table column headers or null
649      * if the table has no header.
650      */

651     public Font JavaDoc getHeaderFont() {
652         JTableHeader JavaDoc header = getTable().getTableHeader();
653         if (header != null) {
654             return header.getFont();
655         }
656         return null;
657     }
658
659     private ColumnHeaderRenderer getColumnHeaderRenderer() {
660         JTableHeader JavaDoc header = getTable().getTableHeader();
661         if (header != null) {
662             TableCellRenderer JavaDoc hr = header.getDefaultRenderer();
663             if (hr instanceof ColumnHeaderRenderer) {
664                 return (ColumnHeaderRenderer) hr;
665             }
666         }
667         return null;
668     }
669
670     /**
671      * Sets the icon which is displayed in a column's header when it has been
672      * sorted in ascending order.
673      * If the table has no header, this method does nothing.
674      * @param upIcon Icon which indicates ascending sort on a column
675      */

676     public void setHeaderSortUpIcon(Icon JavaDoc upIcon) {
677         ColumnHeaderRenderer headerRenderer = getColumnHeaderRenderer();
678         if (headerRenderer != null) {
679             headerRenderer.setUpIcon(upIcon);
680         }
681     }
682
683     /**
684      *
685      * @return Icon which indicates ascending sort on a column
686      */

687     public Icon JavaDoc getHeaderSortUpIcon() {
688         ColumnHeaderRenderer headerRenderer = getColumnHeaderRenderer();
689         if (headerRenderer != null) {
690             return headerRenderer.getUpIcon();
691         }
692         return null;
693     }
694
695     /**
696      * Sets the icon which is displayed in a column's header when it has been
697      * sorted in descending order.
698      * If the table has no header, this method does nothing.
699      * @param downIcon Icon which indicates descending sort on a column
700      */

701     public void setHeaderSortDownIcon(Icon JavaDoc downIcon) {
702         ColumnHeaderRenderer headerRenderer = getColumnHeaderRenderer();
703         if (headerRenderer != null) {
704             headerRenderer.setDownIcon(downIcon);
705         }
706     }
707
708     /**
709      *
710      * @return Icon which indicates descending sort on a column
711      */

712     public Icon JavaDoc getHeaderSortDownIcon() {
713         ColumnHeaderRenderer headerRenderer = getColumnHeaderRenderer();
714         if (headerRenderer != null) {
715             return headerRenderer.getDownIcon();
716         }
717         return null;
718     }
719
720     /*********************************************************
721      * Convenience methods for setting table column properties
722      *********************************************************/

723
724     /**
725      * Convenience method for obtaining the class of a named column in the table.
726      * This method will obtain the class from the underlying data model.
727      * @param columnName String containing the logical name of the column
728      * @return Class representing the data-type for values in the column
729      */

730     public Class JavaDoc getColumnClass(String JavaDoc columnName) {
731         return jxtable.getColumnClass(jxtable.getColumnModel().
732                                      getColumnIndex(columnName));
733     }
734
735     /**
736      * Sets the background color for all cells in the specified named column.
737      * By default, cells are rendered in the background color of the table component
738      * or using the alternate row color properties (if set),
739      * so this method should only be used if a column needs to have an individual
740      * background color.
741      * @param columnName String containing the logical name of the column
742      * @param background Color used to render the background of cells in the column
743      */

744     public void setColumnBackground(String JavaDoc columnName, Color JavaDoc background) {
745         initHighlighter();
746         highlighter.setColumnBackground(columnName, background);
747     }
748
749     /**
750      * @param columnName String containing the logical name of the column
751      * @return Color used to render the background of cells in the column or
752      * null if no background color was set on the column
753      */

754     public Color JavaDoc getColumnBackground(String JavaDoc columnName) {
755         return highlighter != null? highlighter.getColumnBackground(columnName) : null;
756     }
757
758     /**
759      * Sets the foreground color for all cells in the specified named column.
760      * By default, cells are rendered in the foreground color of the table component
761      * so this method should only be used if a column needs to have an individual
762      * foreground color. Setting the column foreground to <code>null</code>
763      * will cause the column to be rendered with the default (table foreground).
764      * @param columnName String containing the logical name of the column
765      * @param foreground Color used to render the foreground of cells in the column
766      */

767     public void setColumnForeground(String JavaDoc columnName, Color JavaDoc foreground) {
768         initHighlighter();
769         highlighter.setColumnForeground(columnName, foreground);
770     }
771
772     /**
773      * @param columnName String containing the logical name of the column
774      * @return Color used to render the foreground of cells in the column or
775      * null if no foreground color was set on the column
776      */

777     public Color JavaDoc getColumnForeground(String JavaDoc columnName) {
778         return highlighter != null? highlighter.getColumnForeground(columnName) : null;
779     }
780
781     /**
782      * Sets the font for all text rendering of cells in the specified named column.
783      * By default, cells are rendered in the font of the table component
784      * so this method should only be used if a column needs to be rendered in
785      * an individual font.
786      * @param columnName String containing the logical name of the column
787      * @param font Font used to render text inside cells in the column
788      */

789     public void setColumnFont(String JavaDoc columnName, Font JavaDoc font) {
790         initHighlighter();
791         highlighter.setColumnFont(columnName, font);
792     }
793
794     /**
795      * @param columnName String containing the logical name of the column
796      * @return Font used to render text within the cells in the column or
797      * null if no font was set on the column
798      */

799     public Font JavaDoc getColumnFont(String JavaDoc columnName) {
800         return highlighter != null? highlighter.getColumnFont(columnName) : null;
801     }
802
803     /**
804      * Sets the horizontal alignment of content rendered inside cells in the
805      * specified named column.
806      * By default, cells are rendered with a horizontal alignment which is
807      * determined by the column's type (e.g. strings are leading-aligned,
808      * numbers are trailing-aligned, dates are center-aligned, etc).
809      * This method should only be used if a column needs to override the
810      * default alignment for the column's type. Setting the column alignment
811      * to -1 will restore the alignment to its default setting.
812      * @see javax.swing.JLabel#setHorizontalAlignment
813      * @param columnName String containing the logical name of the column
814      * @param alignment integer representing the horizontal alignment within the column
815      */

816     public void setColumnHorizontalAlignment(String JavaDoc columnName, int alignment) {
817         // Unfortunately we cannot use highlighters to set column alignment on
818
// the shared table renderers because there is no way to reset the alignment
819
// property after rendering (whereas background, foreground, and font are
820
// almost always re-initialized for each cell rendering op). Thus,
821
// to customize alignment safely, we need to install a dedicated column
822
// renderer so the alignment setting does not disturb other columns which
823
// use the shared renderers.
824

825         TableColumnExt column = getColumn(columnName);
826         TableCellRenderer JavaDoc renderer = column.getCellRenderer();
827         if (renderer == null) {
828             renderer = TableCellRenderers.getNewDefaultRenderer(getColumnClass(
829                 columnName));
830             column.setCellRenderer(renderer);
831             if (renderer instanceof DefaultTableCellRenderer JavaDoc) {
832                 column.putClientProperty("saveAlignment",
833                        new Integer JavaDoc(((DefaultTableCellRenderer JavaDoc)renderer).getHorizontalAlignment()));
834             }
835         }
836         column.putClientProperty("alignment", new Integer JavaDoc(alignment));
837         if (renderer != null && renderer instanceof DefaultTableCellRenderer JavaDoc) {
838             if (alignment != -1) {
839                 ((DefaultTableCellRenderer JavaDoc)renderer).setHorizontalAlignment(alignment);
840             } else {
841                 // restore default alignment
842
Integer JavaDoc restoreAlignment = (Integer JavaDoc)column.getClientProperty("saveAlignment");
843                 if (restoreAlignment != null) {
844                     ((DefaultTableCellRenderer JavaDoc)renderer).setHorizontalAlignment(
845                         restoreAlignment.intValue());
846                 }
847             }
848         }
849     }
850
851     /**
852      * @param columnName String containing the logical name of the column
853      * @return integer representing the horizontal alignment within the column
854      * or -1 if column alignment was not explicitly set
855      */

856     public int getColumnHorizontalAlignment(String JavaDoc columnName) {
857         TableColumnExt column = getColumn(columnName);
858         Integer JavaDoc alignment = (Integer JavaDoc)column.getClientProperty("alignment");
859         return alignment != null? alignment.intValue() : -1;
860     }
861
862     /**
863      * Sets the prototype value for the specified named column.
864      * The prototype value is used to compute the preferred width of the column
865      * when the table's size if first initialized, therefore this property
866      * should be set before the table is shown.
867      * The value should be an instance of the column's type.
868      * Setting the prototype value to <code>null</code> will cause the the
869      * preferred width to fallback to a static pixel width.
870      * @see #getColumnClass
871      * @param columnName String containing the logical name of the column
872      * @param prototype Object used to calculate preferred width of column
873      */

874     public void setColumnPrototypeValue(String JavaDoc columnName, Object JavaDoc prototype) {
875         TableColumnExt column = getColumn(columnName);
876         column.setPrototypeValue(prototype);
877     }
878
879     /**
880      * @param columnName String containing the logical name of the column
881      * @return Object used to calculate preferred width of column or null
882      * if the preferred width should be based on a static pixel value
883      */

884     public Object JavaDoc getColumnPrototypeValue(String JavaDoc columnName) {
885         TableColumnExt column = getColumn(columnName);
886         return column.getPrototypeValue();
887     }
888
889     public boolean print() throws PrinterException JavaDoc {
890         boolean printed = getTable().print();
891         sendMessage("Print " + (printed? "complete." : "cancelled."));
892         return printed;
893     }
894
895     /*
896
897     public int search(String searchString) {
898         return table.search(searchString);
899     }
900
901     public int search(String searchString, int columnIndex) {
902         return table.search(searchString, columnIndex);
903     }
904
905     public int search(Pattern pattern) {
906         return table.search(pattern);
907     }
908
909     public int search(Pattern pattern, int columnIndex) {
910         return table.search(pattern, columnIndex);
911     }
912
913     public int search(Pattern pattern, int columnIndex, boolean backward) {
914         return table.search(pattern, columnIndex, backward);
915     }
916     */

917
918     private class TableModelAdapter implements TableModelListener JavaDoc {
919
920         public void tableChanged(TableModelEvent JavaDoc e) {
921             if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {
922                 if (columnControlButton != null) {
923                     //TODO broken
924
// columnControlButton.bind(getTable());
925
}
926             }
927             // Update the status message with the number of rows/columns.
928
setRowColumnStatus();
929         }
930     }
931
932     private void setRowColumnStatus() {
933         TableColumnModel JavaDoc columnModel = jxtable.getColumnModel();
934         TableModel JavaDoc model = jxtable.getModel();
935
936         if (support != null) {
937             support.fireMessage(model.getRowCount() + " rows, " +
938                                 columnModel.getColumnCount() + " columns");
939         }
940     }
941
942     public String JavaDoc toString() {
943         return super.toString() + "; " + getTable().toString();
944     }
945
946
947     /**
948      * This class is installed in the upper right corner of the table and
949      * is a control which allows for toggling the visibilty of individual
950      * columns.
951      *
952      * TODO: the table reference is a potential leak
953      */

954     private final class ColumnControlButton extends JButton JavaDoc {
955         /**
956          * Used to construct a button with a reasonable preferred size
957          */

958         public final static String JavaDoc TITLE = "x";
959
960         private JPopupMenu JavaDoc popupMenu = null;
961         
962         private MouseAdapter JavaDoc mouseListener = new MouseAdapter JavaDoc() {
963             public void mousePressed(MouseEvent JavaDoc ev) {
964                 if (popupMenu.getComponentCount() > 0) {
965                     JButton JavaDoc button = ColumnControlButton.this;
966                     Dimension JavaDoc buttonSize = button.getSize();
967                     popupMenu.show(button,
968                                    buttonSize.width -
969                                    popupMenu.getPreferredSize().width,
970                                    buttonSize.height);
971                 }
972             }
973         };
974         
975         private PropertyChangeListener JavaDoc columnModelChangeListener = new PropertyChangeListener JavaDoc() {
976             public void propertyChange(PropertyChangeEvent JavaDoc evt) {
977                 ((TableColumnModel JavaDoc)evt.getOldValue()).removeColumnModelListener(columnModelListener);
978                 if (((TableColumnModel JavaDoc)evt.getNewValue()) instanceof TableColumnModelExt) {
979                     setEnabled(true);
980                     ((TableColumnModel JavaDoc)evt.getNewValue()).addColumnModelListener(columnModelListener);
981                     populatePopupMenu();
982                 } else {
983                     setEnabled(false);
984                 }
985             }
986         };
987         
988         private TableColumnModelListener JavaDoc columnModelListener = new TableColumnModelListener JavaDoc() {
989             /** Tells listeners that a column was added to the model. */
990             public void columnAdded(TableColumnModelEvent JavaDoc e) {
991                 if (isVisibilityChange(e, true)) return;
992                 populatePopupMenu();
993             }
994
995             /** Tells listeners that a column was removed from the model. */
996             public void columnRemoved(TableColumnModelEvent JavaDoc e) {
997                if (isVisibilityChange(e, false)) return;
998                populatePopupMenu();
999             }
1000            
1001            /**
1002             * check if the add/remove event is triggered by a
1003             * move to/from the invisible columns.
1004             *
1005             * PRE: the event must be received in columnAdded/Removed.
1006             *
1007             * @param e the received event
1008             * @param added if true the event is assumed to be received via
1009             * columnAdded, otherwise via columnRemoved.
1010             * @return
1011             */

1012            private boolean isVisibilityChange(TableColumnModelEvent JavaDoc e, boolean added) {
1013                // can't tell
1014
if (!(e.getSource() instanceof DefaultTableColumnModelExt)) return false;
1015                DefaultTableColumnModelExt model = (DefaultTableColumnModelExt) e.getSource();
1016                if (added) {
1017                    return model.isAddedFromInvisibleEvent(e.getToIndex());
1018                } else {
1019                    return model.isRemovedToInvisibleEvent(e.getFromIndex());
1020                }
1021            }
1022            /** Tells listeners that a column was repositioned. */
1023            public void columnMoved(TableColumnModelEvent JavaDoc e){}
1024
1025            /** Tells listeners that a column was moved due to a margin change. */
1026            public void columnMarginChanged(ChangeEvent JavaDoc e){}
1027
1028            /**
1029             * Tells listeners that the selection model of the
1030             * TableColumnModel changed.
1031             */

1032            public void columnSelectionChanged(ListSelectionEvent JavaDoc e){}
1033        };
1034        
1035        public ColumnControlButton() {
1036            super(TITLE);
1037            init();
1038        }
1039        public ColumnControlButton(Action JavaDoc action) {
1040            super(action);
1041            init();
1042        }
1043        public ColumnControlButton(Icon JavaDoc icon) {
1044            super(icon);
1045            init();
1046        }
1047        public ColumnControlButton(String JavaDoc title) {
1048            super(title);
1049            init();
1050        }
1051        public ColumnControlButton(String JavaDoc title, Icon JavaDoc icon) {
1052            super(title, icon);
1053            init();
1054        }
1055
1056        /**
1057         * Initialize the column control button's gui
1058         */

1059        private void init() {
1060            setFocusPainted(false);
1061            setMargin(new Insets JavaDoc(1,2,2,1)); // Make this LAF-independent
1062

1063            setEnabled(jxtable.getColumnModel() instanceof TableColumnModelExt);
1064            
1065            //create the popup menu
1066
popupMenu = new JPopupMenu JavaDoc();
1067            //initially populate the menu
1068
populatePopupMenu();
1069            //attach a TableColumnModel listener so that if the column model
1070
//is replaced or changed the popup menu will be regenerated
1071
jxtable.addPropertyChangeListener("columnModel", columnModelChangeListener);
1072            jxtable.getColumnModel().addColumnModelListener(columnModelListener);
1073            
1074        }
1075        
1076        /**
1077         * Populates the popup menu based on the current columns in the
1078         * TableColumnModel for jxtable
1079         */

1080        protected void populatePopupMenu() {
1081            popupMenu.removeAll();
1082            // For each column in the view, add a JCheckBoxMenuItem to popup
1083
TableColumnModel JavaDoc columnModel = jxtable.getColumnModel();
1084            for (int i=0; i<columnModel.getColumnCount(); i++) {
1085                TableColumn JavaDoc column = columnModel.getColumn(i);
1086                // Create a new JCheckBoxMenuItem
1087
JCheckBoxMenuItem JavaDoc item = new JCheckBoxMenuItem JavaDoc(
1088                    column.getHeaderValue().toString(), true);
1089                item.putClientProperty("column", column);
1090                // Attach column visibility action to each menu item
1091
item.addActionListener(columnVisibilityAction);
1092                popupMenu.add(item); // Add item to popup menu
1093
}
1094        }
1095
1096        private AbstractAction JavaDoc columnVisibilityAction = new AbstractAction JavaDoc() {
1097            public void actionPerformed(ActionEvent JavaDoc ev) {
1098                JCheckBoxMenuItem JavaDoc item = (JCheckBoxMenuItem JavaDoc) ev.getSource();
1099                TableColumnExt column = (TableColumnExt) item.getClientProperty("column");
1100                if (item.isSelected()) { // was not selected, but is now...
1101
column.setVisible(true);
1102                    /** @todo Figure out how to restore column index */
1103                }
1104                else {
1105                    // Remove the column unless it's the last one.
1106
TableColumnModelExt model = (TableColumnModelExt)jxtable.getColumnModel();
1107                    if (model.getColumnCount() - 1 == 0) {
1108                        // reselect the checkbox because we cannot unselect the last one
1109
item.setSelected(true);
1110                    } else {
1111                        column.setVisible(false);
1112                    }
1113                }
1114            }
1115        };
1116
1117        public void setEnabled(boolean b) {
1118            if (b && jxtable.getColumnModel() instanceof TableColumnModelExt) {
1119                // Hook popup to button (default button action is to show popup)
1120
removeMouseListener(mouseListener);
1121                addMouseListener(mouseListener);
1122            } else {
1123                removeMouseListener(mouseListener);
1124            }
1125            super.setEnabled(b);
1126        }
1127    } // end class ColumnControlButton
1128
}
Popular Tags