View Javadoc

1   /*
2   This file is part of the xframe software package
3   hosted at http://xframe.sourceforge.net
4   
5   Copyright (c) 2003 Kurt Riede.
6   
7   This library is free software; you can redistribute it and/or
8   modify it under the terms of the GNU Lesser General Public
9   License as published by the Free Software Foundation; either
10  version 2.1 of the License, or (at your option) any later version.
11  
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License for more details.
16  
17  You should have received a copy of the GNU Lesser General Public
18  License along with this library; if not, write to the Free Software
19  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21  package net.sf.xframe.swing;
22  
23  import java.awt.AWTEventMulticaster;
24  import java.awt.BorderLayout;
25  import java.awt.Color;
26  import java.awt.Component;
27  import java.awt.Dimension;
28  import java.awt.Font;
29  import java.awt.Graphics;
30  import java.awt.Point;
31  import java.awt.Rectangle;
32  import java.awt.event.ActionEvent;
33  import java.awt.event.MouseEvent;
34  import java.awt.event.MouseListener;
35  import java.awt.event.MouseMotionListener;
36  import java.awt.event.MouseWheelEvent;
37  import java.awt.event.MouseWheelListener;
38  import java.awt.geom.Rectangle2D;
39  import java.beans.PropertyChangeEvent;
40  import java.beans.PropertyChangeListener;
41  import java.util.EventListener;
42  import java.util.EventObject;
43  
44  import javax.accessibility.Accessible;
45  import javax.swing.AbstractAction;
46  import javax.swing.Action;
47  import javax.swing.DefaultListSelectionModel;
48  import javax.swing.JComponent;
49  import javax.swing.JScrollBar;
50  import javax.swing.JScrollPane;
51  import javax.swing.JTable;
52  import javax.swing.JViewport;
53  import javax.swing.ListSelectionModel;
54  import javax.swing.ScrollPaneConstants;
55  import javax.swing.Scrollable;
56  import javax.swing.TransferHandler;
57  import javax.swing.border.Border;
58  import javax.swing.event.CellEditorListener;
59  import javax.swing.event.ChangeEvent;
60  import javax.swing.event.ListSelectionEvent;
61  import javax.swing.event.ListSelectionListener;
62  import javax.swing.event.TableColumnModelEvent;
63  import javax.swing.event.TableColumnModelListener;
64  import javax.swing.event.TableModelEvent;
65  import javax.swing.event.TableModelListener;
66  import javax.swing.plaf.ScrollPaneUI;
67  import javax.swing.plaf.TableUI;
68  import javax.swing.table.DefaultTableColumnModel;
69  import javax.swing.table.DefaultTableModel;
70  import javax.swing.table.JTableHeader;
71  import javax.swing.table.TableCellEditor;
72  import javax.swing.table.TableCellRenderer;
73  import javax.swing.table.TableColumn;
74  import javax.swing.table.TableColumnModel;
75  import javax.swing.table.TableModel;
76  
77  import net.sf.xframe.swing.scroll.JScrollPaneAdjuster;
78  import net.sf.xframe.swing.table.ColumnGroupHeader;
79  import net.sf.xframe.swing.table.JXTableColumnModel;
80  import net.sf.xframe.swing.table.JXTableHeader;
81  import net.sf.xframe.swing.table.KTable;
82  
83  
84  /***
85   * A table with freezable columns.
86   *
87   * <p>The main feature of this table is two allow locking columns while still
88   * having a neat user interface. For most of the original methods of class
89   * <code>JTable</code> there are delegate methods.</p>
90   *
91   * @author <a href=mailto:kriede@users.sourceforge.net>Kurt Riede</a>
92   */
93  public class JXTable extends JComponent implements TableModelListener, Scrollable,
94                                                     TableColumnModelListener, ListSelectionListener,
95                                                     CellEditorListener, Accessible {
96  
97      /***
98       * Used to set the horizontal scroll bar policy so that
99       * horizontal scrollbars are displayed only when needed.
100      */
101     public static final int HORIZONTAL_SCROLLBAR_AS_NEEDED = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED;
102 
103     /***
104      * Used to set the horizontal scroll bar policy so that
105      * horizontal scrollbars are never displayed.
106      */
107     public static final int HORIZONTAL_SCROLLBAR_NEVER = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER;
108 
109     /***
110      * Used to set the horizontal scroll bar policy so that
111      * horizontal scrollbars are always displayed.
112      */
113     public static final int HORIZONTAL_SCROLLBAR_ALWAYS = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS;
114 
115     /*** Do not adjust column widths automatically; use a scrollbar. */
116     public static final int AUTO_RESIZE_OFF = 0;
117 
118     /*** When a column is adjusted in the UI, adjust the next column the opposite way. */
119     public static final int AUTO_RESIZE_NEXT_COLUMN = 1;
120 
121     /*** During UI adjustment, change subsequent columns to preserve the total width;
122       * this is the default behavior. */
123     public static final int AUTO_RESIZE_SUBSEQUENT_COLUMNS = 2;
124 
125     /*** During all resize operations, apply adjustments to the last column only. */
126     public static final int AUTO_RESIZE_LAST_COLUMN = 3;
127 
128     /*** During all resize operations, proportionately resize all columns. */
129     public static final int AUTO_RESIZE_ALL_COLUMNS = 4;
130 
131     /*** The ScrollPane around the table. */
132     private final JScrollPane scrollPane;
133 
134     /*** the table with the locked columns. */
135     private final KTable lockedTable;
136 
137     /*** The table with the scrollable columns. */
138     private final KTable scrollTable;
139 
140     /*** Scroll pane adjuster. */
141     private final JScrollPaneAdjuster adjuster;
142 
143     /*** Header of the locked table. */
144     private JTableHeader lockedHeader;
145 
146     /*** Header of the scrollable table. */
147     private JTableHeader scrollHeader;
148 
149     /*** The table column model. */
150     private TableColumnModel columnModel;
151 
152     /*** the header. */
153     private JXTableHeader collTableHeader;
154 
155     /*** Number of frozen columns. */
156     private int frozenColumns = 1;
157 
158     /*** Reference to the mouse listener. */
159     private transient MouseListener mouseListener;
160 
161     /*** Reference to the mouse motion listener. */
162     private transient MouseMotionListener mouseMotionListener;
163 
164     /*** Reference to the mouse wheel listener. */
165     private transient MouseWheelListener mouseWheelListener;
166 
167     /***
168      * Constructs a default <code>JTable</code> that is initialized with a default
169      * data model, a default column model, and a default selection
170      * model.
171      */
172     public JXTable() {
173         this(null, null, null, 0);
174     }
175 
176     /***
177      * Constructs a <code>JTable</code> that is initialized with
178      * <code>dm</code> as the data model, a default column model,
179      * and a default selection model.
180      *
181      * @param dm        the data model for the table
182      */
183     public JXTable(final TableModel dm) {
184         this(dm, null, null, 0);
185     }
186 
187     /***
188      * Constructs a <code>JTable</code> that is initialized with
189      * <code>dm</code> as the data model, a default column model,
190      * and a default selection model.
191      *
192      * @param dm the data model for the table
193      * @param numFrozenColumns the initial number of frozen columns
194      */
195     public JXTable(final TableModel dm, final int numFrozenColumns) {
196         this(dm, null, null, numFrozenColumns);
197     }
198 
199     /***
200      * Constructs a <code>JTable</code> that is initialized with
201      * <code>dm</code> as the data model, <code>cm</code>
202      * as the column model, and a default selection model.
203      *
204      * @param dm the data model for the table
205      * @param cm the column model for the table
206      */
207     public JXTable(final TableModel dm, final TableColumnModel cm) {
208         this(dm, cm, null, 0);
209     }
210 
211     /***
212      * Constructs a <code>JTable</code> that is initialized with
213      * <code>dm</code> as the data model, <code>cm</code>
214      * as the column model, and a default selection model.
215      *
216      * @param dm the data model for the table
217      * @param cm the column model for the table
218      * @param numFrozenColumns the initial number of frozen columns
219      */
220     public JXTable(final TableModel dm, final TableColumnModel cm, final int numFrozenColumns) {
221         this(dm, cm, null, numFrozenColumns);
222     }
223 
224     /***
225      * Constructs a <code>JTable</code> that is initialized with
226      * <code>dm</code> as the data model, <code>cm</code> as the
227      * column model, and <code>sm</code> as the selection model.
228      * If any of the parameters are <code>null</code> this method
229      * will initialize the table with the corresponding default model.
230      * The <code>autoCreateColumnsFromModel</code> flag is set to false
231      * if <code>cm</code> is non-null, otherwise it is set to true
232      * and the column model is populated with suitable
233      * <code>TableColumns</code> for the columns in <code>dm</code>.
234      *
235      * @param dm the data model for the table
236      * @param cm the column model for the table
237      * @param sm the row selection model for the table
238      */
239     public JXTable(final TableModel dm, final TableColumnModel cm, final ListSelectionModel sm) {
240         this(dm, cm, sm, 0);
241     }
242 
243     /***
244      * Constructs a <code>JTable</code> that is initialized with
245      * <code>dm</code> as the data model, <code>cm</code> as the
246      * column model, and <code>sm</code> as the selection model.
247      * If any of the parameters are <code>null</code> this method
248      * will initialize the table with the corresponding default model.
249      * The <code>autoCreateColumnsFromModel</code> flag is set to false
250      * if <code>cm</code> is non-null, otherwise it is set to true
251      * and the column model is populated with suitable
252      * <code>TableColumns</code> for the columns in <code>dm</code>.
253      *
254      * @param dm the data model for the table
255      * @param cm the column model for the table
256      * @param sm the row selection model for the table
257      * @param numFrozenColumns the initial number of frozen columns
258      */
259     public JXTable(final TableModel dm, final TableColumnModel cm, final ListSelectionModel sm, final int numFrozenColumns) {
260         super();
261         scrollPane = new JScrollPane();
262         adjuster = new JScrollPaneAdjuster(scrollPane);
263         final TableModel model;
264         if (dm == null) {
265             model = createDefaultDataModel();
266         } else {
267             model = dm;
268         }
269         frozenColumns = numFrozenColumns;
270         if (frozenColumns > model.getColumnCount()) {
271             frozenColumns = model.getColumnCount();
272         }
273         final TableColumnModel cm1;
274         final TableColumnModel cm2;
275         if (cm == null) {
276             cm1 = null;
277             cm2 = null;
278         } else {
279             cm1 = this.createDefaultColumnModel();
280             cm2 = this.createDefaultColumnModel();
281             for (int i = 0; i < cm.getColumnCount(); i++) {
282                 cm1.addColumn(cm.getColumn(i));
283                 cm2.addColumn(cm.getColumn(i));
284             }
285         }
286         // create the two tables
287         lockedTable = new KTable(model, cm1, sm, KTable.TYPE_LOCKED);
288         scrollTable = new KTable(model, cm2, sm, KTable.TYPE_SCROLL);
289         lockedHeader = new ColumnGroupHeader(lockedTable.getColumnModel());
290         scrollHeader = new ColumnGroupHeader(scrollTable.getColumnModel());
291         lockedTable.setTableHeader(lockedHeader);
292         scrollTable.setTableHeader(scrollHeader);
293         scrollPane.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER, lockedHeader);
294         scrollPane.setViewportView(scrollTable);
295         scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
296         setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
297         setResizingAllowed(true);
298 
299         // Put the locked-column table in the row header
300         final JViewport viewport = new JViewport();
301         viewport.setView(lockedTable);
302         scrollPane.setRowHeader(viewport);
303 
304         // share a single-selection selection model between both tables
305         if (sm == null) {
306             scrollTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
307             lockedTable.setSelectionModel(scrollTable.getSelectionModel());
308         }
309         // The following is a JDK 1.4 feature, but runnable with JDK 1.3
310         lockedTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
311         scrollTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
312 
313         // Remove the locked columns from the scrollable table
314         final TableColumnModel scrollColumnModel = scrollTable.getColumnModel();
315         for (int i = 0; i < frozenColumns; i++) {
316             scrollColumnModel.removeColumn(scrollColumnModel.getColumn(0));
317         }
318 
319         // Remove the scrollable columns from the locked table
320         final TableColumnModel lockedColumnModel = lockedTable.getColumnModel();
321         while (lockedTable.getColumnCount() > frozenColumns) {
322             lockedColumnModel.removeColumn(lockedColumnModel.getColumn(frozenColumns));
323         }
324 
325         lockedTable.setAutoCreateColumnsFromModel(false);
326         scrollTable.setAutoCreateColumnsFromModel(false);
327 
328         // add size listeners to all locked columns
329         for (int i = 0; i < lockedColumnModel.getColumnCount(); i++) {
330             lockedColumnModel.getColumn(i).addPropertyChangeListener(new ColumnWidthChangeListener());
331         }
332 
333         // create the combined column model
334         columnModel = new JXTableColumnModel(this, lockedColumnModel, scrollColumnModel);
335 
336         // Add the fixed table to the scroll pane
337         lockedTable.setPreferredScrollableViewportSize(lockedTable.getPreferredSize());
338 
339         adjustActionMaps();
340 
341         final JTableHeader theLockedHeader = lockedTable.getTableHeader();
342         final JTableHeader theScrollHeader = scrollTable.getTableHeader();
343         collTableHeader = new JXTableHeader(columnModel, theLockedHeader, theScrollHeader);
344         collTableHeader.setTable(this);
345 
346         // add scrollpane to container
347         setLayout(new BorderLayout());
348         add(scrollPane, "Center");
349     }
350 
351     /***
352      * Returns the underlying scroll pane.
353      *
354      * @return the underlying scroll pane
355      */
356     public JScrollPane getScrollPane() {
357         return scrollPane;
358     }
359 
360     /***
361      * Adjusts the action maps of the two tables to allow proper navigation
362      * between the two tables.
363      */
364     private void adjustActionMaps() {
365         // collect original left/right/first/last actions
366         final Action scrollNextAction = scrollTable.getActionMap().get("selectNextColumnCell");
367         final Action lockedNextAction = lockedTable.getActionMap().get("selectNextColumnCell");
368         final Action scrollPreviousAction = scrollTable.getActionMap().get("selectPreviousColumnCell");
369         final Action lockedPreviousAction = lockedTable.getActionMap().get("selectPreviousColumnCell");
370         final Action scrollFirstAction = scrollTable.getActionMap().get("selectFirstColumn");
371         final Action lockedLastAction = lockedTable.getActionMap().get("selectLastColumn");
372         // set left and right actions
373         lockedTable.getActionMap().put("selectNextColumn", new LockedNextAction(lockedNextAction));
374         lockedTable.getActionMap().put("selectPreviousColumn", new LockedPreviousAction(lockedPreviousAction));
375         scrollTable.getActionMap().put("selectNextColumn", new ScrollNextAction(scrollNextAction));
376         scrollTable.getActionMap().put("selectPreviousColumn", new ScrollPreviousAction(scrollPreviousAction));
377         // set tab and shift-tab actions
378         lockedTable.getActionMap().put("selectNextColumnCell", new LockedNextAction(lockedNextAction));
379         lockedTable.getActionMap().put("selectPreviousColumnCell", new LockedPreviousAction(lockedPreviousAction));
380         scrollTable.getActionMap().put("selectNextColumnCell", new ScrollNextAction(scrollNextAction));
381         scrollTable.getActionMap().put("selectPreviousColumnCell", new ScrollPreviousAction(scrollPreviousAction));
382         // set top and end actions
383         scrollTable.getActionMap().put("selectFirstColumn", new ScrollFirstAction(scrollFirstAction));
384         lockedTable.getActionMap().put("selectLastColumn", new LockedLastAction(lockedLastAction));
385     }
386 
387     /***
388      * Returns the default table model object, which is
389      * a <code>DefaultTableModel</code>.  A subclass can override this
390      * method to return a different table model object.
391      *
392      * @return the default table model object
393      * @see javax.swing.table.DefaultTableModel
394      */
395     protected TableModel createDefaultDataModel() {
396         return new DefaultTableModel();
397     }
398 
399     /***
400      * Returns the default column model object, which is
401      * a <code>DefaultTableColumnModel</code>.  A subclass can override this
402      * method to return a different column model object.
403      *
404      * @return the default column model object
405      * @see javax.swing.table.DefaultTableColumnModel
406      */
407     protected TableColumnModel createDefaultColumnModel() {
408         return new DefaultTableColumnModel();
409     }
410 
411     /***
412      * Returns the default selection model object, which is
413      * a <code>DefaultListSelectionModel</code>.  A subclass can override this
414      * method to return a different selection model object.
415      *
416      * @return the default selection model object
417      * @see javax.swing.DefaultListSelectionModel
418      */
419     protected ListSelectionModel createDefaultSelectionModel() {
420         return new DefaultListSelectionModel();
421     }
422 
423     /***
424      * Returns the default table header object for the scrollable table,
425      * which is a <code>JTableHeader</code>.  A subclass can override this
426      * method to return a different table header object.
427      *
428      * @return the default table header object
429      * @see javax.swing.table.JTableHeader
430      * @see net.sf.xframe.swing.table.JXTableHeader
431      */
432     public JTableHeader createDefaultScrollTableHeader() {
433         return new ColumnGroupHeader(scrollTable.getColumnModel());
434     }
435 
436 
437     /***
438     /**
439      * Returns the number of the next row in the table relative to the
440      * current selection. If the selection is in the last row, the next row
441      * is the first row.
442      *
443      * @param table a JTable for row calculation
444      * @return row number of next row
445      */
446     protected int nextRow(final JTable table) {
447         int row = table.getSelectedRow() + 1;
448         if (row == table.getRowCount()) {
449             row = 0;
450         }
451         return row;
452     }
453 
454     /***
455      * Returns the number of the previous row in the table relative to the
456      * current selection. If the selection is in the first row, the next row
457      * is the first row.
458      *
459      * @param table a JTable for row calculation
460      * @return row number of previous row
461      */
462     private int previousRow(final JTable table) {
463         int row = table.getSelectedRow() - 1;
464         if (row == -1) {
465             row = table.getRowCount() - 1;
466         }
467         return row;
468     }
469 
470     /***
471      * Returns the internal table for the locked columns.
472      *
473      * @return the locked table
474      */
475     public final JTable getLockedTable() {
476         return lockedTable;
477     }
478 
479     /***
480      * Returns the internal table for the scrollable columns.
481      *
482      * @return the scrollable table
483      */
484     public final JTable getScrollTable() {
485         return scrollTable;
486     }
487 
488     /***
489      * Returns the number of frozen columns.
490      *
491      * @return number of frozen columns
492      */
493     public final int getFrozenColumns() {
494         return frozenColumns;
495     }
496 
497     /***
498      * Setter method for frozen columns attribute.
499      *
500      * <p>Changing the number frozen column results in rearranging the columns
501      * within the fixed-column and the scrollable table.</p>
502      *
503      * @param numFrozenColumns of frozen columns
504      */
505     public final void setFrozenColumns(final int numFrozenColumns) {
506         if (numFrozenColumns > lockedTable.getModel().getColumnCount()) {
507             return;
508         }
509         final TableColumnModel scrollColumnModel = scrollTable.getColumnModel();
510         final TableColumnModel lockedColumnModel = lockedTable.getColumnModel();
511         if (frozenColumns < numFrozenColumns) {
512             // move columns from scrollable to locked table
513             for (int i = frozenColumns; i < numFrozenColumns; i++) {
514                 final TableColumn column = scrollColumnModel.getColumn(0);
515                 lockedColumnModel.addColumn(column);
516                 scrollColumnModel.removeColumn(column);
517                 column.addPropertyChangeListener(new ColumnWidthChangeListener());
518             }
519             lockedTable.setPreferredScrollableViewportSize(lockedTable.getPreferredSize());
520         } else if (frozenColumns > numFrozenColumns) {
521             // move columns from locked to scrollable table
522             for (int i = numFrozenColumns; i < frozenColumns; i++) {
523                 final TableColumn column = lockedColumnModel.getColumn(lockedColumnModel.getColumnCount() - 1);
524                 scrollColumnModel.addColumn(column);
525                 scrollColumnModel.moveColumn(scrollColumnModel.getColumnCount() - 1, 0);
526                 lockedColumnModel.removeColumn(column);
527             }
528             lockedTable.setPreferredScrollableViewportSize(lockedTable.getPreferredSize());
529         }
530         frozenColumns = numFrozenColumns;
531     }
532 
533     /***
534      * Sets the background color of this component.
535      *
536      * @param bg the desired background <code>Color</code>
537      */
538     public void setHeaderBackground(final Color bg) {
539         lockedHeader.setBackground(bg);
540         scrollHeader.setBackground(bg);
541     }
542 
543     /***
544      * Sets the border of the table header.
545      *
546      * @param border the border to be rendered for this component
547      */
548     public void setHeaderBorder(final Border border) {
549         lockedHeader.setBorder(border);
550         scrollHeader.setBorder(border);
551     }
552 
553     /***
554      * Sets the font for the table header.
555      *
556      * @param font the desired <code>Font</code> for this component
557      */
558     public void setHeaderFont(final Font font) {
559         lockedHeader.setFont(font);
560         scrollHeader.setFont(font);
561     }
562 
563     /***
564      *  Sets whether the user can drag column headers to reorder columns.
565      *
566      * @param reorderingAllowed true if the table view should allow
567      *        reordering; otherwise false
568      */
569     public void setReorderingAllowed(final boolean reorderingAllowed) {
570         lockedHeader.setReorderingAllowed(reorderingAllowed);
571         scrollHeader.setReorderingAllowed(reorderingAllowed);
572     }
573 
574     /***
575      * Sets whether the user can resize columns by dragging between headers.
576      *
577      * @param resizingAllowed true if table view should allow resizing
578      */
579     public void setResizingAllowed(final boolean resizingAllowed) {
580         lockedHeader.setResizingAllowed(resizingAllowed);
581         scrollHeader.setResizingAllowed(resizingAllowed);
582     }
583 
584     /***
585      * Sets the font for this component.
586      *
587      * @param font the desired <code>Font</code> for this component
588      */
589     public void setTableFont(final Font font) {
590         lockedTable.setFont(font);
591         scrollTable.setFont(font);
592     }
593 
594     /***
595      * Sets the foreground color of this component.
596      *
597      * @param fg  the desired foreground <code>Color</code>
598      */
599     public void setForeground(final Color fg) {
600         if (lockedTable != null) {
601             lockedTable.setForeground(fg);
602         }
603         if (scrollTable != null) {
604             scrollTable.setForeground(fg);
605         }
606     }
607 
608     /***
609      * Sets the color used to draw grid lines to <code>gridColor</code> and redisplays.
610      * The default color is look and feel dependent.
611      *
612      * @param gridColor the new color of the grid lines
613      */
614     public void setGridColor(final Color gridColor) {
615         lockedTable.setGridColor(gridColor);
616         scrollTable.setGridColor(gridColor);
617     }
618 
619     /***
620      * Sets the background color for selected cells.  Cell renderers
621      * can use this color to the fill selected cells.
622      * <p>
623      * The default value of this property is defined by the look
624      * and feel implementation.
625      * <p>
626      *
627      * @param selectionBackground the <code>Color</code> to use for the background
628      *                            of selected cells
629      */
630     public void setSelectionBackground(final Color selectionBackground) {
631         lockedTable.setSelectionBackground(selectionBackground);
632         scrollTable.setSelectionBackground(selectionBackground);
633     }
634 
635     /***
636      * Sets the foreground color for selected cells.  Cell renderers
637      * can use this color to render text and graphics for selected
638      * cells.
639      * <p>
640      * The default value of this property is defined by the look
641      * and feel implementation.
642      * <p>
643      *
644      * @param selectionForeground the <code>Color</code> to use in the foreground
645      */
646     public void setSelectionForeground(final Color selectionForeground) {
647         lockedTable.setSelectionForeground(selectionForeground);
648         scrollTable.setSelectionForeground(selectionForeground);
649     }
650 
651     /***
652      *  Sets whether the table draws grid lines around cells.
653      *  If <code>showGrid</code> is true it does; if it is false it doesn't.
654      *  There is no <code>getShowGrid</code> method as this state is held
655      *  in two variables -- <code>showHorizontalLines</code> and <code>showVerticalLines</code> --
656      *  each of which can be queried independently.
657      *
658      * @param showGrid true if table view should draw grid lines
659      */
660     public void setShowGrid(final boolean showGrid) {
661         lockedTable.setShowGrid(showGrid);
662         scrollTable.setShowGrid(showGrid);
663     }
664 
665     /***
666      *  Sets whether the table draws horizontal lines between cells.
667      *  If <code>showHorizontalLines</code> is true it does; if it is false it doesn't.
668      *
669      * @param showHorizontalLines true if table view should draw horizontal lines
670      */
671     public void setShowHorizontalLines(final boolean showHorizontalLines) {
672         lockedTable.setShowHorizontalLines(showHorizontalLines);
673         scrollTable.setShowHorizontalLines(showHorizontalLines);
674     }
675 
676     /***
677      *  Sets whether the table draws vertical lines between cells.
678      *  If <code>showVerticalLines</code> is true it does; if it is false it doesn't.
679      *
680      * @param showVerticalLines true if table view should draw vertical lines
681      */
682     public void setShowVerticalLines(final boolean showVerticalLines) {
683         lockedTable.setShowVerticalLines(showVerticalLines);
684         scrollTable.setShowVerticalLines(showVerticalLines);
685     }
686 
687     /***
688      * Updates the selection models of the table, depending on the state of the
689      * two flags: <code>toggle</code> and <code>extend</code>. All changes
690      * to the selection that are the result of keyboard or mouse events received
691      * by the UI are channeled through this method so that the behavior may be
692      * overridden by a subclass.
693      * <p>
694      * This implementation uses the following conventions:
695      * <ul>
696      * <li> <code>toggle</code>: <em>false</em>, <code>extend</code>: <em>false</em>.
697      *      Clear the previous selection and ensure the new cell is selected.
698      * <li> <code>toggle</code>: <em>false</em>, <code>extend</code>: <em>true</em>.
699      *      Extend the previous selection to include the specified cell.
700      * <li> <code>toggle</code>: <em>true</em>, <code>extend</code>: <em>false</em>.
701      *      If the specified cell is selected, deselect it. If it is not selected, select it.
702      * <li> <code>toggle</code>: <em>true</em>, <code>extend</code>: <em>true</em>.
703      *      Leave the selection state as it is, but move the anchor index to the specified location.
704      * </ul>
705      * @param row affects the selection at <code>row</code>
706      * @param column affects the selection at <code>column</code>
707      * @param toggle see description above
708      * @param extend if true, extend the current selection
709      *
710      */
711     public void changeSelection(final int row, final int column, final boolean toggle, final boolean extend) {
712         if (column < frozenColumns) {
713             lockedTable.changeSelection(row, column, toggle, extend);
714         } else {
715             scrollTable.changeSelection(row, column - frozenColumns, toggle, extend);
716         }
717     }
718 
719     /***
720      * Programmatically starts editing the cell at <code>row</code> and
721      * <code>column</code>, if the cell is editable.  Note that this is
722      * a convenience method for <code>editCellAt(int, int, null)</code>.
723      *
724      * @param row the row to be edited
725      * @param column the column to be edited
726      * @return false if for any reason the cell cannot be edited
727      */
728     public boolean editCellAt(final int row, final int column) {
729         if (column < frozenColumns) {
730             return lockedTable.editCellAt(row, column);
731         } else {
732             return scrollTable.editCellAt(row, column - frozenColumns);
733         }
734     }
735 
736     /***
737      * Returns the name of the column appearing in the view at
738      * column position <code>column</code>.
739      *
740      * @param column the column in the view being queried
741      * @return the name of the column at position <code>column</code>
742      *       in the view where the first column is column 0
743      */
744     public String getColumnName(final int column) {
745         if (column < frozenColumns) {
746             return lockedTable.getColumnName(column);
747         } else {
748             return scrollTable.getColumnName(column - frozenColumns);
749         }
750     }
751 
752     /***
753      * Returns the index of the column that contains the cell currently
754      * being edited.  If nothing is being edited, returns -1.
755      *
756      * @return the index of the column that contains the cell currently
757      *         being edited; returns -1 if nothing being edited
758      */
759     public int getEditingColumn() {
760         if (lockedTable.hasFocus()) {
761             return lockedTable.getEditingColumn();
762         }
763 
764         return scrollTable.getEditingColumn() + frozenColumns;
765     }
766 
767     /***
768      * Returns the index of the row that contains the cell currently
769      * being edited.  If nothing is being edited, returns -1.
770      *
771      * @return  the index of the row that contains the cell currently
772      *      being edited; returns -1 if nothing being edited
773      */
774     public int getEditingRow() {
775         if (lockedTable.hasFocus()) {
776             return lockedTable.getEditingRow();
777         }
778         return scrollTable.getEditingRow();
779     }
780 
781     /***
782      * Returns the color used to draw grid lines.
783      * The default color is look and feel dependent.
784      *
785      * @return  the color used to draw grid lines
786      * @see     #setGridColor
787      */
788     public Color getGridColor() {
789         return lockedTable.getGridColor();
790     }
791 
792     /***
793      * Returns true if rows can be selected.
794      *
795      * @return true if rows can be selected, otherwise false
796      * @see #setRowSelectionAllowed
797      */
798     public boolean getRowSelectionAllowed() {
799         return lockedTable.getRowSelectionAllowed();
800     }
801 
802     /***
803      * Returns the index of the first selected row, -1 if no row is selected.
804      *
805      * @return the index of the first selected row
806      */
807     public int getSelectedRow() {
808         return lockedTable.getSelectedRow();
809     }
810 
811     /***
812      * Returns the number of selected rows.
813      *
814      * @return the number of selected rows, 0 if no rows are selected
815      */
816     public int getSelectedRowCount() {
817         return lockedTable.getSelectedRowCount();
818     }
819 
820     /***
821      * Returns the indices of all selected rows.
822      *
823      * @return an array of integers containing the indices of all selected rows,
824      *         or an empty array if no row is selected
825      * @see #getSelectedRow
826      */
827     public int[] getSelectedRows() {
828         return lockedTable.getSelectedRows();
829     }
830 
831     /***
832      * Returns the <code>ListSelectionModel</code> that is used to maintain row
833      * selection state.
834      *
835      * @return  the object that provides row selection state, <code>null</code>
836      *          if row selection is not allowed
837      */
838     public ListSelectionModel getSelectionModel() {
839         return lockedTable.getSelectionModel();
840     }
841 
842     /***
843      * Returns the cell value at <code>row</code> and <code>column</code>.
844      * <p>
845      * <b>Note</b>: The column is specified in the table view's display
846      *              order, and not in the <code>TableModel</code>'s column
847      *              order.  This is an important distinction because as the
848      *              user rearranges the columns in the table,
849      *              the column at a given index in the view will change.
850      *              Meanwhile the user's actions never affect the model's
851      *              column ordering.
852      *
853      * @param row the row whose value is to be queried
854      * @param column the column whose value is to be queried
855      * @return the Object at the specified cell
856      */
857     public Object getValueAt(final int row, final int column) {
858         if (column < frozenColumns) {
859             return lockedTable.getValueAt(row, column);
860         } else {
861             return scrollTable.getValueAt(row, column - frozenColumns);
862         }
863     }
864 
865     /***
866      * Sets the value for the cell in the table model at <code>row</code>
867      * and <code>column</code>.
868      * <p>
869      * <b>Note</b>: The column is specified in the table view's display
870      *              order, and not in the <code>TableModel</code>'s column
871      *              order.  This is an important distinction because as the
872      *              user rearranges the columns in the table,
873      *              the column at a given index in the view will change.
874      *              Meanwhile the user's actions never affect the model's
875      *              column ordering.
876      *
877      * <code>aValue</code> is the new value.
878      *
879      * @param aValue the new value
880      * @param row the row of the cell to be changed
881      * @param column the column of the cell to be changed
882      * @see #getValueAt
883      */
884     public void setValueAt(final Object aValue, final int row, final int column) {
885         if (column < frozenColumns) {
886             lockedTable.setValueAt(aValue, row, column);
887         } else {
888             scrollTable.setValueAt(aValue, row, column - frozenColumns);
889         }
890     }
891 
892     /***
893      * Maps the index of the column in the view at <code>viewColumnIndex</code>
894      * to the index of the column in the table model.  Returns the index of the
895      * corresponding column in the model.  If <code>viewColumnIndex</code>
896      * is less than zero, returns <code>viewColumnIndex</code>.
897      *
898      * @param viewColumnIndex the index of the column in the view
899      * @return the index of the corresponding column in the model
900      *
901      * @see #convertColumnIndexToView
902      */
903     public int convertColumnIndexToModel(final int viewColumnIndex) {
904         if (viewColumnIndex < frozenColumns) {
905             return lockedTable.convertColumnIndexToModel(viewColumnIndex);
906         } else {
907             return scrollTable.convertColumnIndexToModel(viewColumnIndex - frozenColumns);
908         }
909     }
910 
911     /***
912      * Maps the index of the column in the table model at
913      * <code>modelColumnIndex</code> to the index of the column in the view.
914      * Returns the index of the corresponding column in the view; returns -1 if
915      * this column is not being displayed.  If <code>modelColumnIndex</code> is
916      * less than zero, returns <code>modelColumnIndex</code>.
917      *
918      * @param modelColumnIndex the index of the column in the model
919      * @return the index of the corresponding column in the view
920      *
921      * @see #convertColumnIndexToModel
922      */
923     public int convertColumnIndexToView(final int modelColumnIndex) {
924         final int lockedIndex = lockedTable.convertColumnIndexToView(modelColumnIndex);
925         final int scrollIndex = scrollTable.convertColumnIndexToView(modelColumnIndex);
926         if (lockedIndex >= 0) {
927             return lockedIndex;
928         } else {
929             return scrollIndex;
930         }
931     }
932 
933     /***
934      * Returns true if the cell at <code>row</code> and <code>column</code>
935      * is editable. Otherwise, invoking <code>setValueAt</code> on the cell
936      * will have no effect.
937      * <p>
938      * <b>Note</b>: The column is specified in the table view's display order,
939      * and not in the <code>TableModel</code>'s column order. This is an
940      * important distinction because as the user rearranges the columns in the
941      * table, the column at a given index in the view will change. Meanwhile the
942      * user's actions never affect the model's column ordering.
943      * @param row the row whose value is to be queried
944      * @param column the column whose value is to be queried
945      * @return true if the cell is editable
946      * @see #setValueAt
947      */
948     public boolean isCellEditable(final int row, final int column) {
949         return getModel().isCellEditable(row, convertColumnIndexToModel(column));
950     }
951 
952     /***
953      * Returns true if the cell at the specified position is selected.
954      * @param row the row being queried
955      * @param column the column being queried
956      *
957      * @return true if the cell at index <code>(row, column)</code> is selected,
958      *         where the first row and first column are at index 0
959      */
960     public boolean isCellSelected(final int row, final int column) {
961         if (column < frozenColumns) {
962             return lockedTable.isCellSelected(row, column);
963         } else {
964             return scrollTable.isCellSelected(row, column - frozenColumns);
965         }
966     }
967 
968     /***
969      * Returns true if a cell is being edited.
970      *
971      * @return  true if the table is editing a cell
972      */
973     public boolean isEditing() {
974         return lockedTable.isEditing();
975     }
976 
977     /***
978      * Returns true if the row at the specified index is selected.
979      *
980      * @param row the row being queried
981      * @return true if the row at index <code>row</code> is selected, where 0 is the
982      *         first row
983      */
984     public boolean isRowSelected(final int row) {
985         return lockedTable.isRowSelected(row);
986     }
987 
988     /***
989      * Sets the <code>editingRow</code> variable.
990      * @param row the row of the cell to be edited
991      */
992     public void setEditingRow(final int row) {
993         lockedTable.setEditingRow(row);
994         scrollTable.setEditingRow(row);
995     }
996 
997     /***
998      * Sets the table's selection mode to allow only single selections, a single
999      * contiguous interval, or multiple intervals.
1000      * <P>
1001      * <bold>Note:</bold>
1002      * <code>JTable</code> provides all the methods for handling
1003      * column and row selection.  When setting states,
1004      * such as <code>setSelectionMode</code>, it not only
1005      * updates the mode for the row selection model but also sets similar
1006      * values in the selection model of the <code>columnModel</code>.
1007      * If you want to have the row and column selection models operating
1008      * in different modes, set them both directly.
1009      * <p>
1010      * Both the row and column selection models for <code>JTable</code>
1011      * default to using a <code>DefaultListSelectionModel</code>
1012      * so that <code>JTable</code> works the same way as the
1013      * <code>JList</code>. See the <code>setSelectionMode</code> method
1014      * in <code>JList</code> for details about the modes.
1015      *
1016      * The following <code>selectionMode</code> values are allowed:
1017      * <ul>
1018      * <li><code>ListSelectionModel.SINGLE_SELECTION</code>
1019      * Only one list index can be selected at a time.  In this mode the
1020      * <code>setSelectionInterval</code> and <code>addSelectionInterval</code>
1021      * methods are equivalent, and only the second index argument is used.</li>
1022      * <li><code>ListSelectionModel.SINGLE_INTERVAL_SELECTION</code>
1023      * One contiguous index interval can be selected at a time. In this mode
1024      * <code>setSelectionInterval</code> and <code>addSelectionInterval</code>
1025      * are equivalent.</li>
1026      * <li><code>ListSelectionModel.MULTIPLE_INTERVAL_SELECTION</code>
1027      * In this mode, there's no restriction on what can be selected. This is
1028      * the default.</li>
1029      * </ul>
1030      *
1031      * @param selectionMode an integer specifying the type of selections
1032      *        that are permissible
1033      */
1034     public void setSelectionMode(final int selectionMode) {
1035         lockedTable.setSelectionMode(selectionMode);
1036         scrollTable.setSelectionMode(selectionMode);
1037     }
1038 
1039     /***
1040      * Returns the <code>TableModel</code> that provides the data displayed by
1041      * this <code>JTable</code>.
1042      *
1043      * @return the <code>TableModel</code> that provides the data displayed by
1044      * this <code>JTable</code>
1045      */
1046     public TableModel getModel() {
1047         return lockedTable.getModel();
1048     }
1049 
1050     /***
1051      * Sets whether the rows in this model can be selected.
1052      *
1053      * @param rowSelectionAllowed true if this model will allow row selection
1054      * @see #getRowSelectionAllowed
1055      */
1056     public void setRowSelectionAllowed(final boolean rowSelectionAllowed) {
1057         lockedTable.setRowSelectionAllowed(rowSelectionAllowed);
1058         scrollTable.setRowSelectionAllowed(rowSelectionAllowed);
1059     }
1060 
1061     /***
1062      * Listener for column change events of locked columns to adjust the
1063      * viewport size of the locked table.
1064      */
1065     private final class ColumnWidthChangeListener implements PropertyChangeListener {
1066 
1067         /*** Contructor. */
1068         private ColumnWidthChangeListener() {
1069             super();
1070         }
1071 
1072         /***
1073          * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
1074          */
1075         public void propertyChange(final PropertyChangeEvent evt) {
1076             if ("preferredWidth".equals(evt.getPropertyName())) {
1077                 lockedTable.setPreferredScrollableViewportSize(lockedTable.getPreferredSize());
1078             }
1079         }
1080     }
1081 
1082     /***
1083      *  Appends <code>aColumn</code> to the end of the array of columns held by
1084      *  this <code>JTable</code>'s column model.
1085      *  If the column name of <code>aColumn</code> is <code>null</code>,
1086      *  sets the column name of <code>aColumn</code> to the name
1087      *  returned by <code>getModel().getColumnName()</code>.
1088      *  <p>
1089      *  To add a column to this <code>JTable</code> to display the
1090      *  <code>modelColumn</code>'th column of data in the model with a
1091      *  given <code>width</code>, <code>cellRenderer</code>,
1092      *  and <code>cellEditor</code> you can use:
1093      *  <pre>
1094      *
1095      *      addColumn(new TableColumn(modelColumn, width, cellRenderer, cellEditor));
1096      *
1097      *  </pre>
1098      *  [Any of the <code>TableColumn</code> constructors can be used
1099      *  instead of this one.]
1100      *  The model column number is stored inside the <code>TableColumn</code>
1101      *  and is used during rendering and editing to locate the appropriates
1102      *  data values in the model. The model column number does not change
1103      *  when columns are reordered in the view.
1104      *
1105      *  @param  aColumn         the <code>TableColumn</code> to be added
1106      *  @see    #removeColumn
1107      */
1108     public void addColumn(final TableColumn aColumn) {
1109         scrollTable.addColumn(aColumn);
1110     }
1111 
1112     /***
1113      * Adds the rows from <code>index0</code> to <code>index1</code>, inclusive, to
1114      * the current selection.
1115      *
1116      * @param   index0 one end of the interval
1117      * @param   index1 the other end of the interval
1118      */
1119     public void addRowSelectionInterval(final int index0, final int index1) {
1120         lockedTable.addRowSelectionInterval(index0, index1);
1121     }
1122 
1123     /***
1124      * Deselects all selected columns and rows.
1125      */
1126     public void clearSelection() {
1127         lockedTable.clearSelection();
1128         scrollTable.clearSelection();
1129     }
1130 
1131     /***
1132      * Programmatically starts editing the cell at <code>row</code> and
1133      * <code>column</code>, if the cell is editable.
1134      * To prevent the <code>JTable</code> from editing a particular table,
1135      * column or cell value, return false from the <code>isCellEditable</code>
1136      * method in the <code>TableModel</code> interface.
1137      *
1138      * @param   row     the row to be edited
1139      * @param   column  the column to be edited
1140      * @param   e       event to pass into <code>shouldSelectCell</code>;
1141      *                  note that as of Java 2 platform v1.2, the call to
1142      *                  <code>shouldSelectCell</code> is no longer made
1143      * @return  false if for any reason the cell cannot be edited
1144      */
1145     public boolean editCellAt(final int row, final int column, final EventObject e) {
1146         if (column < frozenColumns) {
1147             return lockedTable.editCellAt(row, column, e);
1148         } else {
1149             return scrollTable.editCellAt(row, column - frozenColumns, e);
1150         }
1151     }
1152 
1153     /***
1154      * Determines whether the table will create default columns from the model.
1155      * If true, <code>setModel</code> will clear any existing columns and
1156      * create new columns from the new model.  Also, if the event in
1157      * the <code>tableChanged</code> notification specifies that the
1158      * entire table changed, then the columns will be rebuilt.
1159      * The default is true.
1160      *
1161      * @return  the autoCreateColumnsFromModel of the table
1162      * @see     #setAutoCreateColumnsFromModel
1163      */
1164     public boolean getAutoCreateColumnsFromModel() {
1165         return scrollTable.getAutoCreateColumnsFromModel();
1166     }
1167 
1168     /***
1169      * Returns the cell editor.
1170      *
1171      * @return the <code>TableCellEditor</code> that does the editing
1172      */
1173     public TableCellEditor getCellEditor() {
1174         TableCellEditor editor = lockedTable.getCellEditor();
1175         if (editor != null) {
1176             return editor;
1177         }
1178         return scrollTable.getCellEditor();
1179     }
1180 
1181     /***
1182      * Returns an appropriate editor for the cell specified by
1183      * <code>row</code> and <code>column</code>. If the
1184      * <code>TableColumn</code> for this column has a non-null editor,
1185      * returns that.  If not, finds the class of the data in this
1186      * column (using <code>getColumnClass</code>)
1187      * and returns the default editor for this type of data.
1188      * <p>
1189      * <b>Note:</b>
1190      * Throughout the table package, the internal implementations always
1191      * use this method to provide editors so that this default behavior
1192      * can be safely overridden by a subclass.
1193      *
1194      * @param row       the row of the cell to edit, where 0 is the first row
1195      * @param column    the column of the cell to edit,
1196      *          where 0 is the first column
1197      * @return          the editor for this cell;
1198      *          if <code>null</code> return the default editor for
1199      *          this type of cell
1200      */
1201     public TableCellEditor getCellEditor(final int row, final int column) {
1202         if (column < frozenColumns) {
1203             return lockedTable.getCellEditor(row, column);
1204         } else {
1205             return scrollTable.getCellEditor(row, column - frozenColumns);
1206         }
1207     }
1208 
1209     /***
1210      * Returns an appropriate renderer for the cell specified by this row and
1211      * column. If the <code>TableColumn</code> for this column has a non-null
1212      * renderer, returns that.  If not, finds the class of the data in
1213      * this column (using <code>getColumnClass</code>)
1214      * and returns the default renderer for this type of data.
1215      * <p>
1216      * <b>Note:</b>
1217      * Throughout the table package, the internal implementations always
1218      * use this method to provide renderers so that this default behavior
1219      * can be safely overridden by a subclass.
1220      *
1221      * @param row       the row of the cell to render, where 0 is the first row
1222      * @param column    the column of the cell to render,
1223      *          where 0 is the first column
1224      * @return the assigned renderer; if <code>null</code>
1225      *          returns the default renderer
1226      *          for this type of object
1227      * @see javax.swing.table.TableColumn#setCellRenderer
1228      * @see #setDefaultRenderer
1229      */
1230     public TableCellRenderer getCellRenderer(final int row, final int column) {
1231         if (column < frozenColumns) {
1232             return lockedTable.getCellRenderer(row, column);
1233         } else {
1234             return scrollTable.getCellRenderer(row, column - frozenColumns);
1235         }
1236     }
1237 
1238     /***
1239      * Returns true if both row and column selection models are enabled.
1240      * Equivalent to <code>getRowSelectionAllowed() &&
1241      * getColumnSelectionAllowed()</code>.
1242      *
1243      * @return true if both row and column selection models are enabled
1244      *
1245      * @see #setCellSelectionEnabled
1246      */
1247     public boolean getCellSelectionEnabled() {
1248         return lockedTable.getCellSelectionEnabled();
1249     }
1250 
1251     /***
1252      * Returns the <code>TableColumn</code> object for the column in the table
1253      * whose identifier is equal to <code>identifier</code>, when compared using
1254      * <code>equals</code>.
1255      *
1256      * @param identifier the identifier object
1257      * @return  the <code>TableColumn</code> object that matches the identifier
1258      * @exception IllegalArgumentException      if <code>identifier</code> is <code>null</code> or no <code>TableColumn</code>
1259      * has this identifier
1260      */
1261     public TableColumn getColumn(final Object identifier) throws IllegalArgumentException {
1262         try {
1263             return lockedTable.getColumn(identifier);
1264         } catch (IllegalArgumentException e) {
1265             return scrollTable.getColumn(identifier);
1266         }
1267     }
1268 
1269     /***
1270      * Returns the type of the column appearing in the view at
1271      * column position <code>column</code>.
1272      *
1273      * @param   column   the column in the view being queried
1274      * @return the type of the column at position <code>column</code>
1275      *      in the view where the first column is column 0
1276      */
1277     public Class getColumnClass(final int column) {
1278         if (column < frozenColumns) {
1279             return lockedTable.getColumnClass(column);
1280         } else {
1281             return scrollTable.getColumnClass(column - frozenColumns);
1282         }
1283     }
1284 
1285     /***
1286      * Returns the number of columns in the column model. Note that this may
1287      * be different from the number of columns in the table model.
1288      *
1289      * @return  the number of columns in the table
1290      * @see #getRowCount
1291      * @see #removeColumn
1292      */
1293     public int getColumnCount() {
1294         return lockedTable.getColumnCount() + scrollTable.getColumnCount();
1295     }
1296 
1297     /***
1298      * Returns always false.
1299      *
1300      * @return true if columns can be selected, otherwise false
1301      */
1302     public boolean getColumnSelectionAllowed() {
1303         return false;
1304     }
1305 
1306     /***
1307      * Returns the editor to be used when no editor has been set in
1308      * a <code>TableColumn</code>. During the editing of cells the editor is fetched from
1309      * a <code>Hashtable</code> of entries according to the class of the cells in the column. If
1310      * there is no entry for this <code>columnClass</code> the method returns
1311      * the entry for the most specific superclass. The <code>JTable</code> installs entries
1312      * for <code>Object</code>, <code>Number</code>, and <code>Boolean</code>, all of which can be modified
1313      * or replaced.
1314      *
1315      * @param   columnClass  return the default cell editor for this columnClass
1316      * @return the default cell editor to be used for this columnClass
1317      * @see     #setDefaultEditor
1318      * @see     #getColumnClass
1319      */
1320     public TableCellEditor getDefaultEditor(final Class columnClass) {
1321         return lockedTable.getDefaultEditor(columnClass);
1322     }
1323 
1324     /***
1325      * Returns the cell renderer to be used when no renderer has been set in
1326      * a <code>TableColumn</code>. During the rendering of cells the renderer is fetched from
1327      * a <code>Hashtable</code> of entries according to the class of the cells in the column. If
1328      * there is no entry for this <code>columnClass</code> the method returns
1329      * the entry for the most specific superclass. The <code>JTable</code> installs entries
1330      * for <code>Object</code>, <code>Number</code>, and <code>Boolean</code>, all of which can be modified
1331      * or replaced.
1332      *
1333      * @param   columnClass   return the default cell renderer
1334      *                for this columnClass
1335      * @return  the renderer for this columnClass
1336      * @see     #setDefaultRenderer
1337      * @see     #getColumnClass
1338      */
1339     public TableCellRenderer getDefaultRenderer(final Class columnClass) {
1340         return lockedTable.getDefaultRenderer(columnClass);
1341     }
1342 
1343     /***
1344      * Gets the value of the <code>dragEnabled</code> property.
1345      *
1346      * @return  the value of the <code>dragEnabled</code> property
1347      * @see #setDragEnabled
1348      * @since JDK 1.4
1349      */
1350     public boolean getDragEnabled() {
1351         return lockedTable.getDragEnabled();
1352     }
1353 
1354     /***
1355      * Returns the component that is handling the editing session.
1356      * If nothing is being edited, returns null.
1357      *
1358      * @return  Component handling editing session
1359      */
1360     public Component getEditorComponent() {
1361         return lockedTable.getEditorComponent();
1362     }
1363 
1364     /***
1365      * Returns the horizontal and vertical space between cells.
1366      * The default spacing is (1, 1), which provides room to draw the grid.
1367      *
1368      * @return  the horizontal and vertical spacing between cells
1369      * @see     #setIntercellSpacing
1370      */
1371     public Dimension getIntercellSpacing() {
1372         return lockedTable.getIntercellSpacing();
1373     }
1374 
1375     /***
1376      * Returns the preferred size of the viewport for this table.
1377      *
1378      * @return a <code>Dimension</code> object containing the <code>preferredSize</code> of the <code>JViewport</code>
1379      *         which displays this table
1380      */
1381     public Dimension getPreferredScrollableViewportSize() {
1382         return scrollPane.getPreferredSize();
1383     }
1384 
1385     /***
1386      * Returns the number of rows in this table's model.
1387      * @return the number of rows in this table's model
1388      *
1389      * @see #getColumnCount
1390      */
1391     public int getRowCount() {
1392         return lockedTable.getRowCount();
1393     }
1394 
1395     /***
1396      * Returns the height of a table row, in pixels.
1397      * The default row height is 16.0.
1398      *
1399      * @return  the height in pixels of a table row
1400      * @see     #setRowHeight(int)
1401      */
1402     public int getRowHeight() {
1403         return lockedTable.getRowHeight();
1404     }
1405 
1406     /***
1407      * Returns the height, in pixels, of the cells in <code>row</code>.
1408      * @param   row              the row whose height is to be returned
1409      * @return the height, in pixels, of the cells in the row
1410      */
1411     public int getRowHeight(final int row) {
1412         return lockedTable.getRowHeight(row);
1413     }
1414 
1415     /***
1416      * Gets the amount of empty space, in pixels, between cells. Equivalent to:
1417      * <code>getIntercellSpacing().height</code>.
1418      * @return the number of pixels between cells in a row
1419      *
1420      * @see     #setRowMargin
1421      */
1422     public int getRowMargin() {
1423         return lockedTable.getRowMargin();
1424     }
1425 
1426     /***
1427      * Returns <code>visibleRect.height</code> or
1428      * <code>visibleRect.width</code>,
1429      * depending on this table's orientation.  Note that as of Swing 1.1.1
1430      * (Java 2 v 1.2.2) the value
1431      * returned will ensure that the viewport is cleanly aligned on
1432      * a row boundary.
1433      *
1434      * @param visibleRect The view area visible within the viewport
1435      * @param orientation Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.
1436      * @param direction Less than zero to scroll up/left, greater than zero for down/right.
1437      * @return <code>visibleRect.height</code> or
1438      *                  <code>visibleRect.width</code>
1439      *                  per the orientation
1440      * @see Scrollable#getScrollableBlockIncrement
1441      */
1442     public int getScrollableBlockIncrement(final Rectangle visibleRect, final int orientation, final int direction) {
1443         return scrollTable.getScrollableBlockIncrement(visibleRect, orientation, direction);
1444     }
1445 
1446     /***
1447      * Returns false to indicate that the height of the viewport does not
1448      * determine the height of the table.
1449      *
1450      * @return false
1451      * @see Scrollable#getScrollableTracksViewportHeight
1452      */
1453     public boolean getScrollableTracksViewportHeight() {
1454         return scrollTable.getScrollableTracksViewportHeight();
1455     }
1456 
1457     /***
1458      * Returns false if <code>autoResizeMode</code> is set to
1459      * <code>AUTO_RESIZE_OFF</code>, which indicates that the
1460      * width of the viewport does not determine the width
1461      * of the table.  Otherwise returns true.
1462      *
1463      * @return false if <code>autoResizeMode</code> is set
1464      *   to <code>AUTO_RESIZE_OFF</code>, otherwise returns true
1465      * @see Scrollable#getScrollableTracksViewportWidth
1466      */
1467     public boolean getScrollableTracksViewportWidth() {
1468         return scrollTable.getScrollableTracksViewportWidth();
1469     }
1470 
1471     /***
1472      * Returns the scroll increment (in pixels) that completely exposes one new
1473      * row or column (depending on the orientation).
1474      * <p>
1475      * This method is called each time the user requests a unit scroll.
1476      *
1477      * @param visibleRect the view area visible within the viewport
1478      * @param orientation either <code>SwingConstants.VERTICAL</code>
1479      *                  or <code>SwingConstants.HORIZONTAL</code>
1480      * @param direction less than zero to scroll up/left,
1481      *                  greater than zero for down/right
1482      * @return the "unit" increment for scrolling in the specified direction
1483      * @see Scrollable#getScrollableUnitIncrement
1484      */
1485     public int getScrollableUnitIncrement(final Rectangle visibleRect, final int orientation, final int direction) {
1486         return lockedTable.getScrollableUnitIncrement(visibleRect, orientation, direction);
1487     }
1488 
1489     /***
1490      * Returns the background color for selected cells.
1491      *
1492      * @return the <code>Color</code> used for the background of selected list items
1493      * @see #setSelectionBackground
1494      * @see #setSelectionForeground
1495      */
1496     public Color getSelectionBackground() {
1497         return lockedTable.getSelectionBackground();
1498     }
1499 
1500     /***
1501      * Returns the foreground color for selected cells.
1502      *
1503      * @return the <code>Color</code> object for the foreground property
1504      * @see #setSelectionForeground
1505      * @see #setSelectionBackground
1506      */
1507     public Color getSelectionForeground() {
1508         return lockedTable.getSelectionForeground();
1509     }
1510 
1511     /***
1512      * Returns true if the table draws horizontal lines between cells, false if it
1513      * doesn't. The default is true.
1514      *
1515      * @return  true if the table draws horizontal lines between cells, false if it
1516      *          doesn't
1517      * @see     #setShowHorizontalLines
1518      */
1519     public boolean getShowHorizontalLines() {
1520         return lockedTable.getShowHorizontalLines();
1521     }
1522 
1523     /***
1524      * Returns true if the table draws vertical lines between cells, false if it
1525      * doesn't. The default is true.
1526      *
1527      * @return  true if the table draws vertical lines between cells, false if it
1528      *          doesn't
1529      * @see     #setShowVerticalLines
1530      */
1531     public boolean getShowVerticalLines() {
1532         return lockedTable.getShowVerticalLines();
1533     }
1534 
1535     /***
1536      * Returns true if the editor should get the focus
1537      * when keystrokes cause the editor to be activated.
1538      *
1539      * @return  true if the editor should get the focus
1540      *          when keystrokes cause the editor to be
1541      *          activated
1542      *
1543      * @see #setSurrendersFocusOnKeystroke
1544      */
1545     public boolean getSurrendersFocusOnKeystroke() {
1546         return lockedTable.getSurrendersFocusOnKeystroke();
1547     }
1548 
1549     /***
1550      * Moves the column <code>column</code> to the position currently
1551      * occupied by the column <code>targetColumn</code> in the view.
1552      * The old column at <code>targetColumn</code> is
1553      * shifted left or right to make room.
1554      *
1555      * @param   column                  the index of column to be moved
1556      * @param   targetColumn            the new index of the column
1557      */
1558     public void moveColumn(final int column, final int targetColumn) {
1559         if (column < frozenColumns && targetColumn < frozenColumns) {
1560             // move within locked table
1561             lockedTable.moveColumn(column, targetColumn);
1562         } else if (column < frozenColumns && targetColumn >= frozenColumns) {
1563             // move from locked to scrollable
1564             final TableColumn tableColumn = lockedTable.getColumnModel().getColumn(column);
1565             lockedTable.removeColumn(tableColumn);
1566             scrollTable.addColumn(tableColumn);
1567             scrollTable.moveColumn(scrollTable.getColumnCount(), targetColumn - frozenColumns);
1568         } else if (column >= frozenColumns && targetColumn < frozenColumns) {
1569             // move from scrollable to locked
1570             final TableColumn tableColumn = scrollTable.getColumnModel().getColumn(column);
1571             scrollTable.removeColumn(tableColumn);
1572             lockedTable.addColumn(tableColumn);
1573             lockedTable.moveColumn(lockedTable.getColumnCount(), targetColumn);
1574         } else {
1575             // move within scrollable
1576             scrollTable.moveColumn(column, targetColumn);
1577         }
1578     }
1579 
1580     /***
1581      * Prepares the renderer by querying the data model for the
1582      * value and selection state
1583      * of the cell at <code>row</code>, <code>column</code>.
1584      * Returns the component (may be a <code>Component</code>
1585      * or a <code>JComponent</code>) under the event location.
1586      * <p>
1587      * <b>Note:</b>
1588      * Throughout the table package, the internal implementations always
1589      * use this method to prepare renderers so that this default behavior
1590      * can be safely overridden by a subclass.
1591      *
1592      * @param renderer  the <code>TableCellRenderer</code> to prepare
1593      * @param row       the row of the cell to render, where 0 is the first row
1594      * @param column    the column of the cell to render,
1595      *          where 0 is the first column
1596      * @return          the <code>Component</code> under the event location
1597      * TODO handle the renderer correctly across the KTables
1598      */
1599     public Component prepareRenderer(final TableCellRenderer renderer, final int row, final int column) {
1600         if (column < frozenColumns) {
1601             return lockedTable.prepareRendererSuper(renderer, row, column);
1602         } else {
1603             return scrollTable.prepareRendererSuper(renderer, row, column - frozenColumns);
1604         }
1605     }
1606 
1607     /***
1608      * Removes <code>aColumn</code> from this <code>JTable</code>'s array
1609      * of columns. Note: this method does not remove the column of data from the
1610      * model; it just removes the <code>TableColumn</code> that was
1611      * responsible for displaying it.
1612      * @param aColumn the <code>TableColumn</code> to be removed
1613      * @see #addColumn
1614      */
1615     public void removeColumn(final TableColumn aColumn) {
1616         lockedTable.removeColumn(aColumn);
1617         scrollTable.removeColumn(aColumn);
1618     }
1619 
1620     /***
1621      * Deselects the columns from <code>index0</code> to <code>index1</code>, inclusive.
1622      *
1623      * @param   index0 one end of the interval
1624      * @param   index1 the other end of the interval
1625      */
1626     public void removeColumnSelectionInterval(final int index0, final int index1) {
1627         lockedTable.removeColumnSelectionInterval(index0, index1);
1628         scrollTable.removeColumnSelectionInterval(index0, index1);
1629     }
1630 
1631     /***
1632      * Discards the editor object and frees the real estate it used for
1633      * cell rendering.
1634      */
1635     public void removeEditor() {
1636         lockedTable.removeEditor();
1637         scrollTable.removeEditor();
1638     }
1639 
1640     /***
1641      * Deselects the rows from <code>index0</code> to <code>index1</code>, inclusive.
1642      *
1643      * @param   index0 one end of the interval
1644      * @param   index1 the other end of the interval
1645      */
1646     public void removeRowSelectionInterval(final int index0, final int index1) {
1647         lockedTable.removeRowSelectionInterval(index0, index1);
1648     }
1649 
1650     /***
1651      * Returns the index of the row that <code>point</code> lies in,
1652      * or -1 if the result is not in the range
1653      * [0, <code>getRowCount()</code>-1].
1654      *
1655      * @param   point   the location of interest
1656      * @return  the index of the row that <code>point</code> lies in,
1657      *          or -1 if the result is not in the range
1658      *          [0, <code>getRowCount()</code>-1]
1659      */
1660     public int rowAtPoint(final Point point) {
1661         return lockedTable.rowAtPoint(point);
1662     }
1663 
1664     /***
1665      *  Selects all rows, columns, and cells in the table.
1666      */
1667     public void selectAll() {
1668         lockedTable.selectAll();
1669     }
1670 
1671     /***
1672      * Sets this table's <code>autoCreateColumnsFromModel</code> flag.
1673      * This method calls <code>createDefaultColumnsFromModel</code> if
1674      * <code>autoCreateColumnsFromModel</code> changes from false to true.
1675      *
1676      * @param   autoCreateColumnsFromModel   true if <code>JTable</code> should automatically create columns
1677      * @see     #getAutoCreateColumnsFromModel
1678      */
1679     public void setAutoCreateColumnsFromModel(final boolean autoCreateColumnsFromModel) {
1680         lockedTable.setAutoCreateColumnsFromModel(autoCreateColumnsFromModel);
1681     }
1682 
1683     /***
1684      * Sets the table's auto resize mode when the table is resized.
1685      *
1686      * @param   mode One of 5 legal values:
1687      *                   AUTO_RESIZE_OFF,
1688      *                   AUTO_RESIZE_NEXT_COLUMN,
1689      *                   AUTO_RESIZE_SUBSEQUENT_COLUMNS,
1690      *                   AUTO_RESIZE_LAST_COLUMN,
1691      *                   AUTO_RESIZE_ALL_COLUMNS
1692      */
1693     public void setAutoResizeMode(final int mode) {
1694         lockedTable.setAutoResizeMode(mode);
1695         scrollTable.setAutoResizeMode(mode);
1696     }
1697 
1698     /***
1699      * Sets the <code>cellEditor</code> variable.
1700      *
1701      * @param anEditor  the TableCellEditor that does the editing
1702      */
1703     public void setCellEditor(final TableCellEditor anEditor) {
1704         lockedTable.setCellEditor(anEditor);
1705         scrollTable.setCellEditor(anEditor);
1706     }
1707 
1708     /***
1709      * Sets whether this table allows both a column selection and a
1710      * row selection to exist simultaneously. When set,
1711      * the table treats the intersection of the row and column selection
1712      * models as the selected cells. Override <code>isCellSelected</code> to
1713      * change this default behavior. This method is equivalent to setting
1714      * both the <code>rowSelectionAllowed</code> property and
1715      * <code>columnSelectionAllowed</code> property of the
1716      * <code>columnModel</code> to the supplied value.
1717      *
1718      * @param  cellSelectionEnabled     true if simultaneous row and column
1719      *                  selection is allowed
1720      * @see #getCellSelectionEnabled
1721      * @see #isCellSelected
1722      */
1723     public void setCellSelectionEnabled(final boolean cellSelectionEnabled) {
1724         lockedTable.setCellSelectionEnabled(cellSelectionEnabled);
1725         scrollTable.setCellSelectionEnabled(cellSelectionEnabled);
1726     }
1727 
1728     /***
1729      * Sets a default cell editor to be used if no editor has been set in
1730      * a <code>TableColumn</code>. If no editing is required in a table, or a
1731      * particular column in a table, uses the <code>isCellEditable</code>
1732      * method in the <code>TableModel</code> interface to ensure that this
1733      * <code>JTable</code> will not start an editor in these columns.
1734      * If editor is <code>null</code>, removes the default editor for this
1735      * column class.
1736      *
1737      * @param  columnClass  set the default cell editor for this columnClass
1738      * @param  editor   default cell editor to be used for this columnClass
1739      * @see     TableModel#isCellEditable
1740      * @see     #getDefaultEditor
1741      * @see     #setDefaultRenderer
1742      */
1743     public void setDefaultEditor(final Class columnClass, final TableCellEditor editor) {
1744         lockedTable.setDefaultEditor(columnClass, editor);
1745         scrollTable.setDefaultEditor(columnClass, editor);
1746     }
1747 
1748     /***
1749      * Sets a default cell renderer to be used if no renderer has been set in
1750      * a <code>TableColumn</code>. If renderer is <code>null</code>,
1751      * removes the default renderer for this column class.
1752      *
1753      * @param  columnClass     set the default cell renderer for this columnClass
1754      * @param  renderer        default cell renderer to be used for this columnClass
1755      * @see     #getDefaultRenderer
1756      * @see     #setDefaultEditor
1757      */
1758     public void setDefaultRenderer(final Class columnClass, final TableCellRenderer renderer) {
1759         lockedTable.setDefaultRenderer(columnClass, renderer);
1760         scrollTable.setDefaultRenderer(columnClass, renderer);
1761     }
1762 
1763     /***
1764      * Sets the <code>dragEnabled</code> property,
1765      * which must be <code>true</code> to enable
1766      * automatic drag handling (the first part of drag and drop)
1767      * on this component.
1768      * The <code>transferHandler</code> property needs to be set
1769      * to a non-<code>null</code> value for the drag to do
1770      * anything.  The default value of the <code>dragEnabled</code>
1771      * property
1772      * is <code>false</code>.
1773      *
1774      * <p>
1775      *
1776      * When automatic drag handling is enabled,
1777      * most look and feels begin a drag-and-drop operation
1778      * whenever the user presses the mouse button over a selection
1779      * and then moves the mouse a few pixels.
1780      * Setting this property to <code>true</code>
1781      * can therefore have a subtle effect on
1782      * how selections behave.
1783      *
1784      * <p>
1785      *
1786      * Some look and feels might not support automatic drag and drop;
1787      * they will ignore this property.  You can work around such
1788      * look and feels by modifying the component
1789      * to directly call the <code>exportAsDrag</code> method of a
1790      * <code>TransferHandler</code>.
1791      *
1792      * @param b the value to set the <code>dragEnabled</code> property to
1793      * @see #getDragEnabled
1794      * @since JDK 1.4
1795      */
1796     public void setDragEnabled(final boolean b) {
1797         lockedTable.setDragEnabled(b);
1798         scrollTable.setDragEnabled(b);
1799     }
1800 
1801     /***
1802      * Sets the <code>editingColumn</code> variable.
1803      * @param aColumn  the column of the cell to be edited
1804      */
1805     public void setEditingColumn(final int aColumn) {
1806         if (aColumn < frozenColumns) {
1807             lockedTable.setEditingColumn(aColumn);
1808         } else {
1809             scrollTable.setEditingColumn(aColumn - frozenColumns);
1810         }
1811     }
1812 
1813     /***
1814      * Sets the <code>rowMargin</code> and the <code>columnMargin</code> --
1815      * the height and width of the space between cells -- to
1816      * <code>intercellSpacing</code>.
1817      *
1818      * @param   intercellSpacing        a <code>Dimension</code>
1819      *                  specifying the new width
1820      *                  and height between cells
1821      * @see     #getIntercellSpacing
1822      */
1823     public void setIntercellSpacing(final Dimension intercellSpacing) {
1824         lockedTable.setIntercellSpacing(intercellSpacing);
1825     }
1826 
1827     /***
1828      * Sets the data model for this table to <code>newModel</code> and registers
1829      * with it for listener notifications from the new data model.
1830      *
1831      * @param   dataModel        the new data source for this table
1832      * @see     #getModel
1833      */
1834     public void setModel(final TableModel dataModel) {
1835         // Remove all columns from the scrollable table
1836         final TableColumnModel scrollColumnModel = scrollTable.getColumnModel();
1837         while (scrollColumnModel.getColumnCount() > 0) {
1838             scrollColumnModel.removeColumn(scrollColumnModel.getColumn(0));
1839         }
1840         // Remove all columns from the locked table
1841         final TableColumnModel lockedColumnModel = lockedTable.getColumnModel();
1842         while (lockedTable.getColumnCount() > 0) {
1843             lockedColumnModel.removeColumn(lockedColumnModel.getColumn(0));
1844         }
1845         lockedTable.setModel(dataModel);
1846         scrollTable.setModel(dataModel);
1847         // Create new columns from the new data model
1848         lockedTable.createDefaultColumnsFromModel();
1849         scrollTable.createDefaultColumnsFromModel();
1850         // Check if new model has enough columns for current number of frozen columns
1851         if (frozenColumns > dataModel.getColumnCount()) {
1852             frozenColumns = dataModel.getColumnCount();
1853         }
1854         // Remove the locked columns from the scrollable table
1855         for (int i = 0; i < frozenColumns; i++) {
1856             scrollColumnModel.removeColumn(scrollColumnModel.getColumn(0));
1857         }
1858         // Remove the scrollable columns from the locked table
1859         while (lockedTable.getColumnCount() > frozenColumns) {
1860             lockedColumnModel.removeColumn(lockedColumnModel.getColumn(frozenColumns));
1861         }
1862         // add size listeners to all locked columns
1863         for (int i = 0; i < lockedColumnModel.getColumnCount(); i++) {
1864             lockedColumnModel.getColumn(i).addPropertyChangeListener(new ColumnWidthChangeListener());
1865         }
1866     }
1867 
1868     /***
1869      * Sets the preferred size of the viewport for this table.
1870      *
1871      * @param size  a <code>Dimension</code> object specifying the <code>preferredSize</code> of a
1872      *              <code>JViewport</code> whose view is this table
1873      * @see Scrollable#getPreferredScrollableViewportSize
1874      */
1875     public void setPreferredScrollableViewportSize(final Dimension size) {
1876         scrollTable.setPreferredScrollableViewportSize(size);
1877     }
1878 
1879     /***
1880      * Sets the height, in pixels, of all cells to <code>rowHeight</code>,
1881      * revalidates, and repaints.
1882      * The height of the cells will be equal to the row height minus
1883      * the row margin.
1884      *
1885      * @param   rowHeight                       new row height
1886      * @see     #getRowHeight()
1887      */
1888     public void setRowHeight(final int rowHeight) {
1889         lockedTable.setRowHeight(rowHeight);
1890         scrollTable.setRowHeight(rowHeight);
1891     }
1892 
1893     /***
1894      * Sets the height for <code>row</code> to <code>rowHeight</code>,
1895      * revalidates, and repaints. The height of the cells in this row
1896      * will be equal to the row height minus the row margin.
1897      *
1898      * @param row the row whose height is being changed
1899      * @param rowHeight new row height, in pixels
1900      */
1901     public void setRowHeight(final int row, final int rowHeight) {
1902         lockedTable.setRowHeight(row, rowHeight);
1903         scrollTable.setRowHeight(row, rowHeight);
1904     }
1905 
1906     /***
1907      * Sets the amount of empty space between cells in adjacent rows.
1908      *
1909      * @param  rowMargin  the number of pixels between cells in a row
1910      * @see     #getRowMargin
1911      */
1912     public void setRowMargin(final int rowMargin) {
1913         lockedTable.setRowMargin(rowMargin);
1914         scrollTable.setRowMargin(rowMargin);
1915     }
1916 
1917     /***
1918      * Selects the rows from <code>index0</code> to <code>index1</code>,
1919      * inclusive.
1920      *
1921      * @param   index0 one end of the interval
1922      * @param   index1 the other end of the interval
1923      */
1924     public void setRowSelectionInterval(final int index0, final int index1) {
1925         lockedTable.setRowSelectionInterval(index0, index1);
1926         scrollTable.setRowSelectionInterval(index0, index1);
1927     }
1928 
1929     /***
1930      * Sets the row selection model for this table to <code>newModel</code>
1931      * and registers for listener notifications from the new selection model.
1932      *
1933      * @param   newModel        the new selection model
1934      * @see     #getSelectionModel
1935      */
1936     public void setSelectionModel(final ListSelectionModel newModel) {
1937         lockedTable.setSelectionModel(newModel);
1938         scrollTable.setSelectionModel(newModel);
1939     }
1940 
1941     /***
1942      * Sets whether editors in this JTable get the keyboard focus
1943      * when an editor is activated as a result of the JTable
1944      * forwarding keyboard events for a cell.
1945      * By default, this property is false, and the JTable
1946      * retains the focus unless the cell is clicked.
1947      *
1948      * @param surrendersFocusOnKeystroke true if the editor should get the focus
1949      *          when keystrokes cause the editor to be
1950      *          activated
1951      *
1952      *
1953      * @see #getSurrendersFocusOnKeystroke
1954      */
1955     public void setSurrendersFocusOnKeystroke(final boolean surrendersFocusOnKeystroke) {
1956         lockedTable.setSurrendersFocusOnKeystroke(surrendersFocusOnKeystroke);
1957         scrollTable.setSurrendersFocusOnKeystroke(surrendersFocusOnKeystroke);
1958     }
1959 
1960     /***
1961      * Sizes the table columns to fit the available space.
1962      * @param lastColumnOnly
1963      * @deprecated As of Swing version 1.0.3,
1964      * replaced by <code>doLayout()</code>.
1965      * @see #doLayout()
1966      */
1967     public void sizeColumnsToFit(final boolean lastColumnOnly) {
1968         scrollTable.sizeColumnsToFit(lastColumnOnly);
1969     }
1970 
1971     /***
1972      * Obsolete as of Java 2 platform v1.4.  Please use the
1973      * <code>doLayout()</code> method instead.
1974      * @param resizingColumn    the column whose resizing made this adjustment
1975      *                          necessary or -1 if there is no such column
1976      * @see  #doLayout()
1977      */
1978     public void sizeColumnsToFit(final int resizingColumn) {
1979         scrollTable.sizeColumnsToFit(resizingColumn);
1980     }
1981 
1982     /***
1983      * Returns the table header.
1984      *
1985      * @return table header
1986      */
1987     public JXTableHeader getTableHeader() {
1988         return collTableHeader;
1989     }
1990 
1991     /***
1992      * @see javax.swing.JComponent#setFont(java.awt.Font)
1993      */
1994     public void setFont(final Font f) {
1995         final double f12 = 1d + (2d / (2d + 2d + 2d + 2d + 2d)); // 1.2
1996         lockedTable.setFont(f);
1997         scrollTable.setFont(f);
1998         final Graphics graphics = lockedTable.getGraphics();
1999         final int height;
2000         if (graphics != null) {
2001             final Rectangle2D rectangle2D = graphics.getFontMetrics(f).getMaxCharBounds(lockedTable.getGraphics());
2002             height = (int) (rectangle2D.getHeight() + 1);
2003         } else {
2004             height = (int) (f.getSize() * f12 + 2 * lockedTable.getRowMargin());
2005         }
2006         lockedTable.setRowHeight(height);
2007         scrollTable.setRowHeight(height);
2008     }
2009 
2010     /***
2011      * Sets whether the columns in this model can be selected.
2012      *
2013      * @param b true if this model will allow column selection
2014      * @see #getColumnSelectionAllowed
2015      */
2016     public void setColumnSelectionAllowed(final boolean b) {
2017         lockedTable.setColumnSelectionAllowed(b);
2018         scrollTable.setColumnSelectionAllowed(b);
2019     }
2020 
2021     /***
2022      * Returns the horizontal scrollbar of the table.
2023      *
2024      * @return the horizontal scrollbar
2025      */
2026     public JScrollBar getHorizontalScrollBar() {
2027         return scrollPane.getHorizontalScrollBar();
2028     }
2029 
2030     /***
2031      * Returns the vertical scrollbar of the table.
2032      *
2033      * @return the vertical scrollbar
2034      */
2035     public JScrollBar getVerticalScrollBar() {
2036         return scrollPane.getVerticalScrollBar();
2037     }
2038 
2039     /***
2040      * Determines when the horizontal scrollbar appears in the scrollpane.
2041      * The options are:<ul>
2042      * <li>JXTable.HORIZONTAL_SCROLLBAR_AS_NEEDED
2043      * <li>JXTable.HORIZONTAL_SCROLLBAR_NEVER
2044      * <li>JXTable.HORIZONTAL_SCROLLBAR_ALWAYS
2045      * </ul>
2046      *
2047      * @param policy one of the three values listed above
2048      * @see #getHorizontalScrollBarPolicy
2049      */
2050     public void setHorizontalScrollBarPolicy(final int policy) {
2051         scrollPane.setHorizontalScrollBarPolicy(policy);
2052     }
2053 
2054     /***
2055      * Returns the horizontal scroll bar policy value.
2056      * @return the <code>horizontalScrollBarPolicy</code> property
2057      * @see #setHorizontalScrollBarPolicy
2058      */
2059     public int getHorizontalScrollBarPolicy() {
2060         return scrollPane.getHorizontalScrollBarPolicy();
2061     }
2062 
2063     /***
2064      * Determines when the vertical scrollbar appears in the scrollpane.
2065      * Legal values are:
2066      * <ul>
2067      * <li>JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED
2068      * <li>JScrollPane.VERTICAL_SCROLLBAR_NEVER
2069      * <li>JScrollPane.VERTICAL_SCROLLBAR_ALWAYS
2070      * </ul>
2071      *
2072      * @param policy one of the three values listed above
2073      * @throws IllegalArgumentException if <code>policy</code>
2074      *         is not one of the legal values shown above
2075      * @see #getVerticalScrollBarPolicy
2076      */
2077     public void setVerticalScrollBarPolicy(final int policy) throws IllegalArgumentException {
2078         scrollPane.setVerticalScrollBarPolicy(policy);
2079     }
2080 
2081     /***
2082      * Returns the vertical scroll bar policy value.
2083      * @return the <code>verticalScrollBarPolicy</code> property
2084      * @see #setVerticalScrollBarPolicy
2085      */
2086     public int getVerticalScrollBarPolicy() {
2087         return scrollPane.getVerticalScrollBarPolicy();
2088     }
2089 
2090     /***
2091      * Action of <tt>Goto First</tt> key events in scrollable table.
2092      */
2093     private final class ScrollFirstAction extends AbstractAction {
2094         /*** the original action. */
2095         private final Action originalAction;
2096         /*** Contructor.
2097          * @param theOriginalAction the original action */
2098         private ScrollFirstAction(final Action theOriginalAction) {
2099             super();
2100             this.originalAction = theOriginalAction;
2101         }
2102         /*** @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */
2103         public void actionPerformed(final ActionEvent e) {
2104             if (e.getSource() == scrollTable && frozenColumns > 0 && scrollTable.getSelectedColumn() == 0) {
2105                 scrollTable.transferFocusBackward();
2106                 lockedTable.changeSelection(scrollTable.getSelectedRow(), 0, false, false);
2107             } else {
2108                 originalAction.actionPerformed(e);
2109             }
2110         }
2111     }
2112 
2113     /***
2114      * Action of <tt>Goto Last</tt> key events in locked table.
2115      */
2116     private final class LockedLastAction extends AbstractAction {
2117         /*** the original action. */
2118         private final Action originalAction;
2119         /*** Contructor.
2120          * @param theOriginalAction the original action */
2121         private LockedLastAction(final Action theOriginalAction) {
2122             super();
2123             this.originalAction = theOriginalAction;
2124         }
2125         /*** @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */
2126         public void actionPerformed(final ActionEvent e) {
2127             if (e.getSource() == lockedTable && scrollTable.getColumnCount() > 0
2128                 && lockedTable.getSelectedColumn() == lockedTable.getColumnCount() - 1) {
2129                 lockedTable.transferFocus();
2130                 scrollTable.changeSelection(scrollTable.getSelectedRow(), scrollTable.getColumnCount() - 1, false, false);
2131             } else {
2132                 originalAction.actionPerformed(e);
2133             }
2134         }
2135     }
2136 
2137     /***
2138      * Action of <tt>Next</tt> key events in locked table.
2139      */
2140     private final class LockedNextAction extends AbstractAction {
2141         /*** the original action. */
2142         private final Action originalAction;
2143         /*** Contructor.
2144          * @param theOriginalAction the original action */
2145         private LockedNextAction(final Action theOriginalAction) {
2146             super();
2147             this.originalAction = theOriginalAction;
2148         }
2149         /*** @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */
2150         public void actionPerformed(final ActionEvent e) {
2151             if (lockedTable.getSelectedColumn() == lockedTable.getColumnCount() - 1 && scrollTable.getColumnCount() > 0) {
2152                 lockedTable.transferFocus();
2153                 scrollTable.changeSelection(lockedTable.getSelectedRow(), 0, false, false);
2154             } else {
2155                 originalAction.actionPerformed(e);
2156             }
2157         }
2158     }
2159 
2160     /***
2161      * Action of <tt>Next</tt> key events in scrollable table.
2162      */
2163     private final class ScrollNextAction extends AbstractAction {
2164         /*** the original action. */
2165         private final Action originalAction;
2166         /*** Contructor.
2167          * @param theOriginalAction the original action */
2168         private ScrollNextAction(final Action theOriginalAction) {
2169             super();
2170             this.originalAction = theOriginalAction;
2171         }
2172         /*** @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */
2173         public void actionPerformed(final ActionEvent e) {
2174             if (scrollTable.getSelectedColumn() == scrollTable.getColumnCount() - 1 && lockedTable.getColumnCount() > 0) {
2175                 scrollTable.transferFocusBackward();
2176                 lockedTable.changeSelection(nextRow(scrollTable), 0, false, false);
2177             } else {
2178                 originalAction.actionPerformed(e);
2179             }
2180         }
2181     }
2182 
2183     /***
2184      * Action of <tt>Previous</tt> key events in scrollable table.
2185      */
2186     private final class ScrollPreviousAction extends AbstractAction {
2187         /*** the original action. */
2188         private final Action originalAction;
2189         /*** Contructor.
2190          * @param theOriginalAction the original action */
2191         private ScrollPreviousAction(final Action theOriginalAction) {
2192             super();
2193             this.originalAction = theOriginalAction;
2194         }
2195         /*** @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */
2196         public void actionPerformed(final ActionEvent e) {
2197             if (scrollTable.getSelectedColumn() == 0 && frozenColumns > 0) {
2198                 scrollTable.transferFocusBackward();
2199                 lockedTable.changeSelection(scrollTable.getSelectedRow(), lockedTable.getColumnCount() - 1, false, false);
2200             } else {
2201                 originalAction.actionPerformed(e);
2202             }
2203         }
2204     }
2205 
2206     /***
2207      * Action of <tt>Previous</tt> key events in locked table.
2208      */
2209     private final class LockedPreviousAction extends AbstractAction {
2210         /*** the original action. */
2211         private final Action originalAction;
2212         /*** Contructor.
2213          * @param theOriginalAction the original action */
2214         private LockedPreviousAction(final Action theOriginalAction) {
2215             super();
2216             this.originalAction = theOriginalAction;
2217         }
2218         /*** @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */
2219         public void actionPerformed(final ActionEvent e) {
2220             if (lockedTable.getSelectedColumn() == 0 && scrollTable.getColumnCount() > 0) {
2221                 lockedTable.transferFocus();
2222                 scrollTable.changeSelection(previousRow(scrollTable), scrollTable.getColumnCount() - 1, false, false);
2223             } else {
2224                 originalAction.actionPerformed(e);
2225             }
2226         }
2227     }
2228 
2229     /***
2230      * @return the table column model
2231      */
2232     public TableColumnModel getColumnModel() {
2233         return columnModel;
2234     }
2235 
2236     /***
2237      * Sets the column model for this table to <code>newModel</code> and registers
2238      * for listener notifications from the new column model. Also sets
2239      * the column model of the <code>JTableHeader</code> to <code>columnModel</code>.
2240      *
2241      * @param   cm        the new data source for this table
2242      * @exception IllegalArgumentException      if <code>columnModel</code> is <code>null</code>
2243      * @see     #getColumnModel
2244      */
2245     public void setColumnModel(final TableColumnModel cm) throws IllegalArgumentException {
2246         if (columnModel == null) {
2247             throw new IllegalArgumentException("Cannot set a null ColumnModel");
2248         }
2249         columnModel = cm;
2250         final TableColumnModel cm1 = this.createDefaultColumnModel();
2251         final TableColumnModel cm2 = this.createDefaultColumnModel();
2252         for (int i = 0; i < cm.getColumnCount(); i++) {
2253             cm1.addColumn(cm.getColumn(i));
2254             cm2.addColumn(cm.getColumn(i));
2255         }
2256         lockedTable.setColumnModel(cm1);
2257         scrollTable.setColumnModel(cm2);
2258         // Remove the locked columns from the scrollable table
2259         final TableColumnModel scrollColumnModel = scrollTable.getColumnModel();
2260         for (int i = 0; i < frozenColumns; i++) {
2261             scrollColumnModel.removeColumn(scrollColumnModel.getColumn(0));
2262         }
2263         // Remove the scrollable columns from the locked table
2264         while (lockedTable.getColumnCount() > frozenColumns) {
2265             cm1.removeColumn(cm1.getColumn(frozenColumns));
2266         }
2267         // add size listeners to all locked columns
2268         for (int i = 0; i < cm1.getColumnCount(); i++) {
2269             cm1.getColumn(i).addPropertyChangeListener(new ColumnWidthChangeListener());
2270         }
2271     }
2272 
2273     /***
2274      * This fine grain notification tells listeners the exact range
2275      * of cells, rows, or columns that changed.
2276      *
2277      * @param e a tableModelEvent
2278      */
2279     public void tableChanged(final TableModelEvent e) {
2280         final int column = e.getColumn();
2281         if (column < frozenColumns) {
2282             lockedTable.tableChanged(e);
2283         } else {
2284             TableModelEvent event = new TableModelEvent((TableModel)
2285                 e.getSource(), e.getFirstRow(), e.getLastRow(),
2286                 e.getColumn()  - frozenColumns, e.getType());
2287             scrollTable.tableChanged(event);
2288         }
2289     }
2290 
2291     /***
2292      * Tells listeners that a column was moved due to a margin change.
2293      *
2294      * @param e a ChangeEvent
2295      */
2296     public void columnMarginChanged(final ChangeEvent e) {
2297         lockedTable.columnMarginChanged(e);
2298         scrollTable.columnMarginChanged(e);
2299     }
2300 
2301     /***
2302      * Tells listeners that the selection model of the
2303      * TableColumnModel changed.
2304      *
2305      * @param e a ListSelectionEvent
2306      */
2307     public void columnSelectionChanged(final ListSelectionEvent e) {
2308         lockedTable.columnSelectionChanged(e);
2309         scrollTable.columnSelectionChanged(e);
2310     }
2311 
2312     /***
2313      * Tells listeners that a column was added to the model.
2314      *
2315      * @param e a TableColumnModelEvent
2316      */
2317     public void columnAdded(final TableColumnModelEvent e) {
2318         lockedTable.columnAdded(e);
2319         scrollTable.columnAdded(e);
2320     }
2321 
2322     /***
2323      * Tells listeners that a column was repositioned.
2324      *
2325      * @param e a TableColumnModelEvent
2326      */
2327     public void columnMoved(final TableColumnModelEvent e) {
2328         lockedTable.columnMoved(e);
2329         scrollTable.columnMoved(e);
2330     }
2331 
2332     /***
2333      * Tells listeners that a column was removed from the model.
2334      *
2335      * @param e a TableColumnModelEvent
2336      */
2337     public void columnRemoved(final TableColumnModelEvent e) {
2338         final int from = e.getFromIndex();
2339         final int to = e.getToIndex();
2340         if (to < frozenColumns) {
2341             lockedTable.columnRemoved(e);
2342         } else if (from >= frozenColumns) {
2343             scrollTable.columnRemoved(e);
2344         } else {
2345             TableColumnModelEvent e1 = new TableColumnModelEvent((TableColumnModel) e.getSource(), from, frozenColumns - 1);
2346             TableColumnModelEvent e2 = new TableColumnModelEvent((TableColumnModel) e.getSource(), frozenColumns, to);
2347             lockedTable.columnRemoved(e1);
2348             scrollTable.columnRemoved(e2);
2349         }
2350     }
2351 
2352     /***
2353      * Called whenever the value of the selection changes.
2354      * @param e the event that characterizes the change.
2355      */
2356     public void valueChanged(final ListSelectionEvent e) {
2357         lockedTable.valueChanged(e);
2358         scrollTable.valueChanged(e);
2359     }
2360 
2361     /***
2362      * This tells the listeners the editor has canceled editing.
2363      *
2364      * @param e a ChangeEvent
2365      */
2366     public void editingCanceled(final ChangeEvent e) {
2367         lockedTable.superEditingCanceled(e);
2368         scrollTable.superEditingCanceled(e);
2369     }
2370 
2371     /***
2372      * This tells the listeners the editor has ended editing.
2373      *
2374      * @param e a ChangeEvent
2375      */
2376     public void editingStopped(final ChangeEvent e) {
2377         lockedTable.superEditingStopped(e);
2378         scrollTable.superEditingStopped(e);
2379     }
2380 
2381     /***
2382      * Sets the <code>tableHeader</code> working with this <code>JTable</code>
2383      * to <code>newHeader</code>.
2384      * It is legal to have a <code>null</code> <code>tableHeader</code>.
2385      *
2386      * @param   newHeader new tableHeader
2387      * @see     #getTableHeader
2388      */
2389     public void setTableHeader(final JXTableHeader newHeader) {
2390         if (collTableHeader != newHeader) {
2391             final JXTableHeader oldHeader = collTableHeader;
2392             if (oldHeader != null) {
2393                 oldHeader.setTable(null);
2394             }
2395             collTableHeader = newHeader;
2396             lockedHeader = newHeader.getLockedHeader();
2397             scrollHeader = newHeader.getScrollHeader();
2398             lockedHeader.setColumnModel(lockedTable.getColumnModel());
2399             scrollHeader.setColumnModel(scrollTable.getColumnModel());
2400             lockedHeader.setTable(lockedTable);
2401             scrollHeader.setTable(scrollTable);
2402             lockedTable.setTableHeader(lockedHeader);
2403             scrollTable.setTableHeader(scrollHeader);
2404             scrollPane.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER, lockedHeader);
2405         }
2406     }
2407 
2408     /***
2409      * Returns the auto resize mode of the table.  The default mode
2410      * is AUTO_RESIZE_SUBSEQUENT_COLUMNS.
2411      *
2412      * @return  the autoResizeMode of the table
2413      *
2414      * @see     #setAutoResizeMode(int)
2415      * @see     #doLayout()
2416      */
2417     public int getAutoResizeMode() {
2418         return lockedTable.getAutoResizeMode();
2419     }
2420 
2421     /***
2422      * Creates default columns for the table from
2423      * the data model using the <code>getColumnCount</code> method
2424      * defined in the <code>TableModel</code> interface.
2425      * <p>
2426      * Clears any existing columns before creating the
2427      * new columns based on information from the model.
2428      *
2429      * @see     #getAutoCreateColumnsFromModel
2430      */
2431     public void createDefaultColumnsFromModel() {
2432         createDefaultLockedColumnsFromModel();
2433         createDefaultScrollColumnsFromModel();
2434     }
2435 
2436     /***
2437      * Creates default columns for the locked table from
2438      * the data model using the <code>getColumnCount</code> method
2439      * defined in the <code>TableModel</code> interface.
2440      * <p>
2441      * Clears any existing columns before creating the
2442      * new columns based on information from the model.
2443      * <p>
2444      * If the model contains more than the number of frozen columns,
2445      * only the number of frozen columns is created.
2446      *
2447      * @see     #getAutoCreateColumnsFromModel
2448      */
2449     private void createDefaultLockedColumnsFromModel() {
2450         final TableModel m = getModel();
2451         if (m != null) {
2452             // Remove any current columns
2453             final TableColumnModel cm = lockedTable.getColumnModel();
2454             while (cm.getColumnCount() > 0) {
2455                 cm.removeColumn(cm.getColumn(0));
2456             }
2457             // Create new columns from the data model info
2458             int columnCount = Math.min(frozenColumns - 1, m.getColumnCount());
2459             for (int i = 0; i < columnCount; i++) {
2460                 TableColumn newColumn = new TableColumn(i);
2461                 addColumn(newColumn);
2462             }
2463         }
2464     }
2465 
2466     /***
2467      * Creates default columns for the scrollable table from
2468      * the data model using the <code>getColumnCount</code> method
2469      * defined in the <code>TableModel</code> interface and the number
2470      * of frozen columns .
2471      * <p>
2472      * Clears any existing columns before creating the
2473      * new columns based on information from the model.
2474      * <p>
2475      * If the model contains less than the number of frozen columns,
2476      * no columns are created.
2477      *
2478      * @see     #getAutoCreateColumnsFromModel
2479      */
2480     private void createDefaultScrollColumnsFromModel() {
2481         TableModel m = getModel();
2482         if (m != null) {
2483             // Remove any current columns
2484             TableColumnModel cm = scrollTable.getColumnModel();
2485             while (cm.getColumnCount() > 0) {
2486                 cm.removeColumn(cm.getColumn(0));
2487             }
2488             // Create new columns from the data model info
2489             int columnCount = m.getColumnCount() - frozenColumns;
2490             if (columnCount > 0) {
2491                 for (int i = frozenColumns; i < m.getColumnCount(); i++) {
2492                     TableColumn newColumn = new TableColumn(i);
2493                     addColumn(newColumn);
2494                 }
2495             }
2496         }
2497     }
2498 
2499     /***
2500      * Selects the columns from <code>index0</code> to <code>index1</code>,
2501      * inclusive.
2502      *
2503      * @param   index0 one end of the interval
2504      * @param   index1 the other end of the interval
2505      * @exception IllegalArgumentException      if <code>index0</code> or
2506      *                      <code>index1</code> lie outside
2507      *                                          [0, <code>getColumnCount()</code>-1]
2508      */
2509     public void setColumnSelectionInterval(final int index0, final int index1) throws IllegalArgumentException {
2510         if (index1 < frozenColumns) {
2511             final int count = scrollTable.getColumnCount();
2512             if (count > 0) {
2513                 scrollTable.removeColumnSelectionInterval(0, count - 1);
2514             }
2515             lockedTable.setColumnSelectionInterval(index0, index1);
2516         } else if (index0 >= frozenColumns) {
2517             final int count = lockedTable.getColumnCount();
2518             if (count > 0) {
2519                 lockedTable.removeColumnSelectionInterval(0, count - 1);
2520             }
2521             scrollTable.setColumnSelectionInterval(index0 - frozenColumns, index1 - frozenColumns);
2522         } else {
2523             lockedTable.setColumnSelectionInterval(index0, frozenColumns - 1);
2524             scrollTable.setColumnSelectionInterval(frozenColumns, index1 + frozenColumns);
2525         }
2526     }
2527 
2528     /***
2529      * Adds the columns from <code>index0</code> to <code>index1</code>,
2530      * inclusive, to the current selection.
2531      *
2532      * @param   index0 one end of the interval
2533      * @param   index1 the other end of the interval
2534      * @exception IllegalArgumentException      if <code>index0</code> or
2535      *                      <code>index1</code> lie outside
2536      *                                          [0, <code>getColumnCount()</code>-1]
2537      */
2538     public void addColumnSelectionInterval(final int index0, final int index1) throws IllegalArgumentException {
2539         if (index1 < frozenColumns) {
2540             lockedTable.setColumnSelectionInterval(index0, index1);
2541         } else if (index0 >= frozenColumns) {
2542             scrollTable.addColumnSelectionInterval(index0 - frozenColumns, index1 - frozenColumns);
2543         } else {
2544             lockedTable.addColumnSelectionInterval(index0, frozenColumns - 1);
2545             scrollTable.addColumnSelectionInterval(frozenColumns, index1 + frozenColumns);
2546         }
2547     }
2548 
2549     /***
2550      * Returns the index of the first selected column,
2551      * -1 if no column is selected.
2552      * @return the index of the first selected column
2553      */
2554     public int getSelectedColumn() {
2555         final int i = lockedTable.getSelectedColumn();
2556         return i == -1 ?  frozenColumns + scrollTable.getSelectedColumn() : i;
2557     }
2558 
2559     /***
2560      * Returns the indices of all selected columns.
2561      *
2562      * @return an array of integers containing the indices of all selected columns,
2563      *         or an empty array if no column is selected
2564      * @see #getSelectedColumn
2565      */
2566     public int[] getSelectedColumns() {
2567         int[] i1 = lockedTable.getSelectedColumns();
2568         int[] i2 = scrollTable.getSelectedColumns();
2569         int[] i3 = new int[i1.length + i2.length];
2570         for (int i = 0; i < i1.length; i++) {
2571             i3[i] = i1[i];
2572         }
2573         for (int i = 0; i < i2.length; i++) {
2574             i3[i + i1.length] = i2[i] + frozenColumns;
2575         }
2576         return i3;
2577     }
2578 
2579     /***
2580      * Returns the number of selected columns.
2581      *
2582      * @return the number of selected columns, 0 if no columns are selected
2583      */
2584     public int getSelectedColumnCount() {
2585         return lockedTable.getSelectedColumnCount() + scrollTable.getSelectedColumnCount();
2586     }
2587 
2588     /***
2589      * Returns true if the column at the specified index is selected.
2590      *
2591      * @param   column   the column in the column model
2592      * @return true if the column at index <code>column</code> is selected, where
2593      *              0 is the first column
2594      * @exception IllegalArgumentException      if <code>column</code> is not in the
2595      *                                          valid range
2596      */
2597     public boolean isColumnSelected(final int column) throws IllegalArgumentException {
2598         return lockedTable.isColumnSelected(column) || scrollTable.isColumnSelected(column - frozenColumns);
2599     }
2600 
2601     /***
2602      * Returns the index of the column that <code>point</code> lies in,
2603      * or -1 if the result is not in the range
2604      * [0, <code>getColumnCount()</code>-1].
2605      *
2606      * @param   point   the location of interest
2607      * @return  the index of the column that <code>point</code> lies in,
2608      *      or -1 if the result is not in the range
2609      *      [0, <code>getColumnCount()</code>-1]
2610      * @see     #rowAtPoint
2611      */
2612     public int columnAtPoint(final Point point) {
2613         int lockedWidth = lockedTable.getWidth();
2614         if (point.x < lockedWidth) {
2615             return lockedTable.columnAtPoint(point);
2616         } else {
2617             Point p = new Point(point.x - lockedWidth, point.y);
2618             return frozenColumns + scrollTable.columnAtPoint(p);
2619         }
2620     }
2621 
2622     /***
2623      * Returns a rectangle for the cell that lies at the intersection of
2624      * <code>row</code> and <code>column</code>.
2625      * If <code>includeSpacing</code> is true then the value returned
2626      * has the full height and width of the row and column
2627      * specified. If it is false, the returned rectangle is inset by the
2628      * intercell spacing to return the true bounds of the rendering or
2629      * editing component as it will be set during rendering.
2630      * <p>
2631      * If the column index is valid but the row index is less
2632      * than zero the method returns a rectangle with the
2633      * <code>y</code> and <code>height</code> values set appropriately
2634      * and the <code>x</code> and <code>width</code> values both set
2635      * to zero. In general, when either the row or column indices indicate a
2636      * cell outside the appropriate range, the method returns a rectangle
2637      * depicting the closest edge of the closest cell that is within
2638      * the table's range. When both row and column indices are out
2639      * of range the returned rectangle covers the closest
2640      * point of the closest cell.
2641      * <p>
2642      * In all cases, calculations that use this method to calculate
2643      * results along one axis will not fail because of anomalies in
2644      * calculations along the other axis. When the cell is not valid
2645      * the <code>includeSpacing</code> parameter is ignored.
2646      *
2647      * @param   row                   the row index where the desired cell
2648      *                                is located
2649      * @param   column                the column index where the desired cell
2650      *                                is located in the display; this is not
2651      *                                necessarily the same as the column index
2652      *                                in the data model for the table; the
2653      *                                {@link #convertColumnIndexToView(int)}
2654      *                                method may be used to convert a data
2655      *                                model column index to a display
2656      *                                column index
2657      * @param   includeSpacing        if false, return the true cell bounds -
2658      *                                computed by subtracting the intercell
2659      *                    spacing from the height and widths of
2660      *                    the column and row models
2661      *
2662      * @return  the rectangle containing the cell at location
2663      *          <code>row</code>,<code>column</code>
2664      */
2665     public Rectangle getCellRect(final int row, final int column, final boolean includeSpacing) {
2666         if (column < frozenColumns) {
2667             return lockedTable.getCellRect(row, column, includeSpacing);
2668         } else {
2669             return scrollTable.getCellRect(row, column - frozenColumns, includeSpacing);
2670         }
2671     }
2672 
2673     /***
2674      * Returns the L&F object that renders this component.
2675      *
2676      * @return the <code>TableUI</code> object that renders this component
2677      */
2678     public TableUI getUI() {
2679         return lockedTable.getUI();
2680     }
2681 
2682     /***
2683      * Sets the L&F object that renders this component and repaints.
2684      *
2685      * @param tableUi  the TableUI L&F object
2686      * @see javax.swing.JTable#setUI(javax.swing.plaf.TableUI)
2687      */
2688     public void setUI(final TableUI tableUi) {
2689         lockedTable.setUI(tableUi);
2690         scrollTable.setUI(tableUi);
2691     }
2692 
2693     /***
2694      * Prepares the editor by querying the data model for the value and
2695      * selection state of the cell at <code>row</code>, <code>column</code>.
2696      * <p>
2697      * <b>Note:</b>
2698      * Throughout the table package, the internal implementations always
2699      * use this method to prepare editors so that this default behavior
2700      * can be safely overridden by a subclass.
2701      *
2702      * @param editor  the <code>TableCellEditor</code> to set up
2703      * @param row     the row of the cell to edit,
2704      *            where 0 is the first row
2705      * @param column  the column of the cell to edit,
2706      *            where 0 is the first column
2707      * @return the <code>Component</code> being edited
2708      */
2709     public Component prepareEditor(final TableCellEditor editor, final int row, final int column) {
2710         if (column < frozenColumns) {
2711             return lockedTable.prepareEditor(editor, row, column);
2712         } else {
2713             return scrollTable.prepareEditor(editor, row, column - frozenColumns);
2714         }
2715     }
2716 
2717     /***
2718      * Appends <code>aColumn</code> after the currently last frozen column
2719      * held by this <code>JTable</code>'s column model.
2720      * If the column name of <code>aColumn</code> is <code>null</code>,
2721      * sets the column name of <code>aColumn</code> to the name
2722      * returned by <code>getModel().getColumnName()</code>.
2723      * <p>
2724      * The number of frozenn column is increased by one with a call to
2725      * this method.
2726      * <p>
2727      * To add a column to this <code>JTable</code> to display the
2728      * <code>modelColumn</code>'th column of data in the model with a
2729      * given <code>width</code>, <code>cellRenderer</code>,
2730      * and <code>cellEditor</code> you can use:
2731      * <pre>
2732      *
2733      *     addColumn(new TableColumn(modelColumn, width, cellRenderer, cellEditor));
2734      *
2735      * </pre>
2736      * [Any of the <code>TableColumn</code> constructors can be used
2737      * instead of this one.]
2738      * The model column number is stored inside the <code>TableColumn</code>
2739      * and is used during rendering and editing to locate the appropriates
2740      * data values in the model. The model column number does not change
2741      * when columns are reordered in the view.
2742      *
2743      * @param  aColumn         the <code>TableColumn</code> to be added
2744      * @see    #removeColumn
2745      */
2746     public void addFrozenColumn(final TableColumn aColumn) {
2747         lockedTable.addColumn(aColumn);
2748         frozenColumns++;
2749     }
2750 
2751     /***
2752      * Removes <code>aColumn</code> from this <code>JXTable</code>'s
2753      * frozen columns.  Note: this method does not remove the column
2754      * of data from the model; it just removes the frozen
2755      * <code>TableColumn</code> that was responsible for displaying it.
2756      * <p>
2757      * The number of frozenn column is decreased by one with a call to
2758      * this method.
2759      *
2760      * @param  aColumn         the <code>TableColumn</code> to be removed
2761      * @see    #addFrozenColumn
2762      */
2763     public void removeFrozenColumn(final TableColumn aColumn) {
2764         lockedTable.removeColumn(aColumn);
2765         frozenColumns++;
2766     }
2767 
2768     /***
2769      * Returns the scroll pane adjuster.
2770      * @return the scroll pane adjuster
2771      */
2772     protected JScrollPaneAdjuster getAdjuster() {
2773         return adjuster;
2774     }
2775 
2776     /***
2777      * Sets the <code>transferHandler</code> property,
2778      * which is <code>null</code> if the component does
2779      * not support data transfer operations.
2780      * <p>
2781      * If <code>newHandler</code> is not null, and the system property
2782      * <code>suppressSwingDropSupport</code> is not true, this will
2783      * install a <code>DropTarget</code> on the <code>JComponent</code>.
2784      * The default for the system property is false, so that a
2785      * <code>DropTarget</code> will be added.
2786      *
2787      * @param newHandler  mechanism for transfer of data to
2788      *    and from the component
2789      *
2790      * @see TransferHandler
2791      * @see javax.swing.JComponent#setTransferHandler(javax.swing.TransferHandler)
2792      */
2793     public void setTransferHandler(final TransferHandler newHandler) {
2794         //The KTables are the ones that need the handler
2795         getLockedTable().setTransferHandler(newHandler);
2796         getScrollTable().setTransferHandler(newHandler);
2797     }
2798 
2799     /***
2800      * Gets the <code>transferHandler</code> property.
2801      *
2802      * @return  the value of the <code>transferHandler</code> property
2803      *
2804      * @see TransferHandler
2805      * @see #setTransferHandler
2806      */
2807     public TransferHandler getTransferHandler() {
2808         //Both KTables should have the same handler. Return any of those.
2809         return getLockedTable().getTransferHandler();
2810     }
2811 
2812     /***
2813      * Adds the specified mouse listener to receive mouse events from this
2814      * component. If listener <code>l</code> is <code>null</code>, no
2815      * exception is thrown and no action is performed.
2816      * @param l the mouse listener
2817      * @see java.awt.event.MouseEvent
2818      * @see java.awt.event.MouseListener
2819      * @see #removeMouseListener
2820      * @see #getMouseListeners
2821      *
2822      * @see java.awt.Component#addMouseListener(java.awt.event.MouseListener)
2823      */
2824     public synchronized void addMouseListener(final MouseListener l) {
2825         if (l == null) {
2826             return;
2827         }
2828         mouseListener = AWTEventMulticaster.add(mouseListener, l);
2829         scrollTable.addMouseListener(new JXMouseMultiListener(l, KTable.TYPE_SCROLL));
2830         lockedTable.addMouseListener(new JXMouseMultiListener(l, KTable.TYPE_LOCKED));
2831     }
2832 
2833     /***
2834      * Removes the specified mouse listener so that it no longer
2835      * receives mouse events from this component. This method performs
2836      * no function, nor does it throw an exception, if the listener
2837      * specified by the argument was not previously added to this component.
2838      * If listener <code>l</code> is <code>null</code>,
2839      * no exception is thrown and no action is performed.
2840      *
2841      * @param    l   the mouse listener
2842      * @see      java.awt.event.MouseEvent
2843      * @see      java.awt.event.MouseListener
2844      * @see      #addMouseListener
2845      * @see      #getMouseListeners
2846      * @see java.awt.Component#removeMouseListener(java.awt.event.MouseListener)
2847      */
2848     public synchronized void removeMouseListener(final MouseListener l) {
2849         if (l == null) {
2850             return;
2851         }
2852         mouseListener = AWTEventMulticaster.remove(mouseListener, l);
2853     }
2854 
2855     /***
2856      * Returns an array of all the mouse listeners
2857      * registered on this component.
2858      *
2859      * @return all of this component's <code>MouseListener</code>s
2860      *         or an empty array if no mouse
2861      *         listeners are currently registered
2862      *
2863      * @see      #addMouseListener
2864      * @see      #removeMouseListener
2865      * @since    1.4
2866      * @see java.awt.Component#getMouseListeners()
2867      */
2868     public synchronized MouseListener[] getMouseListeners() {
2869         return (MouseListener[]) getListeners(MouseListener.class);
2870     }
2871 
2872     /***
2873      * Adds the specified mouse motion listener to receive mouse motion
2874      * events from this component.
2875      * If listener <code>l</code> is <code>null</code>,
2876      * no exception is thrown and no action is performed.
2877      *
2878      * @param    l   the mouse motion listener
2879      * @see      java.awt.event.MouseEvent
2880      * @see      java.awt.event.MouseMotionListener
2881      * @see      #removeMouseMotionListener
2882      * @see      #getMouseMotionListeners
2883      * @see java.awt.Component#addMouseMotionListener(java.awt.event.MouseMotionListener)
2884      */
2885     public synchronized void addMouseMotionListener(final MouseMotionListener l) {
2886         if (l == null) {
2887             return;
2888         }
2889         mouseMotionListener = AWTEventMulticaster.add(mouseMotionListener, l);
2890         scrollTable.addMouseMotionListener(new JXMouseMultiListener(l, KTable.TYPE_SCROLL));
2891         lockedTable.addMouseMotionListener(new JXMouseMultiListener(l, KTable.TYPE_LOCKED));
2892     }
2893 
2894     /***
2895      * Removes the specified mouse motion listener so that it no longer
2896      * receives mouse motion events from this component. This method performs
2897      * no function, nor does it throw an exception, if the listener
2898      * specified by the argument was not previously added to this component.
2899      * If listener <code>l</code> is <code>null</code>,
2900      * no exception is thrown and no action is performed.
2901      *
2902      * @param    l   the mouse motion listener
2903      * @see      java.awt.event.MouseEvent
2904      * @see      java.awt.event.MouseMotionListener
2905      * @see      #addMouseMotionListener
2906      * @see      #getMouseMotionListeners
2907      * @see java.awt.Component#removeMouseMotionListener(java.awt.event.MouseMotionListener)
2908      */
2909     public synchronized void removeMouseMotionListener(final MouseMotionListener l) {
2910         if (l == null) {
2911             return;
2912         }
2913         mouseMotionListener = AWTEventMulticaster.remove(mouseMotionListener, l);
2914     }
2915 
2916     /***
2917      * Returns an array of all the mouse motion listeners
2918      * registered on this component.
2919      *
2920      * @return all of this component's <code>MouseMotionListener</code>s
2921      *         or an empty array if no mouse motion
2922      *         listeners are currently registered
2923      *
2924      * @see      #addMouseMotionListener
2925      * @see      #removeMouseMotionListener
2926      * @see java.awt.Component#getMouseMotionListeners()
2927      */
2928     public synchronized MouseMotionListener[] getMouseMotionListeners() {
2929         return (MouseMotionListener[]) getListeners(MouseMotionListener.class);
2930     }
2931 
2932     /***
2933      * Adds the specified mouse wheel listener to receive mouse wheel events
2934      * from this component.  Containers also receive mouse wheel events from
2935      * sub-components.
2936      * If l is null, no exception is thrown and no action is performed.
2937      *
2938      * @param    l   the mouse wheel listener.
2939      * @see      java.awt.event.MouseWheelEvent
2940      * @see      java.awt.event.MouseWheelListener
2941      * @see      #removeMouseWheelListener
2942      * @see      #getMouseWheelListeners
2943      * @see java.awt.Component#addMouseWheelListener(java.awt.event.MouseWheelListener)
2944      */
2945     public synchronized void addMouseWheelListener(final MouseWheelListener l) {
2946         if (l == null) {
2947             return;
2948         }
2949         mouseWheelListener = AWTEventMulticaster.add(mouseWheelListener, l);
2950         scrollTable.addMouseWheelListener(new JXMouseMultiListener(l, KTable.TYPE_SCROLL));
2951         lockedTable.addMouseWheelListener(new JXMouseMultiListener(l, KTable.TYPE_LOCKED));
2952     }
2953 
2954     /***
2955      * Removes the specified mouse wheel listener so that it no longer
2956      * receives mouse wheel events from this component. This method performs
2957      * no function, nor does it throw an exception, if the listener
2958      * specified by the argument was not previously added to this component.
2959      * If l is null, no exception is thrown and no action is performed.
2960      *
2961      * @param    l   the mouse wheel listener.
2962      * @see      java.awt.event.MouseWheelEvent
2963      * @see      java.awt.event.MouseWheelListener
2964      * @see      #addMouseWheelListener
2965      * @see      #getMouseWheelListeners
2966      * @see java.awt.Component#removeMouseWheelListener(java.awt.event.MouseWheelListener)
2967      */
2968     public synchronized void removeMouseWheelListener(final MouseWheelListener l) {
2969         if (l == null) {
2970             return;
2971         }
2972         mouseWheelListener = AWTEventMulticaster.remove(mouseWheelListener, l);
2973     }
2974 
2975     /***
2976      * Returns an array of all the mouse wheel listeners
2977      * registered on this component.
2978      *
2979      * @return all of this component's <code>MouseWheelListener</code>s
2980      *         or an empty array if no mouse wheel
2981      *         listeners are currently registered
2982      *
2983      * @see      #addMouseWheelListener
2984      * @see      #removeMouseWheelListener
2985      * @see java.awt.Component#getMouseWheelListeners()
2986      */
2987     public synchronized MouseWheelListener[] getMouseWheelListeners() {
2988         return (MouseWheelListener[]) getListeners(MouseWheelListener.class);
2989     }
2990 
2991     /***
2992      * Returns an array of all the objects currently registered
2993      * as <code><em>Foo</em>Listener</code>s
2994      * upon this <code>Component</code>.
2995      * <code><em>Foo</em>Listener</code>s are registered using the
2996      * <code>add<em>Foo</em>Listener</code> method.
2997      *
2998      * <p>
2999      * You can specify the <code>listenerType</code> argument
3000      * with a class literal, such as
3001      * <code><em>Foo</em>Listener.class</code>.
3002      * For example, you can query a
3003      * <code>Component</code> <code>c</code>
3004      * for its mouse listeners with the following code:
3005      *
3006      * <pre>MouseListener[] mls = (MouseListener[])(c.getListeners(MouseListener.class));</pre>
3007      *
3008      * If no such listeners exist, this method returns an empty array.
3009      *
3010      * @param listenerType the type of listeners requested; this parameter
3011      *          should specify an interface that descends from
3012      *          <code>java.util.EventListener</code>
3013      * @return an array of all objects registered as
3014      *          <code><em>Foo</em>Listener</code>s on this component,
3015      *          or an empty array if no such listeners have been added
3016      *
3017      * @see #getMouseListeners
3018      */
3019     public EventListener[] getListeners(final Class listenerType) {
3020         final EventListener l;
3021         if (listenerType == MouseListener.class) {
3022             l = mouseListener;
3023         } else if (listenerType == MouseMotionListener.class) {
3024             l = mouseMotionListener;
3025         } else if (listenerType == MouseWheelListener.class) {
3026             l = mouseWheelListener;
3027         } else {
3028             return super.getListeners(listenerType);
3029         }
3030         return AWTEventMulticaster.getListeners(l, listenerType);
3031     }
3032 
3033     /***
3034      * @see javax.swing.JComponent#setBackground(java.awt.Color)
3035      */
3036     public void setBackground(final Color bg) {
3037         scrollPane.setBackground(bg);
3038     }
3039 
3040     /***
3041      * @see javax.swing.JComponent#setOpaque(boolean)
3042      */
3043     public void setOpaque(final boolean isOpaque) {
3044         scrollPane.setOpaque(isOpaque);
3045     }
3046 
3047     /***
3048      * Internal mouse listener delegate for all three mouse listener interfaces.
3049      */
3050     private final class JXMouseMultiListener implements MouseListener, MouseMotionListener, MouseWheelListener {
3051 
3052         /*** Reference to the enclosing mouse listener. */
3053         private final transient EventListener parentEventListener;
3054 
3055         /*** Type of tabel, can be {@link KTable#TYPE_LOCKED}, {@link KTable#TYPE_SCROLL} or {@link KTable#TYPE_NONE}. */
3056         private final int tableType;
3057 
3058         /***
3059          * Constructor.
3060          * @param l mouse listener of sourrounding JTable
3061          * @param type table tye ({@link KTable#TYPE_LOCKED} or {@link KTable#TYPE_SCROLL})
3062          */
3063         private JXMouseMultiListener(final EventListener l, final int type) {
3064             super();
3065             parentEventListener = l;
3066             tableType = type;
3067         }
3068 
3069         /***
3070          * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
3071          */
3072         public void mouseClicked(final MouseEvent e) {
3073             ((MouseListener) parentEventListener).mouseClicked(translateMouseEvent(e));
3074         }
3075 
3076         /***
3077          * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
3078          */
3079         public void mouseEntered(final MouseEvent e) {
3080             ((MouseListener) parentEventListener).mouseEntered(translateMouseEvent(e));
3081         }
3082 
3083         /***
3084          * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
3085          */
3086         public void mouseExited(final MouseEvent e) {
3087             ((MouseListener) parentEventListener).mouseExited(translateMouseEvent(e));
3088         }
3089 
3090         /***
3091          * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
3092          */
3093         public void mousePressed(final MouseEvent e) {
3094             ((MouseListener) parentEventListener).mousePressed(translateMouseEvent(e));
3095         }
3096 
3097         /***
3098          * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
3099          */
3100         public void mouseReleased(final MouseEvent e) {
3101             ((MouseListener) parentEventListener).mouseReleased(translateMouseEvent(e));
3102         }
3103 
3104         /***
3105          * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
3106          */
3107         public void mouseDragged(final MouseEvent e) {
3108             ((MouseMotionListener) parentEventListener).mouseDragged(translateMouseEvent(e));
3109         }
3110 
3111         /***
3112          * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
3113          */
3114         public void mouseMoved(final MouseEvent e) {
3115             ((MouseMotionListener) parentEventListener).mouseMoved(translateMouseEvent(e));
3116         }
3117 
3118         /***
3119          * @see java.awt.event.MouseWheelListener#mouseWheelMoved(java.awt.event.MouseWheelEvent)
3120          */
3121         public void mouseWheelMoved(final MouseWheelEvent e) {
3122             ((MouseWheelListener) parentEventListener).mouseWheelMoved(translateMouseWheelEvent(e));
3123         }
3124 
3125         /***
3126          * Translates the coordinates of a KTable's mouse event into
3127          * coordinated of the surrounding JTable.
3128          *
3129          * @param e mouse event of KTable
3130          * @return mouse event for JTable
3131          */
3132         private MouseEvent translateMouseEvent(final MouseEvent e) {
3133             final int x;
3134             if (tableType == KTable.TYPE_LOCKED) {
3135                 x = e.getX();
3136             } else {
3137                 x = e.getX() + lockedTable.getWidth();
3138             }
3139             return new MouseEvent(
3140                     e.getComponent(), e.getID(), e.getWhen(), e.getModifiers(), x, e.getY(),
3141                     e.getClickCount(), e.isPopupTrigger(), e.getButton());
3142         }
3143 
3144         /***
3145          * Translates the coordinates of a KTable's mouse wheel event into
3146          * coordinated of the surrounding JTable.
3147          *
3148          * @param e mouse event of KTable
3149          * @return mouse event for JTable
3150          */
3151         private MouseWheelEvent translateMouseWheelEvent(final MouseWheelEvent e) {
3152             final int x;
3153             if (tableType == KTable.TYPE_LOCKED) {
3154                 x = e.getX();
3155             } else {
3156                 x = e.getX() + lockedTable.getWidth();
3157             }
3158             return new MouseWheelEvent(
3159                     e.getComponent(), e.getID(), e.getWhen(), e.getModifiers(), x, e.getY(),
3160                     e.getClickCount(), e.isPopupTrigger(), e.getScrollType(), e.getScrollAmount(), e.getWheelRotation());
3161         }
3162     }
3163 
3164     /***
3165      * @see javax.swing.JScrollPane#setUI(javax.swing.plaf.ScrollPaneUI)
3166      */
3167     public void setScrollPaneUI(final ScrollPaneUI scrollpaneui) {
3168         scrollPane.setUI(scrollpaneui);
3169     }
3170 }