diff options
Diffstat (limited to 'libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java')
-rw-r--r-- | libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java | 785 |
1 files changed, 735 insertions, 50 deletions
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java index 778743619bc..4559937eb69 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java @@ -40,26 +40,37 @@ package javax.swing.plaf.basic; import java.awt.Color; import java.awt.Component; +import java.awt.ComponentOrientation; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; +import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; import javax.swing.BorderFactory; import javax.swing.CellRendererPane; +import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.border.Border; +import javax.swing.event.ChangeEvent; import javax.swing.event.MouseInputListener; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.TableUI; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; @@ -85,6 +96,9 @@ public class BasicTableUI /** The cell border for selected/highlighted cells. */ Border highlightCellBorder; + /** The action bound to KeyStrokes. */ + TableAction action; + class FocusHandler implements FocusListener { public void focusGained(FocusEvent e) @@ -95,54 +109,37 @@ public class BasicTableUI } } - class KeyHandler implements KeyListener - { - public void keyPressed(KeyEvent e) - { - } - public void keyReleased(KeyEvent e) - { - } - public void keyTyped(KeyEvent e) - { - } - } - class MouseInputHandler implements MouseInputListener { Point begin, curr; private void updateSelection(boolean controlPressed) { - if (table.getRowSelectionAllowed()) + // Update the rows + int lo_row = table.rowAtPoint(begin); + int hi_row = table.rowAtPoint(curr); + ListSelectionModel rowModel = table.getSelectionModel(); + if (lo_row != -1 && hi_row != -1) { - int lo_row = table.rowAtPoint(begin); - int hi_row = table.rowAtPoint(curr); - ListSelectionModel rowModel = table.getSelectionModel(); - if (lo_row != -1 && hi_row != -1) - { - if (controlPressed && rowModel.getSelectionMode() - != ListSelectionModel.SINGLE_SELECTION) - rowModel.addSelectionInterval(lo_row, hi_row); - else - rowModel.setSelectionInterval(lo_row, hi_row); - } + if (controlPressed && rowModel.getSelectionMode() + != ListSelectionModel.SINGLE_SELECTION) + rowModel.addSelectionInterval(lo_row, hi_row); + else + rowModel.setSelectionInterval(lo_row, hi_row); } - - if (table.getColumnSelectionAllowed()) + + // Update the columns + int lo_col = table.columnAtPoint(begin); + int hi_col = table.columnAtPoint(curr); + ListSelectionModel colModel = table.getColumnModel(). + getSelectionModel(); + if (lo_col != -1 && hi_col != -1) { - int lo_col = table.columnAtPoint(begin); - int hi_col = table.columnAtPoint(curr); - ListSelectionModel colModel = table.getColumnModel(). - getSelectionModel(); - if (lo_col != -1 && hi_col != -1) - { - if (controlPressed && colModel.getSelectionMode() != - ListSelectionModel.SINGLE_SELECTION) - colModel.addSelectionInterval(lo_col, hi_col); - else - colModel.setSelectionInterval(lo_col, hi_col); - } + if (controlPressed && colModel.getSelectionMode() != + ListSelectionModel.SINGLE_SELECTION) + colModel.addSelectionInterval(lo_col, hi_col); + else + colModel.setSelectionInterval(lo_col, hi_col); } } @@ -165,6 +162,11 @@ public class BasicTableUI } public void mousePressed(MouseEvent e) { + ListSelectionModel rowModel = table.getSelectionModel(); + ListSelectionModel colModel = table.getColumnModel().getSelectionModel(); + int rowLead = rowModel.getLeadSelectionIndex(); + int colLead = colModel.getLeadSelectionIndex(); + begin = new Point(e.getX(), e.getY()); curr = new Point(e.getX(), e.getY()); //if control is pressed and the cell is already selected, deselect it @@ -180,7 +182,12 @@ public class BasicTableUI } else updateSelection(e.isControlDown()); - + + // If we were editing, but the moved to another cell, stop editing + if (rowLead != rowModel.getLeadSelectionIndex() || + colLead != colModel.getLeadSelectionIndex()) + if (table.isEditing()) + table.editingStopped(new ChangeEvent(e)); } public void mouseReleased(MouseEvent e) { @@ -193,23 +200,50 @@ public class BasicTableUI { return new FocusHandler(); } - protected KeyListener createKeyListener() - { - return new KeyHandler(); - } + protected MouseInputListener createMouseInputListener() { return new MouseInputHandler(); } + /** + * Return the maximum size of the table. The maximum height is the row + * height times the number of rows. The maximum width is the sum of + * the maximum widths of each column. + * + * @param comp the component whose maximum size is being queried, + * this is ignored. + * @return a Dimension object representing the maximum size of the table, + * or null if the table has no elements. + */ public Dimension getMaximumSize(JComponent comp) { - return getPreferredSize(comp); + int maxTotalColumnWidth = 0; + for (int i = 0; i < table.getColumnCount(); i++) + maxTotalColumnWidth += table.getColumnModel().getColumn(i).getMaxWidth(); + if (maxTotalColumnWidth == 0 || table.getRowCount() == 0) + return null; + return new Dimension(maxTotalColumnWidth, table.getRowCount()*table.getRowHeight()); } + /** + * Return the minimum size of the table. The minimum height is the row + * height times the number of rows. The minimum width is the sum of + * the minimum widths of each column. + * + * @param comp the component whose minimum size is being queried, + * this is ignored. + * @return a Dimension object representing the minimum size of the table, + * or null if the table has no elements. + */ public Dimension getMinimumSize(JComponent comp) { - return getPreferredSize(comp); + int minTotalColumnWidth = 0; + for (int i = 0; i < table.getColumnCount(); i++) + minTotalColumnWidth += table.getColumnModel().getColumn(i).getMinWidth(); + if (minTotalColumnWidth == 0 || table.getRowCount() == 0) + return null; + return new Dimension(minTotalColumnWidth, table.getRowCount()*table.getRowHeight()); } public Dimension getPreferredSize(JComponent comp) @@ -233,8 +267,657 @@ public class BasicTableUI highlightCellBorder = defaults.getBorder("Table.focusCellHighlightBorder"); cellBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1); } + + private int convertModifiers(int mod) + { + if ((mod & KeyEvent.SHIFT_DOWN_MASK) != 0) + { + mod |= KeyEvent.SHIFT_MASK; + mod &= ~KeyEvent.SHIFT_DOWN_MASK; + } + if ((mod & KeyEvent.CTRL_DOWN_MASK) != 0) + { + mod |= KeyEvent.CTRL_MASK; + mod &= ~KeyEvent.CTRL_DOWN_MASK; + } + if ((mod & KeyEvent.META_DOWN_MASK) != 0) + { + mod |= KeyEvent.META_MASK; + mod &= ~KeyEvent.META_DOWN_MASK; + } + if ((mod & KeyEvent.ALT_DOWN_MASK) != 0) + { + mod |= KeyEvent.ALT_MASK; + mod &= ~KeyEvent.ALT_DOWN_MASK; + } + if ((mod & KeyEvent.ALT_GRAPH_DOWN_MASK) != 0) + { + mod |= KeyEvent.ALT_GRAPH_MASK; + mod &= ~KeyEvent.ALT_GRAPH_DOWN_MASK; + } + return mod; + } + protected void installKeyboardActions() { + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + InputMap ancestorMap = (InputMap)defaults.get("Table.ancestorInputMap"); + InputMapUIResource parentInputMap = new InputMapUIResource(); + // FIXME: The JDK uses a LazyActionMap for parentActionMap + ActionMap parentActionMap = new ActionMap(); + action = new TableAction(); + Object keys[] = ancestorMap.allKeys(); + // Register key bindings in the UI InputMap-ActionMap pair + // Note that we register key bindings with both the old and new modifier + // masks: InputEvent.SHIFT_MASK and InputEvent.SHIFT_DOWN_MASK and so on. + for (int i = 0; i < keys.length; i++) + { + parentInputMap.put(KeyStroke.getKeyStroke + (((KeyStroke)keys[i]).getKeyCode(), convertModifiers + (((KeyStroke)keys[i]).getModifiers())), + (String)ancestorMap.get((KeyStroke)keys[i])); + + parentInputMap.put(KeyStroke.getKeyStroke + (((KeyStroke)keys[i]).getKeyCode(), + ((KeyStroke)keys[i]).getModifiers()), + (String)ancestorMap.get((KeyStroke)keys[i])); + + parentActionMap.put + ((String)ancestorMap.get((KeyStroke)keys[i]), new ActionListenerProxy + (action, (String)ancestorMap.get((KeyStroke)keys[i]))); + + } + // Set the UI InputMap-ActionMap pair to be the parents of the + // JTable's InputMap-ActionMap pair + parentInputMap.setParent + (table.getInputMap + (JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).getParent()); + parentActionMap.setParent(table.getActionMap().getParent()); + table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT). + setParent(parentInputMap); + table.getActionMap().setParent(parentActionMap); + } + + /** + * This class is used to mimmic the behaviour of the JDK when registering + * keyboard actions. It is the same as the private class used in JComponent + * for the same reason. This class receives an action event and dispatches + * it to the true receiver after altering the actionCommand property of the + * event. + */ + private static class ActionListenerProxy + extends AbstractAction + { + ActionListener target; + String bindingCommandName; + + public ActionListenerProxy(ActionListener li, + String cmd) + { + target = li; + bindingCommandName = cmd; + } + + public void actionPerformed(ActionEvent e) + { + ActionEvent derivedEvent = new ActionEvent(e.getSource(), + e.getID(), + bindingCommandName, + e.getModifiers()); + target.actionPerformed(derivedEvent); + } + } + + /** + * This class implements the actions that we want to happen + * when specific keys are pressed for the JTable. The actionPerformed + * method is called when a key that has been registered for the JTable + * is received. + */ + class TableAction extends AbstractAction + { + /** + * What to do when this action is called. + * + * @param e the ActionEvent that caused this action. + */ + public void actionPerformed (ActionEvent e) + { + ListSelectionModel rowModel = table.getSelectionModel(); + ListSelectionModel colModel = table.getColumnModel().getSelectionModel(); + + int rowLead = rowModel.getLeadSelectionIndex(); + int rowMax = table.getModel().getRowCount() - 1; + + int colLead = colModel.getLeadSelectionIndex(); + int colMax = table.getModel().getColumnCount() - 1; + + if (e.getActionCommand().equals("selectPreviousRowExtendSelection")) + { + rowModel.setLeadSelectionIndex(Math.max(rowLead - 1, 0)); + colModel.setLeadSelectionIndex(colLead); + } + else if (e.getActionCommand().equals("selectLastColumn")) + { + table.clearSelection(); + rowModel.setSelectionInterval(rowLead, rowLead); + colModel.setSelectionInterval(colMax, colMax); + } + else if (e.getActionCommand().equals("startEditing")) + { + if (table.isCellEditable(rowLead, colLead)) + table.editCellAt(rowLead,colLead); + } + else if (e.getActionCommand().equals("selectFirstRowExtendSelection")) + { + rowModel.setLeadSelectionIndex(0); + colModel.setLeadSelectionIndex(colLead); + } + else if (e.getActionCommand().equals("selectFirstColumn")) + { + rowModel.setSelectionInterval(rowLead, rowLead); + colModel.setSelectionInterval(0, 0); + } + else if (e.getActionCommand().equals("selectFirstColumnExtendSelection")) + { + colModel.setLeadSelectionIndex(0); + rowModel.setLeadSelectionIndex(rowLead); + } + else if (e.getActionCommand().equals("selectLastRow")) + { + rowModel.setSelectionInterval(rowMax,rowMax); + colModel.setSelectionInterval(colLead, colLead); + } + else if (e.getActionCommand().equals("selectNextRowExtendSelection")) + { + rowModel.setLeadSelectionIndex(Math.min(rowLead + 1, rowMax)); + colModel.setLeadSelectionIndex(colLead); + } + else if (e.getActionCommand().equals("selectFirstRow")) + { + rowModel.setSelectionInterval(0,0); + colModel.setSelectionInterval(colLead, colLead); + } + else if (e.getActionCommand().equals("selectNextColumnExtendSelection")) + { + colModel.setLeadSelectionIndex(Math.min(colLead + 1, colMax)); + rowModel.setLeadSelectionIndex(rowLead); + } + else if (e.getActionCommand().equals("selectLastColumnExtendSelection")) + { + colModel.setLeadSelectionIndex(colMax); + rowModel.setLeadSelectionIndex(rowLead); + } + else if (e.getActionCommand().equals("selectPreviousColumnExtendSelection")) + { + colModel.setLeadSelectionIndex(Math.max(colLead - 1, 0)); + rowModel.setLeadSelectionIndex(rowLead); + } + else if (e.getActionCommand().equals("selectNextRow")) + { + rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax), + Math.min(rowLead + 1, rowMax)); + colModel.setSelectionInterval(colLead,colLead); + } + else if (e.getActionCommand().equals("scrollUpExtendSelection")) + { + int target; + if (rowLead == getFirstVisibleRowIndex()) + target = Math.max + (0, rowLead - (getLastVisibleRowIndex() - + getFirstVisibleRowIndex() + 1)); + else + target = getFirstVisibleRowIndex(); + + rowModel.setLeadSelectionIndex(target); + colModel.setLeadSelectionIndex(colLead); + } + else if (e.getActionCommand().equals("selectPreviousRow")) + { + rowModel.setSelectionInterval(Math.max(rowLead - 1, 0), + Math.max(rowLead - 1, 0)); + colModel.setSelectionInterval(colLead,colLead); + } + else if (e.getActionCommand().equals("scrollRightChangeSelection")) + { + int target; + if (colLead == getLastVisibleColumnIndex()) + target = Math.min + (colMax, colLead + (getLastVisibleColumnIndex() - + getFirstVisibleColumnIndex() + 1)); + else + target = getLastVisibleColumnIndex(); + + colModel.setSelectionInterval(target, target); + rowModel.setSelectionInterval(rowLead, rowLead); + } + else if (e.getActionCommand().equals("selectPreviousColumn")) + { + rowModel.setSelectionInterval(rowLead,rowLead); + colModel.setSelectionInterval(Math.max(colLead - 1, 0), + Math.max(colLead - 1, 0)); + } + else if (e.getActionCommand().equals("scrollLeftChangeSelection")) + { + int target; + if (colLead == getFirstVisibleColumnIndex()) + target = Math.max + (0, colLead - (getLastVisibleColumnIndex() - + getFirstVisibleColumnIndex() + 1)); + else + target = getFirstVisibleColumnIndex(); + + colModel.setSelectionInterval(target, target); + rowModel.setSelectionInterval(rowLead, rowLead); + } + else if (e.getActionCommand().equals("clearSelection")) + { + table.clearSelection(); + } + else if (e.getActionCommand().equals("cancel")) + { + // FIXME: implement other parts of "cancel" like undo-ing last + // selection. Right now it just calls editingCancelled if + // we're currently editing. + if (table.isEditing()) + table.editingCanceled(new ChangeEvent("cancel")); + } + else if (e.getActionCommand().equals("selectNextRowCell") + || e.getActionCommand().equals("selectPreviousRowCell") + || e.getActionCommand().equals("selectNextColumnCell") + || e.getActionCommand().equals("selectPreviousColumnCell")) + { + // If nothing is selected, select the first cell in the table + if (table.getSelectedRowCount() == 0 && + table.getSelectedColumnCount() == 0) + { + rowModel.setSelectionInterval(0, 0); + colModel.setSelectionInterval(0, 0); + return; + } + + // If the lead selection index isn't selected (ie a remove operation + // happened, then set the lead to the first selected cell in the + // table + if (!table.isCellSelected(rowLead, colLead)) + { + rowModel.addSelectionInterval(rowModel.getMinSelectionIndex(), + rowModel.getMinSelectionIndex()); + colModel.addSelectionInterval(colModel.getMinSelectionIndex(), + colModel.getMinSelectionIndex()); + return; + } + + // multRowsSelected and multColsSelected tell us if multiple rows or + // columns are selected, respectively + boolean multRowsSelected, multColsSelected; + multRowsSelected = table.getSelectedRowCount() > 1 && + table.getRowSelectionAllowed(); + + multColsSelected = table.getSelectedColumnCount() > 1 && + table.getColumnSelectionAllowed(); + + // If there is just one selection, select the next cell, and wrap + // when you get to the edges of the table. + if (!multColsSelected && !multRowsSelected) + { + if (e.getActionCommand().indexOf("Column") != -1) + advanceSingleSelection(colModel, colMax, rowModel, rowMax, + (e.getActionCommand().equals + ("selectPreviousColumnCell"))); + else + advanceSingleSelection(rowModel, rowMax, colModel, colMax, + (e.getActionCommand().equals + ("selectPreviousRowCell"))); + return; + } + + + // rowMinSelected and rowMaxSelected are the minimum and maximum + // values respectively of selected cells in the row selection model + // Similarly for colMinSelected and colMaxSelected. + int rowMaxSelected = table.getRowSelectionAllowed() ? + rowModel.getMaxSelectionIndex() : table.getModel().getRowCount() - 1; + int rowMinSelected = table.getRowSelectionAllowed() ? + rowModel.getMinSelectionIndex() : 0; + int colMaxSelected = table.getColumnSelectionAllowed() ? + colModel.getMaxSelectionIndex() : + table.getModel().getColumnCount() - 1; + int colMinSelected = table.getColumnSelectionAllowed() ? + colModel.getMinSelectionIndex() : 0; + + // If there are multiple rows and columns selected, select the next + // cell and wrap at the edges of the selection. + if (e.getActionCommand().indexOf("Column") != -1) + advanceMultipleSelection(colModel, colMinSelected, colMaxSelected, + rowModel, rowMinSelected, rowMaxSelected, + (e.getActionCommand().equals + ("selectPreviousColumnCell")), true); + + else + advanceMultipleSelection(rowModel, rowMinSelected, rowMaxSelected, + colModel, colMinSelected, colMaxSelected, + (e.getActionCommand().equals + ("selectPreviousRowCell")), false); + } + else if (e.getActionCommand().equals("selectNextColumn")) + { + rowModel.setSelectionInterval(rowLead,rowLead); + colModel.setSelectionInterval(Math.min(colLead + 1, colMax), + Math.min(colLead + 1, colMax)); + } + else if (e.getActionCommand().equals("scrollLeftExtendSelection")) + { + int target; + if (colLead == getFirstVisibleColumnIndex()) + target = Math.max + (0, colLead - (getLastVisibleColumnIndex() - + getFirstVisibleColumnIndex() + 1)); + else + target = getFirstVisibleColumnIndex(); + + colModel.setLeadSelectionIndex(target); + rowModel.setLeadSelectionIndex(rowLead); + } + else if (e.getActionCommand().equals("scrollDownChangeSelection")) + { + int target; + if (rowLead == getLastVisibleRowIndex()) + target = Math.min + (rowMax, rowLead + (getLastVisibleRowIndex() - + getFirstVisibleRowIndex() + 1)); + else + target = getLastVisibleRowIndex(); + + rowModel.setSelectionInterval(target, target); + colModel.setSelectionInterval(colLead, colLead); + } + else if (e.getActionCommand().equals("scrollRightExtendSelection")) + { + int target; + if (colLead == getLastVisibleColumnIndex()) + target = Math.min + (colMax, colLead + (getLastVisibleColumnIndex() - + getFirstVisibleColumnIndex() + 1)); + else + target = getLastVisibleColumnIndex(); + + colModel.setLeadSelectionIndex(target); + rowModel.setLeadSelectionIndex(rowLead); + } + else if (e.getActionCommand().equals("selectAll")) + { + table.selectAll(); + } + else if (e.getActionCommand().equals("selectLastRowExtendSelection")) + { + rowModel.setLeadSelectionIndex(rowMax); + colModel.setLeadSelectionIndex(colLead); + } + else if (e.getActionCommand().equals("scrollDownExtendSelection")) + { + int target; + if (rowLead == getLastVisibleRowIndex()) + target = Math.min + (rowMax, rowLead + (getLastVisibleRowIndex() - + getFirstVisibleRowIndex() + 1)); + else + target = getLastVisibleRowIndex(); + + rowModel.setLeadSelectionIndex(target); + colModel.setLeadSelectionIndex(colLead); + } + else if (e.getActionCommand().equals("scrollUpChangeSelection")) + { + int target; + if (rowLead == getFirstVisibleRowIndex()) + target = Math.max + (0, rowLead - (getLastVisibleRowIndex() - + getFirstVisibleRowIndex() + 1)); + else + target = getFirstVisibleRowIndex(); + + rowModel.setSelectionInterval(target, target); + colModel.setSelectionInterval(colLead, colLead); + } + else + { + // If we're here that means we bound this TableAction class + // to a keyboard input but we either want to ignore that input + // or we just haven't implemented its action yet. + } + + if (table.isEditing() && e.getActionCommand() != "startEditing") + table.editingCanceled(new ChangeEvent("update")); + table.repaint(); + + table.scrollRectToVisible + (table.getCellRect(rowModel.getLeadSelectionIndex(), + colModel.getLeadSelectionIndex(), false)); + } + + int getFirstVisibleColumnIndex() + { + ComponentOrientation or = table.getComponentOrientation(); + Rectangle r = table.getVisibleRect(); + if (!or.isLeftToRight()) + r.translate((int) r.getWidth() - 1, 0); + return table.columnAtPoint(r.getLocation()); + } + + /** + * Returns the column index of the last visible column. + * + */ + int getLastVisibleColumnIndex() + { + ComponentOrientation or = table.getComponentOrientation(); + Rectangle r = table.getVisibleRect(); + if (or.isLeftToRight()) + r.translate((int) r.getWidth() - 1, 0); + return table.columnAtPoint(r.getLocation()); + } + + /** + * Returns the row index of the first visible row. + * + */ + int getFirstVisibleRowIndex() + { + ComponentOrientation or = table.getComponentOrientation(); + Rectangle r = table.getVisibleRect(); + if (!or.isLeftToRight()) + r.translate((int) r.getWidth() - 1, 0); + return table.rowAtPoint(r.getLocation()); + } + + /** + * Returns the row index of the last visible row. + * + */ + int getLastVisibleRowIndex() + { + ComponentOrientation or = table.getComponentOrientation(); + Rectangle r = table.getVisibleRect(); + r.translate(0, (int) r.getHeight() - 1); + if (or.isLeftToRight()) + r.translate((int) r.getWidth() - 1, 0); + // The next if makes sure that we don't return -1 simply because + // there is white space at the bottom of the table (ie, the display + // area is larger than the table) + if (table.rowAtPoint(r.getLocation()) == -1) + { + if (getFirstVisibleRowIndex() == -1) + return -1; + else + return table.getModel().getRowCount() - 1; + } + return table.rowAtPoint(r.getLocation()); + } + + /** + * A helper method for the key bindings. Used because the actions + * for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar. + * + * Selects the next (previous if SHIFT pressed) column for TAB, or row for + * ENTER from within the currently selected cells. + * + * @param firstModel the ListSelectionModel for columns (TAB) or + * rows (ENTER) + * @param firstMin the first selected index in firstModel + * @param firstMax the last selected index in firstModel + * @param secondModel the ListSelectionModel for rows (TAB) or + * columns (ENTER) + * @param secondMin the first selected index in secondModel + * @param secondMax the last selected index in secondModel + * @param reverse true if shift was held for the event + * @param eventIsTab true if TAB was pressed, false if ENTER pressed + */ + void advanceMultipleSelection (ListSelectionModel firstModel, int firstMin, + int firstMax, ListSelectionModel secondModel, + int secondMin, int secondMax, boolean reverse, + boolean eventIsTab) + { + // If eventIsTab, all the "firsts" correspond to columns, otherwise, to rows + // "seconds" correspond to the opposite + int firstLead = firstModel.getLeadSelectionIndex(); + int secondLead = secondModel.getLeadSelectionIndex(); + int numFirsts = eventIsTab ? + table.getModel().getColumnCount() : table.getModel().getRowCount(); + int numSeconds = eventIsTab ? + table.getModel().getRowCount() : table.getModel().getColumnCount(); + + // check if we have to wrap the "firsts" around, going to the other side + if ((firstLead == firstMax && !reverse) || + (reverse && firstLead == firstMin)) + { + firstModel.addSelectionInterval(reverse ? firstMax : firstMin, + reverse ? firstMax : firstMin); + + // check if we have to wrap the "seconds" + if ((secondLead == secondMax && !reverse) || + (reverse && secondLead == secondMin)) + secondModel.addSelectionInterval(reverse ? secondMax : secondMin, + reverse ? secondMax : secondMin); + + // if we're not wrapping the seconds, we have to find out where we + // are within the secondModel and advance to the next cell (or + // go back to the previous cell if reverse == true) + else + { + int[] secondsSelected; + if (eventIsTab && table.getRowSelectionAllowed() || + !eventIsTab && table.getColumnSelectionAllowed()) + secondsSelected = eventIsTab ? + table.getSelectedRows() : table.getSelectedColumns(); + else + { + // if row selection is not allowed, then the entire column gets + // selected when you click on it, so consider ALL rows selected + secondsSelected = new int[numSeconds]; + for (int i = 0; i < numSeconds; i++) + secondsSelected[i] = i; + } + + // and now find the "next" index within the model + int secondIndex = reverse ? secondsSelected.length - 1 : 0; + if (!reverse) + while (secondsSelected[secondIndex] <= secondLead) + secondIndex++; + else + while (secondsSelected[secondIndex] >= secondLead) + secondIndex--; + + // and select it - updating the lead selection index + secondModel.addSelectionInterval(secondsSelected[secondIndex], + secondsSelected[secondIndex]); + } + } + // We didn't have to wrap the firsts, so just find the "next" first + // and select it, we don't have to change "seconds" + else + { + int[] firstsSelected; + if (eventIsTab && table.getColumnSelectionAllowed() || + !eventIsTab && table.getRowSelectionAllowed()) + firstsSelected = eventIsTab ? + table.getSelectedColumns() : table.getSelectedRows(); + else + { + // if selection not allowed, consider ALL firsts to be selected + firstsSelected = new int[numFirsts]; + for (int i = 0; i < numFirsts; i++) + firstsSelected[i] = i; + } + int firstIndex = reverse ? firstsSelected.length - 1 : 0; + if (!reverse) + while (firstsSelected[firstIndex] <= firstLead) + firstIndex++; + else + while (firstsSelected[firstIndex] >= firstLead) + firstIndex--; + firstModel.addSelectionInterval(firstsSelected[firstIndex], + firstsSelected[firstIndex]); + secondModel.addSelectionInterval(secondLead, secondLead); + } + } + + /** + * A helper method for the key bindings. Used because the actions + * for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar. + * + * Selects the next (previous if SHIFT pressed) column (TAB) or row (ENTER) + * in the table, changing the current selection. All cells in the table + * are eligible, not just the ones that are currently selected. + * @param firstModel the ListSelectionModel for columns (TAB) or rows + * (ENTER) + * @param firstMax the last index in firstModel + * @param secondModel the ListSelectionModel for rows (TAB) or columns + * (ENTER) + * @param secondMax the last index in secondModel + * @param reverse true if SHIFT was pressed for the event + */ + + void advanceSingleSelection (ListSelectionModel firstModel, int firstMax, + ListSelectionModel secondModel, int secondMax, + boolean reverse) + { + // for TABs, "first" corresponds to columns and "seconds" to rows. + // the opposite is true for ENTERs + int firstLead = firstModel.getLeadSelectionIndex(); + int secondLead = secondModel.getLeadSelectionIndex(); + + // if we are going backwards subtract 2 because we later add 1 + // for a net change of -1 + if (reverse && (firstLead == 0)) + { + // check if we have to wrap around + if (secondLead == 0) + secondLead += secondMax + 1; + secondLead -= 2; + } + + // do we have to wrap the "seconds"? + if (reverse && (firstLead == 0) || !reverse && (firstLead == firstMax)) + secondModel.setSelectionInterval((secondLead + 1)%(secondMax + 1), + (secondLead + 1)%(secondMax + 1)); + // if not, just reselect the current lead + else + secondModel.setSelectionInterval(secondLead, secondLead); + + // if we are going backwards, subtract 2 because we add 1 later + // for net change of -1 + if (reverse) + { + // check for wraparound + if (firstLead == 0) + firstLead += firstMax + 1; + firstLead -= 2; + } + // select the next "first" + firstModel.setSelectionInterval ((firstLead + 1)%(firstMax + 1), + (firstLead + 1)%(firstMax + 1)); + } } protected void installListeners() @@ -281,7 +964,6 @@ public class BasicTableUI { table = (JTable)comp; focusListener = createFocusListener(); - keyListener = createKeyListener(); mouseInputListener = createMouseInputListener(); installDefaults(); installKeyboardActions(); @@ -332,14 +1014,19 @@ public class BasicTableUI gfx.translate(x, y); comp.setBounds(new Rectangle(0, 0, width, height)); // Set correct border on cell renderer. + // Only the lead selection cell gets a border if (comp instanceof JComponent) { - if (table.isCellSelected(r, c)) + if (table.getSelectionModel().getLeadSelectionIndex() == r + && table.getColumnModel().getSelectionModel(). + getLeadSelectionIndex() == c) ((JComponent) comp).setBorder(highlightCellBorder); else ((JComponent) comp).setBorder(cellBorder); } comp.paint(gfx); + if (comp instanceof JTextField) + ((JTextField)comp).getCaret().paint(gfx); gfx.translate(-x, -y); } y += height; @@ -392,7 +1079,5 @@ public class BasicTableUI } gfx.setColor(save); } - } - } |