Using the RowHeader Action Proxy

You may notice that we do not specify the names of the actions this time, we look them up in the InputMap. This is another, more robust solution. As this is a training example, both methods are shown.

  1 
  2 package com.vizitsolutions.identitytable;
  3 
  4 import javax.swing.ActionMap;
  5 import javax.swing.table.DefaultTableColumnModel;
  6 import javax.swing.InputMap;
  7 import javax.swing.JComponent;
  8 import javax.swing.JScrollPane;
  9 import javax.swing.JTable;
 10 import javax.swing.JViewport;
 11 import java.awt.event.KeyEvent;
 12 import javax.swing.KeyStroke;
 13 import javax.swing.ListSelectionModel;
 14 import javax.swing.table.TableColumn;
 15 import javax.swing.table.TableColumnModel;
 16 
 17 /**
 18  * <p>$Id$</p>
 19  *
 20  * <p>
 21  * This class maps a TableModel into two JTables, and places
 22  * them into a JScrollPane. The first JTable contains n fixed
 23  * columns, and represents what is commonly called a rowheader.
 24  * This is placed in the rowheader area of the JScrollPane. These
 25  * columns will not scroll horizontally. This is particularly
 26  * useful when you have one or more columns that identify an object
 27  * and several additional columns that contain attributes of the
 28  * object. Placing the identifying columns in these fixed columns
 29  * ensures that they are always visible while the attributes of the
 30  * object are scrolled into view.
 31  * </p>
 32  * <p>
 33  * The second JTable contains the remaining columns and is placed in
 34  * the main viewport and will scroll normally.
 35  * </p>
 36  *
 37  * @author Alex Kluge
 38  * @version $Revision$, $Date$
 39  */
 40 public class IdentityTable5 extends JScrollPane
 41 {
 42     // These names follow a similar pattern to those used in JTable UI delegate.
 43     public static final String LEFT_ARROW_ACTION_NAME  = "selectPreviousColumn";
 44     public static final String RIGHT_ARROW_ACTION_NAME = "selectNextColumn";
 45     public static final String SHIFT_TAB_ACTION_NAME   = "selectPreviousColumnCell";
 46     public static final String TAB_ACTION_NAME         = "selectNextColumnCell";
 47     
 48     private JTable           mainTable;
 49     private TableColumnModel mainColumnModel      = new DefaultTableColumnModel();
 50     private JTable           rowHeaderTable;
 51     private TableColumnModel rowHeaderColumnModel = new DefaultTableColumnModel();
 52     
 53     /**
 54      * Creates a new instance of IdentityTable
 55      */
 56     public IdentityTable5(IdentityTableModel baseTableModel)
 57     {
 58         // Start out life as an empty scrollpane.
 59         super();
 60         
 61         // Loop over each of the fixed columns, and create columns in the row
 62         // header column model for them.
 63         TableColumn column;
 64         int         columnCount         = baseTableModel.getColumnCount();
 65         int         currentColumn;
 66         int         identityColumnCount = baseTableModel.getIdentityColumnCount();
 67         
 68         for (currentColumn = 0; currentColumn<identityColumnCount; currentColumn++)
 69         {
 70             column = new TableColumn(currentColumn);
 71             column.setHeaderValue(baseTableModel.getColumnName(currentColumn));
 72             
 73             rowHeaderColumnModel.addColumn(column);
 74         }
 75         
 76         // Now loop over the remaining columns, and add them to the main table's column model.
 77         for (currentColumn = identityColumnCount; currentColumn<columnCount; currentColumn++)
 78         {
 79             column = new TableColumn(currentColumn);
 80             column.setHeaderValue(baseTableModel.getColumnName(currentColumn));
 81             
 82             mainColumnModel.addColumn(column);
 83         }
 84         
 85         // Providing a column model here automatically sets the
 86         // autoCreateColumnsFromModel flag to false.
 87         rowHeaderTable = new JTable(baseTableModel, rowHeaderColumnModel);
 88         rowHeaderTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
 89 
 90         mainTable      = new JTable(baseTableModel, mainColumnModel);
 91         mainTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
 92         
 93         // Ensure that both tables have common selections.
 94         // Save the row selection model for later.
 95         ListSelectionModel rowSelectionModel = mainTable.getSelectionModel();
 96         rowHeaderTable.setSelectionModel(rowSelectionModel);
 97         rowHeaderTable.setPreferredScrollableViewportSize(rowHeaderTable.getPreferredSize());
 98         
 99         // This puts the column headers in the upper left corner above the row
100         // header columns. This is the same thing that happens when a JTable configures
101         // the containing JScrollPane.
102         setCorner(JScrollPane.UPPER_LEFT_CORNER, rowHeaderTable.getTableHeader());
103         
104         JViewport rowHeaderViewPort = new JViewport();
105         rowHeaderViewPort.setView(rowHeaderTable);
106         setRowHeader(rowHeaderViewPort);
107 
108         // Listen for resize events on the row header, and resize the main table as appropriate.
109         RowHeaderResizeListener rowHeaderResizeListener = new RowHeaderResizeListener(this,           rowHeaderViewPort,
110                                                                                       rowHeaderTable, mainTable);
111         rowHeaderTable.addComponentListener(rowHeaderResizeListener);
112 
113         setViewportView(mainTable);
114         
115        installKeybordActions(rowHeaderTable,                      mainTable,
116                               rowSelectionModel,                   rowHeaderColumnModel.getSelectionModel(),
117                               mainColumnModel.getSelectionModel());
118     }
119     
120     protected void installKeybordActions(JTable             rowHeaderTable,           final JTable       mainTable,
121                                          ListSelectionModel rowSelectionModel,        ListSelectionModel headerColumnSelectionModel,
122                                          ListSelectionModel mainColumnSelectionModel)
123     {
124         InputMap rowHeaderInputMap
125                     = rowHeaderTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
126         ActionMap rowHeaderActionmap = rowHeaderTable.getActionMap();
127         ActionMap mainActionMap      = mainTable.getActionMap();
128 
129 
130          // Tab action handler, shift forward one cell, with wrapping
131         TabKeyActionHandler tabKeyActionHandler
132                     = new TabKeyActionHandler(rowHeaderTable,           mainTable,
133                                               rowSelectionModel,        headerColumnSelectionModel,
134                                               mainColumnSelectionModel, 1,
135                                               true);
136         // Put the action handler into the appropriate place for both the
137         // row header and the main table.
138         rowHeaderActionmap.put(TAB_ACTION_NAME, tabKeyActionHandler);
139         mainActionMap.put(TAB_ACTION_NAME,      tabKeyActionHandler);
140 
141         // Right arrow action handler, shift forward one cell, no wrapping.
142         tabKeyActionHandler
143                     = new TabKeyActionHandler(rowHeaderTable,           mainTable,
144                                               rowSelectionModel,        headerColumnSelectionModel,
145                                               mainColumnSelectionModel, 1,
146                                               false);
147 
148         rowHeaderActionmap.put(RIGHT_ARROW_ACTION_NAME, tabKeyActionHandler);
149         mainActionMap.put(RIGHT_ARROW_ACTION_NAME,      tabKeyActionHandler);
150 
151         // Shift-Tab action handler, shift backward one cell, with wrapping
152         tabKeyActionHandler
153                     = new TabKeyActionHandler(rowHeaderTable,           mainTable,
154                                               rowSelectionModel,        headerColumnSelectionModel,
155                                               mainColumnSelectionModel, -1,
156                                               true);
157 
158         rowHeaderActionmap.put(SHIFT_TAB_ACTION_NAME, tabKeyActionHandler);
159         mainActionMap.put(SHIFT_TAB_ACTION_NAME,      tabKeyActionHandler);
160 
161         // Left arrow action handler, shift backward one cell, no wrapping.
162         tabKeyActionHandler
163                     = new TabKeyActionHandler(rowHeaderTable,           mainTable,
164                                               rowSelectionModel,        headerColumnSelectionModel,
165                                               mainColumnSelectionModel, -1,
166                                               false);
167 
168         rowHeaderActionmap.put(LEFT_ARROW_ACTION_NAME, tabKeyActionHandler);
169         mainActionMap.put(LEFT_ARROW_ACTION_NAME,      tabKeyActionHandler);
170         
171         // map the row header page down to the main table's page down.
172         Object actionMapKey = rowHeaderInputMap.get(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0));
173         rowHeaderActionmap.put(actionMapKey, new RowHeaderActionProxy(mainTable, mainActionMap.get(actionMapKey)));
174         
175         // and map the page up too
176         actionMapKey = rowHeaderInputMap.get(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0));
177         rowHeaderActionmap.put(actionMapKey, new RowHeaderActionProxy(mainTable, mainActionMap.get(actionMapKey)));
178     }
179     
180     public TableColumnModel getMainColumnModel()
181     {
182         return mainColumnModel;
183     }
184     
185     public TableColumnModel getRowHeaderColumnModel()
186     {
187         return rowHeaderColumnModel;
188     }
189 }
190 
191 

Notes