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