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