KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdesktop > swing > JXTable


1 /*
2  * $Id: JXTable.java,v 1.16 2005/02/22 17:14:59 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.swing;
9
10 import java.text.MessageFormat JavaDoc;
11 import java.util.ArrayList JavaDoc;
12 import java.util.Date JavaDoc;
13 import java.util.Enumeration JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.List JavaDoc;
16 import java.util.Hashtable JavaDoc;
17 import java.util.Timer JavaDoc;
18 import java.util.TimerTask JavaDoc;
19 import java.util.Vector JavaDoc;
20 import java.util.regex.Pattern JavaDoc;
21 import javax.print.attribute.HashPrintRequestAttributeSet JavaDoc;
22 import javax.print.attribute.PrintRequestAttributeSet JavaDoc;
23
24 import java.awt.Color JavaDoc;
25 import java.awt.Component JavaDoc;
26 import java.awt.Dimension JavaDoc;
27 import java.awt.EventQueue JavaDoc;
28 import java.awt.FontMetrics JavaDoc;
29 import java.awt.Graphics JavaDoc;
30 import java.awt.Point JavaDoc;
31 import java.awt.Rectangle JavaDoc;
32 import java.awt.event.*;
33 import java.awt.print.Printable JavaDoc;
34 import java.awt.print.PrinterException JavaDoc;
35 import java.awt.print.PrinterJob JavaDoc;
36
37 import java.text.DateFormat JavaDoc;
38 import java.text.NumberFormat JavaDoc;
39
40 import javax.swing.ActionMap JavaDoc;
41 import javax.swing.Icon JavaDoc;
42 import javax.swing.ImageIcon JavaDoc;
43 import javax.swing.JCheckBox JavaDoc;
44 import javax.swing.JLabel JavaDoc;
45 import javax.swing.JTable JavaDoc;
46 import javax.swing.JViewport JavaDoc;
47 import javax.swing.ListSelectionModel JavaDoc;
48 import javax.swing.UIDefaults JavaDoc;
49 import javax.swing.event.TableModelEvent JavaDoc;
50 import javax.swing.table.DefaultTableCellRenderer JavaDoc;
51 import javax.swing.table.JTableHeader JavaDoc;
52 import javax.swing.table.TableCellRenderer JavaDoc;
53 import javax.swing.table.TableColumn JavaDoc;
54 import javax.swing.table.TableColumnModel JavaDoc;
55 import javax.swing.table.TableModel JavaDoc;
56
57 import org.jdesktop.swing.data.Link;
58 import org.jdesktop.swing.data.MetaData;
59 import org.jdesktop.swing.data.MetaDataProvider;
60 import org.jdesktop.swing.data.DefaultTableModelExt;
61
62 import org.jdesktop.swing.table.ColumnHeaderRenderer;
63 import org.jdesktop.swing.table.TableColumnExt;
64 import org.jdesktop.swing.table.TableColumnModelExt;
65
66 import org.jdesktop.swing.decorator.ComponentAdapter;
67 import org.jdesktop.swing.decorator.FilterPipeline;
68 import org.jdesktop.swing.decorator.HighlighterPipeline;
69 import org.jdesktop.swing.decorator.PipelineEvent;
70 import org.jdesktop.swing.decorator.PipelineListener;
71 import org.jdesktop.swing.decorator.Sorter;
72 import org.jdesktop.swing.table.DefaultTableColumnModelExt;
73
74 /**
75  * JXTable
76  *
77  * @author Ramesh Gupta
78  * @author Amy Fowler
79  * @author Mark Davidson
80  */

81 public class JXTable extends JTable JavaDoc implements PipelineListener, Searchable {
82
83 public static boolean TRACE = false;
84     /**
85      * Printing mode that prints the table at its current size,
86      * spreading both columns and rows across multiple pages if necessary.
87      */

88     public static final int PRINT_MODE_NORMAL = 0;
89
90     /**
91      * Printing mode that scales the output smaller, if necessary,
92      * to fit the table's entire width (and thereby all columns) on each page;
93      * Rows are spread across multiple pages as necessary
94      */

95     public static final int PRINT_MODE_FIT_WIDTH = 1;
96
97     protected Sorter sorter = null;
98     protected FilterPipeline filters = null;
99     protected HighlighterPipeline highlighters = null;
100
101     // MUST ALWAYS ACCESS dataAdapter through accessor method!!!
102
private final ComponentAdapter dataAdapter = new TableAdapter(this);
103
104     // No need to define a separate JTableHeader subclass!
105
private final static MouseAdapter headerListener = new MouseAdapter() {
106         // MouseAdapter must be stateless
107
public void mouseClicked(MouseEvent e) {
108             JTableHeader JavaDoc header = (JTableHeader JavaDoc) e.getSource();
109             JXTable table = (JXTable) header.getTable();
110             if (!table.isSortable()) return;
111             if ((e.getModifiersEx() & e.SHIFT_DOWN_MASK) == e.SHIFT_DOWN_MASK) {
112                 table.resetSorter();
113             }
114             else {
115                 
116                 int column = header.getColumnModel().getColumnIndexAtX(e.getX());
117                 if (column >= 0) {
118                     table.setSorter(column);
119                 }
120             }
121             header.repaint();
122         }
123     };
124
125     /**
126      * A flag to indicate whether or not the table is currently being printed.
127      * Used by print() and prepareRenderer() to disable indication of the
128      * selection and focused cell while printing.
129      */

130     private transient boolean isPrinting = false;
131
132     private boolean sortable = false;
133     private int visibleRowCount = 18;
134
135     public JXTable() {
136         init();
137     }
138
139     public JXTable(TableModel JavaDoc dm) {
140         super(dm);
141         init();
142     }
143
144     public JXTable(TableModel JavaDoc dm, TableColumnModel JavaDoc cm) {
145         super(dm, cm);
146         init();
147     }
148
149     public JXTable(TableModel JavaDoc dm, TableColumnModel JavaDoc cm, ListSelectionModel JavaDoc sm) {
150         super(dm, cm, sm);
151         init();
152     }
153
154     public JXTable(int numRows, int numColumns) {
155         super(numRows, numColumns);
156         init();
157     }
158
159     public JXTable(Vector JavaDoc rowData, Vector JavaDoc columnNames) {
160         super(rowData, columnNames);
161         init();
162     }
163
164     public JXTable(Object JavaDoc[][] rowData, Object JavaDoc[] columnNames) {
165         super(rowData, columnNames);
166         init();
167     }
168
169     protected void init() {
170         setSortable(true);
171         // Register the actions that this class can handle.
172
ActionMap JavaDoc map = getActionMap();
173         map.put("print", new Actions("print"));
174         map.put("find", new Actions("find"));
175
176         // Add a link handler to to the table.
177
// XXX note: this listener represents overhead if no columns are links.
178
// Beter to detect if the table has a link column and add the handler.
179
LinkHandler handler = new LinkHandler();
180         addMouseListener(handler);
181         addMouseMotionListener(handler);
182     }
183
184
185     /**
186      * Returns a new instance of the default renderer for the specified class.
187      * This differs from <code>getDefaultRenderer()</code> in that it returns a <b>new</b>
188      * instance each time so that the renderer may be set and customized on
189      * a particular column.
190      *
191      * @param columnClass Class of value being rendered
192      * @return TableCellRenderer instance which renders values of the specified type
193      */

194     public TableCellRenderer JavaDoc getNewDefaultRenderer(Class JavaDoc columnClass) {
195         TableCellRenderer JavaDoc renderer = getDefaultRenderer(columnClass);
196         if (renderer != null) {
197             try {
198                 return (TableCellRenderer JavaDoc) renderer.getClass().newInstance();
199             } catch (Exception JavaDoc e) {
200                 e.printStackTrace();
201             }
202         }
203         return null;
204     }
205
206     private void setLazyValue(Hashtable JavaDoc h, Class JavaDoc c, String JavaDoc s) {
207         h.put(c, new UIDefaults.ProxyLazyValue JavaDoc(s));
208     }
209
210     private void setLazyRenderer(Class JavaDoc c, String JavaDoc s) {
211         setLazyValue(defaultRenderersByColumnClass, c, s);
212     }
213
214     /**
215      * Creates default cell renderers for objects, numbers, doubles, dates,
216      * booleans, icons, and links.
217      *
218      */

219     protected void createDefaultRenderers() {
220         // super.createDefaultRenderers();
221
// This duplicates JTable's functionality in order to make the renderers
222
// available in getNewDefaultRenderer(); If JTable's renderers either
223
// were public, or it provided a factory for *new* renderers, this would
224
// not be needed
225

226         defaultRenderersByColumnClass = new UIDefaults JavaDoc();
227
228         // Objects
229
setLazyRenderer(Object JavaDoc.class,
230                         "javax.swing.table.DefaultTableCellRenderer");
231
232         // Numbers
233
setLazyRenderer(Number JavaDoc.class, "org.jdesktop.swing.JXTable$NumberRenderer");
234
235         // Doubles and Floats
236
setLazyRenderer(Float JavaDoc.class, "org.jdesktop.swing.JXTable$DoubleRenderer");
237         setLazyRenderer(Double JavaDoc.class, "org.jdesktop.swing.JXTable$DoubleRenderer");
238
239         // Dates
240
setLazyRenderer(Date JavaDoc.class, "org.jdesktop.swing.JXTable$DateRenderer");
241
242         // Icons and ImageIcons
243
setLazyRenderer(Icon JavaDoc.class, "org.jdesktop.swing.JXTable$IconRenderer");
244         setLazyRenderer(ImageIcon JavaDoc.class, "org.jdesktop.swing.JXTable$IconRenderer");
245
246         // Booleans
247
setLazyRenderer(Boolean JavaDoc.class, "org.jdesktop.swing.JXTable$BooleanRenderer");
248
249         // Other
250
setLazyRenderer(Link.class, "org.jdesktop.swing.JXTable$LinkRenderer");
251     }
252
253
254     /**
255      * A small class which dispatches actions.
256      * TODO: Is there a way that we can make this static?
257      */

258     private class Actions extends UIAction {
259         Actions(String JavaDoc name) {
260             super(name);
261         }
262
263         public void actionPerformed(ActionEvent evt) {
264             if ("print".equals(getName())) {
265                 try {
266                     print();
267                 }
268                 catch (PrinterException JavaDoc ex) {
269                     //REMIND(aim): should invoke pluggable application error handler
270
ex.printStackTrace();
271                 }
272             }
273             else if ("find".equals(getName())) {
274                 find();
275             }
276         }
277     }
278
279     private JXFindDialog dialog = null;
280     private boolean automaticSortDisabled;
281
282     private void find() {
283         if (dialog == null) {
284             dialog = new JXFindDialog(this);
285         }
286         dialog.setVisible(true);
287     }
288
289     /**
290      * Sets &quot;sortable&quot; property indicating whether or not this table
291          * supports sortable columns. If <code>sortable</code> is <code>true</code>
292      * then sorting will be enabled on all columns whose <code>sortable</code>
293          * property is <code>true</code>. If <code>sortable</code> is <code>false</code>
294          * then sorting will be disabled for all columns, regardless of each column's
295          * individual <code>sorting</code> property. The default is <code>true</code>.
296      * @see TableColumnExt#isSortable
297      * @see TableColumnExt#setSortable
298      * @param sortable boolean indicating whether or not this table supports
299      * sortable columns
300      */

301     public void setSortable(boolean sortable) {
302         if (sortable == isSortable()) return;
303         this.sortable = sortable;
304         firePropertyChange("sortable", !sortable, sortable);
305         //JW @todo: this is a hack!
306
if (sorter != null) {
307            contentsChanged(null);
308         }
309         
310     }
311     
312     public boolean isSortable() {
313         return sortable;
314     }
315
316     
317 // public void setAutomaticSort(boolean automaticEnabled) {
318
// this.automaticSortDisabled = !automaticEnabled;
319
//
320
// }
321
//
322
// public boolean isAutomaticSort() {
323
// return !automaticSortDisabled;
324
// }
325

326     public void tableChanged(TableModelEvent JavaDoc e) {
327         Selection selection = new Selection(this);
328         if (filters != null) {
329             filters.flush(); // will call contentsChanged()
330
}
331         else if (sorter != null) {
332             sorter.refresh();
333 // if (isAutomaticSort()) {
334
// sorter.refresh();
335
// }
336
}
337         
338         super.tableChanged(e);
339         restoreSelection(selection);
340     }
341
342     public void contentsChanged(PipelineEvent e) {
343         removeSorter();
344         clearSelection();
345
346         // Force private rowModel in JTable to null;
347
setRowHeight(getRowHeight()); // Ugly!
348

349         revalidate();
350         repaint();
351     }
352
353     public int getRowCount() {
354         int count;
355         if (filters == null) {
356             count = getModel().getRowCount();
357         }
358         else {
359             count = filters.getOutputSize();
360         }
361         return count;
362     }
363
364     /**
365      * Convert row index from view coordinates to model coordinates
366      * accounting for the presence of sorters and filters.
367      *
368      * @param row row index in view coordinates
369      * @return row index in model coordinates
370      */

371     public int convertRowIndexToModel(int row) {
372         if (sorter == null) {
373             if (filters == null) {
374                 return row;
375             }
376             else {
377                 // delegate conversion to the filters pipeline
378
return filters.convertRowIndexToModel(row);
379             }
380         }
381         else {
382             // after performing its own conversion, the sorter
383
// delegates the conversion to the filters pipeline, if any
384
return sorter.convertRowIndexToModel(row);
385         }
386     }
387
388     /**
389      * Convert row index from model coordinates to view coordinates
390      * accounting for the presence of sorters and filters.
391      *
392      * @param row row index in model coordinates
393      * @return row index in view coordinates
394      */

395     public int convertRowIndexToView(int row) {
396         if (sorter == null) {
397             if (filters == null) {
398                 return row;
399             }
400             else {
401                 // delegate conversion to the filters pipeline
402
return filters.convertRowIndexToView(row);
403             }
404         }
405         else {
406             // before performing its own conversion, the sorter
407
// delegates the conversion to the filters pipeline, if any
408
return sorter.convertRowIndexToView(row);
409         }
410     }
411
412     /**
413      * {@inheritDoc}
414      */

415     public Object JavaDoc getValueAt(int row, int column) {
416         if (sorter == null) { // have interactive sorter?
417
if (filters == null) { // have filter pipeline?
418
return super.getValueAt(row, column); // unsorted, unfiltered
419
}
420             else { // filtered
421
return filters.getValueAt(row, convertColumnIndexToModel(column));
422             }
423         }
424         else { // interactively sorted, and potentially filtered
425
return sorter.getValueAt(row, convertColumnIndexToModel(column));
426         }
427     }
428
429     public void setValueAt(Object JavaDoc aValue, int row, int column) {
430         if (sorter == null) {
431             if (filters == null) {
432                 super.setValueAt(aValue, row, column);
433             }
434             else {
435                 filters.setValueAt(aValue, row, convertColumnIndexToModel(column));
436             }
437         }
438         else {
439             sorter.setValueAt(aValue, row, convertColumnIndexToModel(column));
440         }
441     }
442
443     public boolean isCellEditable(int row, int column) {
444         if (sorter == null) {
445             if (filters == null) {
446                 return super.isCellEditable(row, column);
447             }
448             else {
449                 return filters.isCellEditable(row, convertColumnIndexToModel(column));
450             }
451         }
452         else {
453             return sorter.isCellEditable(row, convertColumnIndexToModel(column));
454         }
455     }
456
457     public void setModel(TableModel JavaDoc newModel) {
458         //JW: need to clear here because super.setModel
459
// calls tableChanged...
460
// fixing #173
461
clearSelection();
462         super.setModel(newModel);
463         use(filters);
464     }
465
466
467     /**
468      * Adds private mouse listener to the table header (for sorting support)
469      * before handing it off to the super class for processing.
470      *
471      * @param tableHeader
472      */

473     public void setTableHeader(JTableHeader JavaDoc tableHeader) {
474         // This method is also called during construction of JTable
475
if (tableHeader != null) {
476             tableHeader.addMouseListener(headerListener);
477             tableHeader.setDefaultRenderer(new ColumnHeaderRenderer());
478         }
479         super.setTableHeader(tableHeader);
480     }
481
482 /*
483     protected JTableHeader createDefaultTableHeader() {
484         return new JTableHeaderExt(columnModel);
485     }
486 */

487
488     protected TableColumnModel JavaDoc createDefaultColumnModel() {
489         return new DefaultTableColumnModelExt();
490     }
491
492     private void restoreSelection(Selection selection) {
493         clearSelection(); // call overridden version
494

495         for (int i = 0; i < selection.selected.length; i++) {
496             // JW: make sure we convert valid row indices (in model coordinates) only
497
// fix #16
498
if ((selection.selected[i] != selection.lead) && (selection.selected[i] < getModel().getRowCount())) {
499                 int index = convertRowIndexToView(selection.selected[i]);
500                 selectionModel.addSelectionInterval(index, index);
501             }
502         }
503
504         // JW: make sure we convert valid row indices (in model coordinates) only
505
// fix #16
506
if ((selection.lead >= 0) && (selection.lead < getModel().getRowCount())) {
507             selection.lead = convertRowIndexToView(selection.lead);
508             selectionModel.addSelectionInterval(selection.lead, selection.lead);
509         }
510     }
511
512     public FilterPipeline getFilters() {
513         return filters;
514     }
515
516     /**
517      * setModel() and setFilters() may be called in either order.
518      *
519      * @param pipeline
520      */

521     private void use(FilterPipeline pipeline) {
522         if (pipeline != null) {
523             // check JW: adding listener multiple times (after setModel)?
524
if (initialUse(pipeline)) {
525                 pipeline.addPipelineListener(this);
526                 pipeline.assign(getComponentAdapter());
527             }
528             pipeline.flush();
529         }
530     }
531     
532     /**
533      *
534      * @param pipeline must be != null
535      * @return true is not yet used in this JXTable, false otherwise
536      */

537     private boolean initialUse(FilterPipeline pipeline) {
538         PipelineListener[] l = pipeline.getPipelineListeners();
539         for (int i = 0; i < l.length; i++) {
540             if (this.equals(l[i])) return false;
541         }
542         return true;
543     }
544
545     public void setFilters(FilterPipeline pipeline) {
546         unsetFilters();
547         doSetFilters(pipeline);
548     }
549
550     private void unsetFilters() {
551         if (filters == null) return;
552         // fix#125: cleanup old filters
553
filters.removePipelineListener(this);
554         // hacking around -
555
//brute force update of sorter by removing
556
contentsChanged(null);
557     }
558
559     private void doSetFilters(FilterPipeline pipeline) {
560         filters = pipeline;
561         use(filters);
562     }
563
564     public HighlighterPipeline getHighlighters() {
565         return highlighters;
566     }
567
568     public void setHighlighters(HighlighterPipeline pipeline) {
569         highlighters = pipeline;
570     }
571
572     private void removeSorter() {
573 // // fixing #167: remove from pipeline
574
// // moved to refreshSorter
575
// if (sorter != null) {
576
// sorter.interpose(null, getComponentAdapter(), null);
577
// }
578
sorter = null;
579         getTableHeader().repaint();
580     }
581     
582     
583     /*
584      * Used by headerListener
585      */

586     protected void resetSorter() {
587         if (sorter != null) {
588             Selection selection = new Selection(this);
589             removeSorter();
590             restoreSelection(selection);
591         }
592     }
593
594     private Sorter refreshSorter(int columnIndex) {
595         TableColumn JavaDoc col = getColumnModel().getColumn(columnIndex);
596         if (col instanceof TableColumnExt) {
597             TableColumnExt column = (TableColumnExt) col;
598             Sorter newSorter = column.getSorter();
599             if (newSorter != null) {
600                 // JW: hacking around #167: un-assign from filters
601
// this should be done somewhere else!
602
newSorter.interpose(null, getComponentAdapter(), null);
603                 // filter pipeline may be null!
604
newSorter.interpose(filters, getComponentAdapter(), sorter); // refresh
605
return newSorter;
606             }
607         }
608         return sorter;
609     }
610
611     /*
612      * Used by headerListener
613      */

614     protected void setSorter(int columnIndex) {
615         Selection selection = new Selection(this);
616         if (sorter == null) {
617             sorter = refreshSorter(columnIndex); // create and refresh
618
}
619         else {
620             int modelColumnIndex = convertColumnIndexToModel(columnIndex);
621             if (sorter.getColumnIndex() == modelColumnIndex) {
622                 sorter.toggle();
623             }
624             else {
625                 sorter = refreshSorter(columnIndex); // create and refresh
626
}
627         }
628         restoreSelection(selection);
629     }
630
631     /*
632      * Used by ColumnHeaderRenderer.getTableCellRendererComponent()
633      */

634     public Sorter getSorter(int columnIndex) {
635         return sorter == null ? null :
636             sorter.getColumnIndex() == convertColumnIndexToModel(columnIndex) ?
637             sorter : null;
638     }
639
640     /**
641      * Remove all columns
642      */

643     protected void removeColumns() {
644         /** @todo promote this method to superclass, and
645          * change createDefaultColumnsFromModel() to call this method */

646         TableColumnModel JavaDoc cm = getColumnModel();
647         while (cm.getColumnCount() > 0) {
648             cm.removeColumn(cm.getColumn(0));
649         }
650     }
651
652     public List JavaDoc getColumns() {
653         return null; /** @todo Implement this */
654     }
655
656     /**
657      * Returns the <code>TableColumn</code> object for the column in the table
658      * whose identifier is equal to <code>identifier</code>, when compared using
659      * <code>equals</code>.
660      *
661      * @return the <code>TableColumn</code> object that matches the identifier
662      * @exception IllegalArgumentException
663      * if <code>identifier</code> is <code>null</code>
664      * or no <code>TableColumn</code> has this identifier
665      *
666      * @param identifier the identifier object
667      */

668
669     public TableColumnExt getColumnExt(Object JavaDoc identifier) {
670         return (TableColumnExt)super.getColumn(identifier);
671     }
672
673     public TableColumnExt getColumnExt(int viewColumnIndex) {
674         return (TableColumnExt) getColumnModel().getColumn(viewColumnIndex);
675     }
676
677     /**
678      * Sets &quot;sortable&quot; property indicating whether or not this table
679          * supports sortable columns. If <code>sortable</code> is <code>true</code>
680      * then sorting will be enabled on all columns whose <code>sortable</code>
681          * property is <code>true</code>. If <code>sortable</code> is <code>false</code>
682          * then sorting will be disabled for all columns, regardless of each column's
683          * individual <code>sorting</code> property. The default is <code>true</code>.
684      * @see TableColumnExt#isSortable
685      * @see TableColumnExt#setSortable
686      * @param sortable boolean indicating whether or not this table supports
687      * sortable columns
688      */

689     /* public void setSortable(boolean sortable) {
690             boolean old = this.sortable;
691             this.sortable = sortable;
692             if (!old && sortable) {
693                 TableModel model = getModel();
694                 if (model != null) {
695                     // wrap model
696                     super.setModel(new TableSorter(getModel()));
697                 }
698                 setTableHeader(new SortableTableHeader(getColumnModel()));
699             } else if (old && !sortable) {
700                 TableModel model = getModel();
701                 if (model instanceof TableSorter) {
702                     // unwrap model
703                     super.setModel(((TableSorter)model).getModel());
704                 }
705                 setTableHeader(createDefaultTableHeader());
706             }
707             firePropertyChange("sortable", old, sortable);
708         }
709         public boolean isSortable() {
710             return sortable;
711         }
712      */

713
714     /**
715      * Returns the default table model object, which is
716      * a <code>DefaultTableModel</code>. A subclass can override this
717      * method to return a different table model object.
718      *
719      * @return the default table model object
720      * @see org.jdesktop.swing.table.DefaultTableModelExt
721      */

722     protected TableModel JavaDoc createDefaultDataModel() {
723         return new DefaultTableModelExt();
724     }
725
726     public void createDefaultColumnsFromModel() {
727         TableModel JavaDoc model = getModel();
728         if (model != null) {
729             // Create new columns from the data model info
730
// Note: it's critical to create the new columns before
731
// deleting the old ones. Why?
732
int modelColumnCount = model.getColumnCount();
733             TableColumn JavaDoc newColumns[] = new TableColumn JavaDoc[modelColumnCount];
734             for (int i = 0; i < newColumns.length; i++) {
735                 newColumns[i] = createColumn(i);
736             }
737
738             if (model instanceof MetaDataProvider) {
739                 MetaData metaData[] = ((MetaDataProvider)model).getMetaData();
740                 for(int i = 0; i < metaData.length; i++) {
741                     newColumns[i].setIdentifier(metaData[i].getName());
742                     newColumns[i].setHeaderValue(metaData[i].getLabel());
743                 }
744             }
745
746             // Remove any current columns
747
TableColumnModel JavaDoc columnModel = getColumnModel();
748             while (columnModel.getColumnCount() > 0) {
749                 columnModel.removeColumn(columnModel.getColumn(0));
750             }
751
752             // Now add the new columns to the column model
753
for (int i = 0; i < newColumns.length; i++) {
754                 addColumn(newColumns[i]);
755             }
756         }
757     }
758
759     protected TableColumn JavaDoc createColumn(int modelIndex) {
760         return new TableColumnExt(modelIndex);
761     }
762
763
764     /**
765      * Returns the margin between columns.
766      *
767      * @return the margin between columns
768      */

769     public int getColumnMargin() {
770         return getColumnModel().getColumnMargin();
771     }
772
773     /**
774      * Sets the margin between columns.
775      *
776          * @param value margin between columns; must be greater than or equal to zero.
777      */

778     public void setColumnMargin(int value) {
779         getColumnModel().setColumnMargin(value);
780     }
781
782     /**
783      * Returns the selection mode used by this table's selection model.
784      *
785      * @return the selection mode used by this table's selection model
786      */

787     public int getSelectionMode() {
788         return getSelectionModel().getSelectionMode();
789     }
790
791     /**
792      * Returns the decorated <code>Component</code> used as a stamp to render
793      * the specified cell. Overrides superclass version to provide support for
794      * cell decorators.
795      *
796      * @param renderer the <code>TableCellRenderer</code> to prepare
797      * @param row the row of the cell to render, where 0 is the first row
798          * @param column the column of the cell to render, where 0 is the first column
799      * @return the decorated <code>Component</code> used as a stamp to render the specified cell
800      * @see org.jdesktop.swing.decorator.Highlighter
801      */

802     public Component JavaDoc prepareRenderer(TableCellRenderer JavaDoc renderer, int row,
803                                      int column) {
804         Component JavaDoc stamp = super.prepareRenderer(renderer, row, column);
805         if (highlighters == null) {
806             return stamp; // no need to decorate renderer with highlighters
807
}
808         else {
809             // MUST ALWAYS ACCESS dataAdapter through accessor method!!!
810
ComponentAdapter adapter = getComponentAdapter();
811             adapter.row = row;
812             adapter.column = column;
813             return highlighters.apply(stamp, adapter);
814         }
815     }
816
817     protected ComponentAdapter getComponentAdapter() {
818         // MUST ALWAYS ACCESS dataAdapter through accessor method!!!
819
return dataAdapter;
820     }
821
822     public int search(String JavaDoc searchString) {
823         return search(searchString, -1);
824     }
825
826     public int search(String JavaDoc searchString, int columnIndex) {
827         Pattern JavaDoc pattern = null;
828         if (searchString != null) {
829             return search(Pattern.compile(searchString, 0), columnIndex);
830         }
831         return -1;
832     }
833
834     public int search(Pattern JavaDoc pattern) {
835         return search(pattern, -1);
836     }
837
838     public int search(Pattern JavaDoc pattern, int startIndex) {
839         return search(pattern, startIndex, false);
840     }
841
842     // Save the last column with the match.
843
private int lastCol = 0;
844
845     /**
846      * @param startIndex row to start search
847      * @return row with a match.
848      */

849     public int search(Pattern JavaDoc pattern, int startIndex, boolean backwards) {
850         if (pattern == null) {
851             lastCol = 0;
852             return -1;
853         }
854         int rows = getRowCount();
855         int endCol = getColumnCount();
856
857         int startRow = startIndex + 1;
858         int matchRow = -1;
859
860         if (backwards == true) {
861             for (int r = startRow; r >= 0 && matchRow == -1; r--) {
862                 for (int c = endCol; c >= 0; c--) {
863                     Object JavaDoc value = getValueAt(r, c);
864                     if ( (value != null) &&
865                         pattern.matcher(value.toString()).find()) {
866                         changeSelection(r, c, false, false);
867                         matchRow = r;
868                         lastCol = c;
869                         break; // No need to search other columns
870
}
871                 }
872                 if (matchRow == -1) {
873                     lastCol = endCol;
874                 }
875             }
876         }
877         else {
878             for (int r = startRow; r < rows && matchRow == -1; r++) {
879                 for (int c = lastCol; c < endCol; c++) {
880                     Object JavaDoc value = getValueAt(r, c);
881                     if ( (value != null) &&
882                         pattern.matcher(value.toString()).find()) {
883                         changeSelection(r, c, false, false);
884                         matchRow = r;
885                         lastCol = c;
886                         break; // No need to search other columns
887
}
888                 }
889                 if (matchRow == -1) {
890                     lastCol = 0;
891                 }
892             }
893         }
894
895         if (matchRow != -1) {
896             Object JavaDoc viewport = getParent();
897             if (viewport instanceof JViewport JavaDoc) {
898                 Rectangle JavaDoc rect = getCellRect(getSelectedRow(), 0, true);
899                 Point JavaDoc pt = ( (JViewport JavaDoc) viewport).getViewPosition();
900                 rect.setLocation(rect.x - pt.x, rect.y - pt.y);
901                 ( (JViewport JavaDoc) viewport).scrollRectToVisible(rect);
902             }
903         }
904         return matchRow;
905     }
906
907     public void setVisibleRowCount(int visibleRowCount) {
908         this.visibleRowCount = visibleRowCount;
909     }
910
911     public int getVisibleRowCount() {
912         return visibleRowCount;
913     }
914
915     public Dimension JavaDoc getPreferredScrollableViewportSize() {
916         Dimension JavaDoc prefSize = super.getPreferredScrollableViewportSize();
917
918         // JTable hardcodes this to 450 X 400, so we'll calculate it
919
// based on the preferred widths of the columns and the
920
// visibleRowCount property instead...
921

922         if (prefSize.getWidth() == 450 && prefSize.getHeight() == 400) {
923             TableColumnModel JavaDoc columnModel = getColumnModel();
924             int columnCount = columnModel.getColumnCount();
925
926             int w = 0;
927             for (int i = 0; i < columnCount; i++) {
928                 TableColumn JavaDoc column = columnModel.getColumn(i);
929                 initializeColumnPreferredWidth(column);
930                 w += column.getPreferredWidth();
931             }
932             prefSize.width = w;
933             JTableHeader JavaDoc header = getTableHeader();
934             //remind(aim): height is still off...???
935
int rowCount = getVisibleRowCount();
936             prefSize.height = rowCount * getRowHeight() +
937                 (header != null? header.getPreferredSize().height : 0);
938             setPreferredScrollableViewportSize(prefSize);
939         }
940         return prefSize;
941     }
942
943     /**
944      * Initialize the preferredWidth of the specified column based on the
945      * column's prototypeValue property. If the column is not an
946      * instance of <code>TableColumnExt</code> or prototypeValue is <code>null</code>
947      * then the preferredWidth is left unmodified.
948      * @see org.jdesktop.swing.table.TableColumnExt#setPrototypeValue
949      * @param column TableColumn object representing view column
950      */

951     protected void initializeColumnPreferredWidth(TableColumn JavaDoc column) {
952         if (column instanceof TableColumnExt) {
953             Dimension JavaDoc cellSpacing = getIntercellSpacing();
954             TableColumnExt columnx = (TableColumnExt) column;
955  // if (columnx.isVisible()) {
956
Object JavaDoc prototypeValue = columnx.getPrototypeValue();
957                 if (prototypeValue != null) {
958                     // calculate how much room the prototypeValue requires
959
TableCellRenderer JavaDoc renderer = getCellRenderer(0,
960                         convertColumnIndexToView(columnx.getModelIndex()));
961                     Component JavaDoc comp = renderer.getTableCellRendererComponent(this,
962                         prototypeValue, false, false, 0, 0);
963                     int prefWidth = comp.getPreferredSize().width + cellSpacing.width;
964
965                     // now calculate how much room the column header wants
966
renderer = columnx.getHeaderRenderer();
967                     if (renderer == null) {
968                         JTableHeader JavaDoc header = getTableHeader();
969                         if (header != null) {
970                             renderer = header.getDefaultRenderer();
971                         }
972                     }
973                     if (renderer != null) {
974                         comp = renderer.getTableCellRendererComponent(this,
975                                  columnx.getHeaderValue(), false, false, 0,
976                                  convertColumnIndexToView(columnx.getModelIndex()));
977
978                         prefWidth = Math.max(comp.getPreferredSize().width, prefWidth);
979                     }
980                     prefWidth += getColumnModel().getColumnMargin();
981                     columnx.setPreferredWidth(prefWidth);
982                 }
983 // } else {
984
// columnx.setPreferredWidth(0);
985
// }
986
}
987     }
988
989 // Printing Support extracted from 1.5
990

991     /**
992      *
993      * @return boolean indicating whether or not this <code>JTable</code>
994      * is currently being printed
995      */

996     public boolean isPrinting() {
997         return isPrinting;
998     }
999
1000    /**
1001     * A convenience method that displays a printing dialog, and then prints
1002     * this <code>JTable</code> in mode <code>PRINT_MODE_FIT_WIDTH</code>,
1003     * with no header or footer text.
1004     *
1005     * @return true, unless the user cancels the print dialog
1006     * @throws PrinterException if an error in the print system causes the job
1007     * to be aborted
1008     * @see #print(int, MessageFormat, MessageFormat, boolean, PrintRequestAttributeSet)
1009     * @see #getPrintable
1010     *
1011     * @since 1.5
1012     */

1013    public boolean print() throws PrinterException JavaDoc {
1014
1015        return print(PRINT_MODE_FIT_WIDTH);
1016    }
1017
1018    /**
1019     * A convenience method that displays a printing dialog, and then prints
1020     * this <code>JTable</code> in the given printing mode,
1021     * with no header or footer text.
1022     *
1023         * @param printMode the printing mode that the printable should use:
1024     * <code>PRINT_MODE_NORMAL</code> or
1025     * <code>PRINT_MODE_FIT_WIDTH</code>
1026     * @return true, unless the user cancels the print dialog
1027     * @throws PrinterException if an error in the print system causes the job
1028     * to be aborted
1029     * @throws IllegalArgumentException if passed an invalid print mode
1030     * @see #print(int, MessageFormat, MessageFormat, boolean, PrintRequestAttributeSet)
1031     * @see #getPrintable
1032     *
1033     * @since 1.5
1034     */

1035    public boolean print(int printMode) throws PrinterException JavaDoc {
1036
1037        return print(printMode, null, null);
1038    }
1039
1040    /**
1041     * A convenience method that displays a printing dialog, and then prints
1042     * this <code>JTable</code> in the given printing mode,
1043     * with the specified header and footer text.
1044     *
1045         * @param printMode the printing mode that the printable should use:
1046     * <code>PRINT_MODE_NORMAL</code> or
1047     * <code>PRINT_MODE_FIT_WIDTH</code>
1048         * @param headerFormat a <code>MessageFormat</code> specifying the text
1049     * to be used in printing a header,
1050     * or null for none
1051         * @param footerFormat a <code>MessageFormat</code> specifying the text
1052     * to be used in printing a footer,
1053     * or null for none
1054     * @return true, unless the user cancels the print dialog
1055     * @throws PrinterException if an error in the print system causes the job
1056     * to be aborted
1057     * @throws IllegalArgumentException if passed an invalid print mode
1058     * @see #print(int, MessageFormat, MessageFormat, boolean, PrintRequestAttributeSet)
1059     * @see #getPrintable
1060     *
1061     * @since 1.5
1062     */

1063    public boolean print(int printMode,
1064                         MessageFormat JavaDoc headerFormat,
1065                         MessageFormat JavaDoc footerFormat) throws PrinterException JavaDoc {
1066
1067        return print(printMode, headerFormat, footerFormat, true, null);
1068    }
1069
1070    /**
1071     * Print this <code>JTable</code>. Takes steps that the majority of
1072     * developers would take in order to print a <code>JTable</code>.
1073     * In short, it prepares the table, calls <code>getPrintable</code> to
1074     * fetch an appropriate <code>Printable</code>, and then sends it to the
1075     * printer.
1076     * <p>
1077     * A <code>boolean</code> parameter allows you to specify whether or not
1078     * a printing dialog is displayed to the user. When it is, the user may
1079     * use the dialog to change printing attributes or even cancel the print.
1080     * Another parameter allows for printing attributes to be specified
1081     * directly. This can be used either to provide the initial values for the
1082     * print dialog, or to supply any needed attributes when the dialog is not
1083     * shown.
1084     * <p>
1085     * Before fetching the printable, this method prepares the table in order
1086     * to get the most desirable printed result. If the table is currently
1087     * in an editing mode, it terminates the editing as gracefully as
1088     * possible. It also ensures that the the table's current selection and
1089     * focused cell are not indicated in the printed output. This is handled on
1090     * the view level, and only for the duration of the printing, thus no
1091     * notification needs to be sent to the selection models.
1092     * <p>
1093     * REMIND(aim): This method is temporarily hacked to execute the print
1094     * asynchronously to get around the problem of having the damage from
1095     * the print dialogs remain in the application window while the print
1096     * process ties up the event dispatch thread; ultimately we need to
1097     * find a way NOT to tie up the EDT during the print processing.
1098     *
1099     * See {@link #getPrintable} for further description on how the
1100     * table is printed.
1101     *
1102         * @param printMode the printing mode that the printable should use:
1103     * <code>PRINT_MODE_NORMAL</code> or
1104     * <code>PRINT_MODE_FIT_WIDTH</code>
1105         * @param headerFormat a <code>MessageFormat</code> specifying the text
1106     * to be used in printing a header,
1107     * or null for none
1108         * @param footerFormat a <code>MessageFormat</code> specifying the text
1109     * to be used in printing a footer,
1110     * or null for none
1111     * @param showPrintDialog whether or not to display a print dialog
1112     * @param attr a <code>PrintRequestAttributeSet</code>
1113     * specifying any printing attributes,
1114     * or null for none
1115     * @return true, unless the print dialog is shown and the user cancels it
1116     * @throws PrinterException if an error in the print system causes the job
1117     * to be aborted
1118     * @throws IllegalArgumentException if passed an invalid print mode
1119     * @see #getPrintable
1120     *
1121     * @since 1.5
1122     */

1123    public boolean print(int printMode,
1124                         MessageFormat JavaDoc headerFormat,
1125                         MessageFormat JavaDoc footerFormat,
1126                         boolean showPrintDialog,
1127                         PrintRequestAttributeSet JavaDoc attr) throws PrinterException JavaDoc {
1128
1129        if (isEditing()) {
1130            // try to stop cell editing, and failing that, cancel it
1131
if (!getCellEditor().stopCellEditing()) {
1132                getCellEditor().cancelCellEditing();
1133            }
1134        }
1135
1136        if (attr == null) {
1137            attr = new HashPrintRequestAttributeSet JavaDoc();
1138        }
1139        final PrintRequestAttributeSet JavaDoc attrset = attr;
1140
1141        final PrinterJob JavaDoc job = PrinterJob.getPrinterJob();
1142        job.setPrintable(getPrintable(printMode, headerFormat, footerFormat));
1143
1144        if (showPrintDialog && !job.printDialog(attrset)) {
1145            return false;
1146        }
1147
1148        //REMIND(aim): temporary bandaid...
1149
// Before we tie up the EDT with the printing process (which could
1150
// take awhile), we want to ensure any damaged areas which appear
1151
// when the print dialog(s) is dismissed are repainted, otherwise
1152
// garbage will appear in the window while the print request executes.
1153
// To ensure this, we must delay the print request momentarily while
1154
// we wait for the native window system to deliver the paint event
1155
// which results in the repaint of the areas damaged by the now
1156
// hidden print dialogs.
1157
//
1158
Timer JavaDoc timer = new Timer JavaDoc();
1159        TimerTask JavaDoc task = new TimerTask JavaDoc() {
1160            public void run() {
1161                EventQueue.invokeLater(new Runnable JavaDoc() {
1162                    public void run() {
1163                        // set a flag to disable indication of the selection and focused cell
1164
isPrinting = true;
1165                        try {
1166                            // do the printing
1167
job.print(attrset);
1168                        }
1169                        catch (PrinterException JavaDoc e) {
1170                            //REMIND(aim): how to handle throwing exception from asynchronous call?
1171
e.printStackTrace();
1172                        }
1173                        finally {
1174                            // restore the flag
1175
isPrinting = false;
1176                        }
1177                    }
1178                });
1179            }
1180        };
1181        timer.schedule(task, 100);
1182
1183        //REMIND(aim): return value is currently bogus with above hack
1184
return true;
1185    }
1186
1187    /**
1188     * Return a <code>Printable</code> for use in printing this JTable.
1189     * <p>
1190         * The <code>Printable</code> can be requested in one of two printing modes.
1191     * In both modes, it spreads table rows naturally in sequence across
1192     * multiple pages, fitting as many rows as possible per page.
1193     * <code>PRINT_MODE_NORMAL</code> specifies that the table be
1194     * printed at its current size. In this mode, there may be a need to spread
1195     * columns across pages in a similar manner to that of the rows. When the
1196     * need arises, columns are distributed in an order consistent with the
1197     * table's <code>ComponentOrientation</code>.
1198     * <code>PRINT_MODE_FIT_WIDTH</code> specifies that the output be
1199     * scaled smaller, if necessary, to fit the table's entire width
1200     * (and thereby all columns) on each page. Width and height are scaled
1201     * equally, maintaining the aspect ratio of the output.
1202     * <p>
1203     * The <code>Printable</code> heads the portion of table on each page
1204     * with the appropriate section from the table's <code>JTableHeader</code>,
1205     * if it has one.
1206     * <p>
1207     * Header and footer text can be added to the output by providing
1208     * <code>MessageFormat</code> arguments. The printing code requests
1209     * Strings from the formats, providing a single item which may be included
1210         * in the formatted string: an <code>Integer</code> representing the current
1211     * page number.
1212     * <p>
1213     * You are encouraged to read the documentation for
1214     * <code>MessageFormat</code> as some characters, such as single-quote,
1215     * are special and need to be escaped.
1216     * <p>
1217     * Here's an example of creating a <code>MessageFormat</code> that can be
1218     * used to print "Duke's Table: Page - " and the current page number:
1219     * <p>
1220     * <pre>
1221     * // notice the escaping of the single quote
1222     * // notice how the page number is included with "{0}"
1223         * MessageFormat format = new MessageFormat("Duke''s Table: Page - {0}");
1224     * </pre>
1225     * <p>
1226     * The <code>Printable</code> constrains what it draws to the printable
1227     * area of each page that it prints. Under certain circumstances, it may
1228     * find it impossible to fit all of a page's content into that area. In
1229     * these cases the output may be clipped, but the implementation
1230     * makes an effort to do something reasonable. Here are a few situations
1231     * where this is known to occur, and how they may be handled by this
1232     * particular implementation:
1233     * <ul>
1234     * <li>In any mode, when the header or footer text is too wide to fit
1235     * completely in the printable area -- print as much of the text as
1236     * possible starting from the beginning, as determined by the table's
1237     * <code>ComponentOrientation</code>.
1238     * <li>In any mode, when a row is too tall to fit in the
1239     * printable area -- print the upper-most portion of the row
1240     * and paint no lower border on the table.
1241     * <li>In <code>JTable.PRINT_MODE_NORMAL</code> when a column
1242     * is too wide to fit in the printable area -- print the center
1243     * portion of the column and leave the left and right borders
1244     * off the table.
1245     * </ul>
1246     * <p>
1247     * It is entirely valid for this <code>Printable</code> to be wrapped
1248     * inside another in order to create complex reports and documents. You may
1249     * even request that different pages be rendered into different sized
1250     * printable areas. The implementation must be prepared to handle this
1251     * (possibly by doing its layout calculations on the fly). However,
1252     * providing different heights to each page will likely not work well
1253     * with <code>PRINT_MODE_NORMAL</code> when it has to spread columns
1254     * across pages.
1255     * <p>
1256     * It is important to note that this <code>Printable</code> prints the
1257     * table at its current visual state, using the table's existing renderers.
1258     * <i>Before</i> calling this method, you may wish to <i>first</i> modify
1259     * the state of the table (such as to change the renderers, cancel editing,
1260     * or hide the selection).
1261     * <p>
1262     * Here's a simple example that calls this method to fetch a
1263     * <code>Printable</code>, shows a cross-platform print dialog, and then
1264     * prints the <code>Printable</code> unless the user cancels the dialog:
1265     * <p>
1266     * <pre>
1267         * // prepare the table for printing here first (for example, hide selection)
1268     *
1269         * // wrap in a try/finally so table can be restored even if something fails
1270     * try {
1271     * // fetch the printable
1272         * Printable printable = table.getPrintable(JTable.PRINT_MODE_FIT_WIDTH,
1273     * new MessageFormat("My Table"),
1274     * new MessageFormat("Page - {0}"));
1275     *
1276     * // fetch a PrinterJob
1277     * PrinterJob job = PrinterJob.getPrinterJob();
1278     *
1279     * // set the Printable on the PrinterJob
1280     * job.setPrintable(printable);
1281     *
1282         * // create an attribute set to store attributes from the print dialog
1283         * PrintRequestAttributeSet attr = new HashPrintRequestAttributeSet();
1284     *
1285     * // display a print dialog and record whether or not the user cancels it
1286     * boolean printAccepted = job.printDialog(attr);
1287     *
1288     * // if the user didn't cancel the dialog
1289     * if (printAccepted) {
1290     * // do the printing (may need to handle PrinterException)
1291     * job.print(attr);
1292     * }
1293     * } finally {
1294     * // restore the original table state here (for example, restore selection)
1295     * }
1296     * </pre>
1297     *
1298     * @param printMode the printing mode that the printable should use:
1299     * <code>PRINT_MODE_NORMAL</code> or
1300     * <code>PRINT_MODE_FIT_WIDTH</code>
1301         * @param headerFormat a <code>MessageFormat</code> specifying the text to
1302     * be used in printing a header, or null for none
1303         * @param footerFormat a <code>MessageFormat</code> specifying the text to
1304     * be used in printing a footer, or null for none
1305     * @return a <code>Printable</code> for printing this JTable
1306     * @throws IllegalArgumentException if passed an invalid print mode
1307     * @see #PRINT_MODE_NORMAL
1308     * @see #PRINT_MODE_FIT_WIDTH
1309     * @see Printable
1310     * @see PrinterJob
1311     *
1312     * @since 1.5
1313     */

1314    public Printable JavaDoc getPrintable(int printMode,
1315                                  MessageFormat JavaDoc headerFormat,
1316                                  MessageFormat JavaDoc footerFormat) {
1317
1318        return new TablePrintable(this, printMode, headerFormat, footerFormat);
1319    }
1320
1321    static class TableAdapter extends ComponentAdapter {
1322        private final JTable JavaDoc table;
1323
1324        /**
1325         * Constructs a <code>TableDataAdapter</code> for the specified
1326         * target component.
1327         *
1328         * @param component the target component
1329         */

1330        public TableAdapter(JTable JavaDoc component) {
1331            super(component);
1332            table = component;
1333        }
1334
1335        /**
1336         * Typesafe accessor for the target component.
1337         *
1338         * @return the target component as a {@link javax.swing.JTable}
1339         */

1340        public JTable JavaDoc getTable() {
1341            return table;
1342        }
1343
1344        /**
1345         * {@inheritDoc}
1346         */

1347        public boolean hasFocus() {
1348            //REMIND(aim): think through printing implications on decorators
1349
if (table instanceof JXTable && ( (JXTable) table).isPrinting()) {
1350                return false;
1351            }
1352            boolean rowIsLead = (table.getSelectionModel().
1353                                   getLeadSelectionIndex() == row);
1354            boolean colIsLead =
1355                (table.getColumnModel().getSelectionModel().
1356                 getLeadSelectionIndex() ==
1357                 column);
1358            return table.isFocusOwner() && (rowIsLead && colIsLead);
1359
1360        }
1361
1362        public String JavaDoc getColumnName(int columnIndex) {
1363            TableColumnModel JavaDoc columnModel = table.getColumnModel();
1364            if (columnModel == null){
1365                return "Column " + columnIndex;
1366            }
1367            TableColumn JavaDoc column = columnModel.getColumn(columnIndex);
1368
1369            return column == null ? "" : column.getHeaderValue().toString();
1370        }
1371
1372        public int getColumnCount() {
1373            return table.getModel().getColumnCount();
1374        }
1375
1376        public int getRowCount() {
1377            return table.getModel().getRowCount();
1378        }
1379
1380        /**
1381         * {@inheritDoc}
1382         */

1383        public Object JavaDoc getValueAt(int row, int column) {
1384            return table.getModel().getValueAt(row, viewToModel(column));
1385        }
1386
1387        public Object JavaDoc getFilteredValueAt(int row, int column) {
1388            return table.getValueAt(row, column); // in view coordinates
1389
}
1390
1391        public void setValueAt(Object JavaDoc aValue, int row, int column) {
1392            table.getModel().setValueAt(aValue, row, viewToModel(column));
1393        }
1394
1395        public boolean isCellEditable(int row, int column) {
1396            return table.getModel().isCellEditable(row, viewToModel(column));
1397        }
1398
1399        /**
1400         * {@inheritDoc}
1401         */

1402        public boolean isSelected() {
1403            //REMIND(aim): think through printing implications on decorators
1404
if (table instanceof JXTable && ( (JXTable) table).isPrinting()) {
1405                return false;
1406            }
1407            return table.isCellSelected(row, column);
1408        }
1409
1410        /**
1411         * {@inheritDoc}
1412         */

1413        public int modelToView(int columnIndex) {
1414            return table.convertColumnIndexToView(columnIndex);
1415        }
1416
1417        /**
1418         * {@inheritDoc}
1419         */

1420        public int viewToModel(int columnIndex) {
1421            return table.convertColumnIndexToModel(columnIndex);
1422        }
1423
1424    }
1425
1426    private static class Selection {
1427        protected final int[] selected; // used ONLY within save/restoreSelection();
1428
protected int lead = -1;
1429        protected Selection(JXTable table) {
1430            selected = table.getSelectedRows(); // in view coordinates
1431
for (int i = 0; i < selected.length; i++) {
1432                selected[i] = table.convertRowIndexToModel(selected[i]); // model coordinates
1433
}
1434
1435            if (selected.length > 0) {
1436                // convert lead selection index to model coordinates
1437
lead = table.convertRowIndexToModel(
1438                    table.getSelectionModel().getLeadSelectionIndex());
1439            }
1440        }
1441    }
1442
1443    /*
1444     * Default Type-based Renderers:
1445     * JTable's default table cell renderer classes are private and
1446     * JTable:getDefaultRenderer() returns a *shared* cell renderer instance,
1447     * thus there is no way for us to instantiate a new instance of one of its
1448     * default renderers. So, we must replicate the default renderer classes
1449     * here so that we can instantiate them when we need to create renderers
1450     * to be set on specific columns.
1451     */

1452    public static class NumberRenderer extends DefaultTableCellRenderer JavaDoc {
1453        public NumberRenderer() {
1454            super();
1455            setHorizontalAlignment(JLabel.RIGHT);
1456        }
1457    }
1458
1459    public static class DoubleRenderer extends NumberRenderer {
1460        NumberFormat JavaDoc formatter;
1461        public DoubleRenderer() {
1462            super();
1463        }
1464
1465        public void setValue(Object JavaDoc value) {
1466            if (formatter == null) {
1467                formatter = NumberFormat.getInstance();
1468            }
1469            setText( (value == null) ? "" : formatter.format(value));
1470        }
1471    }
1472
1473    public static class DateRenderer extends DefaultTableCellRenderer JavaDoc {
1474        DateFormat JavaDoc formatter;
1475        public DateRenderer() {
1476            super();
1477        }
1478
1479        public void setValue(Object JavaDoc value) {
1480            if (formatter == null) {
1481                formatter = DateFormat.getDateInstance();
1482            }
1483            setText( (value == null) ? "" : formatter.format(value));
1484        }
1485    }
1486
1487    public static class IconRenderer extends DefaultTableCellRenderer JavaDoc {
1488        public IconRenderer() {
1489            super();
1490            setHorizontalAlignment(JLabel.CENTER);
1491        }
1492
1493        public void setValue(Object JavaDoc value) {
1494            setIcon( (value instanceof Icon JavaDoc) ? (Icon JavaDoc) value : null);
1495        }
1496    }
1497
1498    public static class BooleanRenderer extends JCheckBox JavaDoc
1499        implements TableCellRenderer JavaDoc {
1500        public BooleanRenderer() {
1501            super();
1502            setHorizontalAlignment(JLabel.CENTER);
1503        }
1504
1505        public Component JavaDoc getTableCellRendererComponent(JTable JavaDoc table,
1506            Object JavaDoc value,
1507            boolean isSelected, boolean hasFocus, int row, int column) {
1508            if (isSelected) {
1509                setForeground(table.getSelectionForeground());
1510                super.setBackground(table.getSelectionBackground());
1511            }
1512            else {
1513                setForeground(table.getForeground());
1514                setBackground(table.getBackground());
1515            }
1516            setSelected( (value != null && ( (Boolean JavaDoc) value).booleanValue()));
1517            return this;
1518        }
1519    }
1520
1521    /**
1522     * Renders a Link type the link in the table column
1523     */

1524    public static class LinkRenderer extends DefaultTableCellRenderer JavaDoc {
1525
1526        // Should have a way of setting these statically
1527
private static Color JavaDoc colorLive = new Color JavaDoc(0, 0, 238);
1528        private static Color JavaDoc colorVisited = new Color JavaDoc(82, 24, 139);
1529
1530        public void setValue(Object JavaDoc value) {
1531            if (value != null && value instanceof Link) {
1532                Link link = (Link) value;
1533
1534                setText(link.getText());
1535                setToolTipText(link.getURL().toString());
1536
1537                if (link.getVisited()) {
1538                    setForeground(colorVisited);
1539                }
1540                else {
1541                    setForeground(colorLive);
1542                }
1543            }
1544            else {
1545                super.setValue(value != null ? value.toString() : "");
1546            }
1547        }
1548
1549        public void paintComponent(Graphics JavaDoc g) {
1550            super.paintComponent(g);
1551            if (!getText().equals("")) {
1552                // Render an underline. A really smart person
1553
// would actually render an underline font but
1554
// that's too much for my little brain.
1555
Rectangle JavaDoc rect = PaintUtils.getTextBounds(g, this);
1556
1557                FontMetrics JavaDoc fm = g.getFontMetrics();
1558                int descent = fm.getDescent();
1559
1560                //REMIND(aim): should we be basing the underline on
1561
//the font's baseline instead of the text bounds?
1562

1563                g.drawLine(rect.x, (rect.y + rect.height) - descent + 1,
1564                           rect.x + rect.width,
1565                           (rect.y + rect.height) - descent + 1);
1566            }
1567        }
1568    }
1569}
1570
Popular Tags