001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.widgets; 003 004import java.awt.KeyboardFocusManager; 005import java.awt.event.ActionEvent; 006import java.awt.event.KeyEvent; 007 008import javax.swing.AbstractAction; 009import javax.swing.JComponent; 010import javax.swing.JTable; 011import javax.swing.KeyStroke; 012import javax.swing.ListSelectionModel; 013import javax.swing.table.TableColumnModel; 014import javax.swing.table.TableModel; 015 016/** 017 * Generic table offering custom cell navigation features. 018 * @since 9497 019 */ 020public abstract class JosmTable extends JTable { 021 022 private int colEnd; 023 024 protected SelectNextColumnCellAction selectNextColumnCellAction; 025 protected SelectPreviousColumnCellAction selectPreviousColumnCellAction; 026 027 protected JosmTable(TableModel dm, TableColumnModel cm) { 028 this(dm, cm, null); 029 } 030 031 protected JosmTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) { 032 super(dm, cm, sm); 033 } 034 035 protected void installCustomNavigation(int colEnd) { 036 // make ENTER behave like TAB 037 getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( 038 KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false), "selectNextColumnCell"); 039 040 // install custom navigation actions 041 this.colEnd = colEnd; 042 selectNextColumnCellAction = new SelectNextColumnCellAction(); 043 selectPreviousColumnCellAction = new SelectPreviousColumnCellAction(); 044 getActionMap().put("selectNextColumnCell", selectNextColumnCellAction); 045 getActionMap().put("selectPreviousColumnCell", selectPreviousColumnCellAction); 046 } 047 048 /** 049 * Action to be run when the user navigates to the next cell in the table, for instance by 050 * pressing TAB or ENTER. The action alters the standard navigation path from cell to cell: <ul> 051 * <li>it jumps over cells in the first column</li> 052 * <li>it automatically add a new empty row when the user leaves the last cell in the table</li></ul> 053 */ 054 protected class SelectNextColumnCellAction extends AbstractAction { 055 @Override 056 public void actionPerformed(ActionEvent e) { 057 int col = getSelectedColumn(); 058 int row = getSelectedRow(); 059 if (getCellEditor() != null) { 060 getCellEditor().stopCellEditing(); 061 } 062 063 if (col == colEnd && row < getRowCount() - 1) { 064 row++; 065 } else if (row < getRowCount() - 1) { 066 col = colEnd; 067 row++; 068 } else { 069 // go to next component, no more rows in this table 070 KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); 071 manager.focusNextComponent(); 072 return; 073 } 074 changeSelection(row, col, false, false); 075 if (editCellAt(getSelectedRow(), getSelectedColumn())) { 076 getEditorComponent().requestFocusInWindow(); 077 } 078 } 079 } 080 081 /** 082 * Action to be run when the user navigates to the previous cell in the table, for instance by 083 * pressing Shift-TAB 084 */ 085 protected class SelectPreviousColumnCellAction extends AbstractAction { 086 087 @Override 088 public void actionPerformed(ActionEvent e) { 089 int col = getSelectedColumn(); 090 int row = getSelectedRow(); 091 if (getCellEditor() != null) { 092 getCellEditor().stopCellEditing(); 093 } 094 095 if (col <= 0 && row <= 0) { 096 // change nothing 097 } else if (row > 0) { 098 col = colEnd; 099 row--; 100 } 101 changeSelection(row, col, false, false); 102 if (editCellAt(getSelectedRow(), getSelectedColumn())) { 103 getEditorComponent().requestFocusInWindow(); 104 } 105 } 106 } 107}