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}