2

I want to use Ctrl-F6 to switch between a JTable and an associated JTree. Ctrl-F6 however starts the JTable's cell editor. JTable only has one default key listener: a javax.swing.ToolTipManager$AccessibilityKeyListener... but removing this didn't solve the problem. So I tried to find out whether this was a key binding and where it might reside... so I wrote this simple function (in Jython but should be perfectly intelligible to Java-ists):

def print_comp_maps( comp, name ):    
    # list all task tree's system-set hotkeys
    # NB respective values here: 0, 1, 2
    input_map_conditions = [ JComponent.WHEN_FOCUSED, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, JComponent.WHEN_IN_FOCUSED_WINDOW ]
    input_map_names = [ "focused", "ancestor", "in focused window" ]

    print( "=== comp: %s" % name )
    for condition in input_map_conditions:
        print( "  === condition: %s" % input_map_names[ condition ])
        i_map = comp.getInputMap( condition )
        depth = 0
        while i_map:
            depth += 1
            print( "  %s=== imap %s:" % ( "  " * depth, i_map ))
            keys = i_map.keys()
            if keys:
                for keystroke in keys:
                    print( "  %s=== keystroke %s value %s" % ( "  " * ( depth + 1 ), keystroke, i_map.get( keystroke ) ) )
            i_map = i_map.parent

The results for the JTable are:

=== comp: date_table
  === condition: focused
    === imap javax.swing.InputMap@1a8a84f:
  === condition: ancestor
    === imap javax.swing.InputMap@1c1edd9:
      === imap javax.swing.plaf.InputMapUIResource@e674b9:
        === keystroke ctrl pressed DOWN value selectNextRowChangeLead
        === keystroke shift pressed UP value selectPreviousRowExtendSelection
        === keystroke shift pressed INSERT value paste
        === keystroke ctrl pressed RIGHT value selectNextColumnChangeLead
        === keystroke shift ctrl pressed LEFT value selectPreviousColumnExtendSe
lection
        === keystroke shift pressed KP_UP value selectPreviousRowExtendSelection
        === keystroke pressed DOWN value selectNextRow
        === keystroke ctrl pressed UP value selectPreviousRowChangeLead
        === keystroke ctrl pressed LEFT value selectPreviousColumnChangeLead
        === keystroke pressed CUT value cut
        === keystroke pressed END value selectLastColumn
        === keystroke shift pressed PAGE_UP value scrollUpExtendSelection
        === keystroke pressed KP_UP value selectPreviousRow
        === keystroke shift ctrl pressed UP value selectPreviousRowExtendSelecti
on
        === keystroke ctrl pressed HOME value selectFirstRow
        === keystroke ctrl pressed INSERT value copy
        === keystroke shift pressed LEFT value selectPreviousColumnExtendSelecti
on
        === keystroke ctrl pressed END value selectLastRow
        === keystroke ctrl pressed PAGE_DOWN value scrollRightChangeSelection
        === keystroke shift ctrl pressed RIGHT value selectNextColumnExtendSelec
tion
        === keystroke pressed LEFT value selectPreviousColumn
        === keystroke ctrl pressed PAGE_UP value scrollLeftChangeSelection
        === keystroke pressed KP_LEFT value selectPreviousColumn
        === keystroke shift pressed KP_RIGHT value selectNextColumnExtendSelecti
on
        === keystroke pressed SPACE value addToSelection
        === keystroke ctrl pressed SPACE value toggleAndAnchor
        === keystroke shift pressed SPACE value extendTo
        === keystroke shift ctrl pressed SPACE value moveSelectionTo
        === keystroke shift ctrl pressed DOWN value selectNextRowExtendSelection
        === keystroke ctrl pressed BACK_SLASH value clearSelection
        === keystroke pressed ESCAPE value cancel
        === keystroke shift pressed DELETE value cut
        === keystroke shift pressed HOME value selectFirstColumnExtendSelection
        === keystroke pressed ENTER value selectNextRowCell
        === keystroke shift pressed ENTER value selectPreviousRowCell
        === keystroke pressed F8 value focusHeader
        === keystroke pressed RIGHT value selectNextColumn
        === keystroke shift ctrl pressed PAGE_UP value scrollLeftExtendSelection
        === keystroke shift pressed DOWN value selectNextRowExtendSelection
        === keystroke shift ctrl pressed KP_UP value selectPreviousRowExtendSele
ction
        === keystroke pressed PAGE_DOWN value scrollDownChangeSelection
        === keystroke shift pressed KP_LEFT value selectPreviousColumnExtendSele
ction
        === keystroke ctrl pressed X value cut
        === keystroke shift ctrl pressed PAGE_DOWN value scrollRightExtendSelect
ion
        === keystroke ctrl pressed SLASH value selectAll
        === keystroke ctrl pressed C value copy
        === keystroke ctrl pressed KP_RIGHT value selectNextColumnChangeLead
        === keystroke shift pressed END value selectLastColumnExtendSelection
        === keystroke shift ctrl pressed KP_DOWN value selectNextRowExtendSelect
ion
        === keystroke shift pressed TAB value selectPreviousColumnCell
        === keystroke ctrl pressed KP_LEFT value selectPreviousColumnChangeLead
        === keystroke pressed HOME value selectFirstColumn
        === keystroke ctrl pressed V value paste
        === keystroke pressed KP_DOWN value selectNextRow
        === keystroke ctrl pressed KP_DOWN value selectNextRowChangeLead
        === keystroke shift pressed RIGHT value selectNextColumnExtendSelection
        === keystroke ctrl pressed A value selectAll
        === keystroke shift ctrl pressed END value selectLastRowExtendSelection
        === keystroke pressed COPY value copy
        === keystroke ctrl pressed KP_UP value selectPreviousRowChangeLead
        === keystroke shift ctrl pressed KP_LEFT value selectPreviousColumnExten
dSelection
        === keystroke shift pressed KP_DOWN value selectNextRowExtendSelection
        === keystroke pressed TAB value selectNextColumnCell
        === keystroke pressed UP value selectPreviousRow
        === keystroke shift ctrl pressed HOME value selectFirstRowExtendSelectio
n
        === keystroke shift pressed PAGE_DOWN value scrollDownExtendSelection
        === keystroke pressed KP_RIGHT value selectNextColumn
        === keystroke shift ctrl pressed KP_RIGHT value selectNextColumnExtendSe
lection
        === keystroke pressed F2 value startEditing
        === keystroke pressed PAGE_UP value scrollUpChangeSelection
        === keystroke pressed PASTE value paste
  === condition: in focused window
    === imap javax.swing.ComponentInputMap@fa5f28:

... no sign of any F6 or Ctrl-F6 key-binding... has anyone got any idea what's going on?

later

by the way, it's perfectly possible to add a Ctrl-F6 binding to (for example) the JTable's "WHEN ANCESTOR OF ..." InputMap (for example, first generation, i.e. javax.swing.InputMap@1c1edd9 in the above listing). Then add a binding between the Object there and an Action in the ActionMap. Ctrl-F6 then fires the Action. But, as one might expect perhaps, this in no way inhibits the "startEditing" Action. The mystery of mysteries is: where the ferkin is this binding located??? I have explored the JScrollPane (no joy, no surprise) and even the JTextField object submitted to the constructor of the DefaultCellEditor... (no joy, and even less surprise). Stumped. Anyone know a bindings super-geek?

even later

Although Yarik has sorted out the main thing I still have a problem: when the table is realised and focus is then put on a previously selected row programmatically (i.e. not using mouse), F2 does not initially work... it is only after you navigate (e.g. with up/down keys) that you then find that F2 starts editing. SSCCE illustrating problem:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.lang.Runnable;
import javax.swing.*;


public class TableCellEditProb {
    public static void main(String args[]) {
        Runnable show_frame = new Runnable(){
            public void run(){
                JFrame main_frame = new JFrame( "F2 no effect on first realisation!");
                main_frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
                final JTable table = new JTable( 5, 2 );
                table.setPreferredSize( new Dimension( 400, 200 ));
                table.setRowSelectionInterval( 2, 2 );
                table.putClientProperty("JTable.autoStartsEdit", false );      
                JPanel panel = new JPanel( new BorderLayout());
                AbstractAction button_action = new AbstractAction(){
                    public void actionPerformed(ActionEvent e){
                        System.out.println( "button pressed");
                        table.requestFocus();
                    }
                };
                button_action.putValue( AbstractAction.NAME, "Click (or press spacebar) to change focus to table..." );
                JButton button = new JButton( button_action );
                panel.add( table, BorderLayout.WEST );
                panel.add( button, BorderLayout.EAST );
                main_frame.getContentPane().add( panel );
                main_frame.pack();
                button.requestFocus();
                main_frame.setVisible( true );
            }
        };
        java.awt.EventQueue.invokeLater( show_frame );
    }
}
mike rodent
  • 14,126
  • 11
  • 103
  • 157
  • 1
    Many L&Fs bind `F2` to `startEditing`, as shown in the *Key Bindings* article cited [here](http://stackoverflow.com/a/7517027/230513). – trashgod Apr 29 '14 at 19:28
  • umm... I know F2 does... and it is shown in the listing from the input maps if you care to look! But my question is about Ctrl-F6 – mike rodent Apr 29 '14 at 19:30
  • have you tried the solution in this question: http://stackoverflow.com/questions/2019371/swing-setting-a-function-key-f2-as-an-accelerator – Marshall Tigerus Apr 29 '14 at 19:35
  • 1
    I cared enough not only to look but to cite a helpful article; you are welcome. I see that `com.apple.laf.AquaLookAndFeel` binds `F6`, perhaps to avoid a collision. – trashgod Apr 29 '14 at 19:36
  • @trashgod thanx - I assume that it's relevant to say that this is a Windows box, specifically Windows 7... also that I'm not using an Apple L&F. – mike rodent Apr 29 '14 at 22:50
  • how/where do you install the F6/ctrl-f6 for switching between components? As always, a SSCCE in plain java would be helpful :-) – kleopatra Apr 30 '14 at 15:09
  • @kleopatra... you're absolutely right, I started putting one together last night. It's a pretty trivial affair, so please come back in a day or so...! – mike rodent Apr 30 '14 at 15:22
  • it takes you a day or so to put together a _pretty trivial affair_ Please answer my question now (if possible), even without code, might have an idea. – kleopatra Apr 30 '14 at 15:32
  • @kleopatra I stand chastised. But Yarik below has answered it... any key pressed starts editing... unless you set the JTable.autoStartsEdit client property to false. Amazing. Probably also possible (set to true) to filter out non-printing chars should one be so inclined. – mike rodent Apr 30 '14 at 15:43
  • In my case my JTable was inside a JSplitPane which has an input map action for F6. Removing it fixed my F6 table problem. – Anthony Dec 12 '20 at 14:44

1 Answers1

2

The default actions <-> keys are defined by the L&F in the InputMap and ActionMap I believe they are called before any external listener. You can always overwrite it.

Alternatively, you can overwrite processKeyBinding

Here is an example of the code that may do what you need. It intercepts Cntrl-F6 and prints to sysout.

Please see my comment as to why Cntrl-F6 is not in the bindings.

public class PrintTableTest {

public JPanel buildUI() {
    String[] columnNames = {"First Name",
            "Last Name",
            "Sport",
            "# of Years",
            "Vegetarian"};

    Object[][] data = {
            {"Mary", "Campione",
                    "Snowboarding", 5, new JComboBox(new Object[]{Boolean.TRUE, Boolean.FALSE})},
            {"Alison", "Huml",
                    "Rowing", 3, Boolean.FALSE},
            {"Kathy", "Walrath",
                    "Knitting", 2, Boolean.FALSE},
            {"Sharon", "Zakhour",
                    "Speed reading", 20, Boolean.TRUE},
            {"Philip", "Milne",
                    "Pool", 10, Boolean.TRUE
            }
    };


    final JTable table = new JTable(increase(data, 50), columnNames);
    JScrollPane scrollPane = new JScrollPane(table);
    JButton print = new JButton("Print");
    print.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            try {
                table.print();
            } catch (PrinterException e1) {
                e1.printStackTrace();
            }
        }
    });

    table.setDefaultRenderer(TableColumn.class, new TableCellRenderer());
    JPanel panel = new JPanel(new BorderLayout(5, 5));
    panel.add(scrollPane, BorderLayout.CENTER);
    panel.add(print, BorderLayout.SOUTH);
    JTextField textField = new JFormattedTextField();
    JSpinner spinner = new JSpinner(new SpinnerNumberModel());
    spinner.setEditor(textField);
    textField.setEditable(false);
    panel.add(spinner, BorderLayout.NORTH);


    panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F6, InputEvent.CTRL_DOWN_MASK), "doSomething" );
    panel.getActionMap().put("doSomething",new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("TEST OUT");
        }
    });
    table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
    return panel;
}

private Object[][] increase(Object[][] src, int times) {

    Object[][] out = new Object[src.length * times][];
    for (int i = 0; i < times; i++) {
        System.arraycopy(src, 0, out, i * src.length, src.length);
    }
    return out;
}

public JFrame getFrame(String name) {
    JFrame frame = new JFrame(name);
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.setContentPane(buildUI());
    frame.setSize(200, 200);
    frame.pack();

    return frame;

}

public static void main
        (String[] args) {
    SwingUtilities.invokeLater(new Runnable() {

        public void run() {
            PrintTableTest ui = new PrintTableTest();
            JFrame frame = ui.getFrame("TABLE PRINT TEST");
            frame.setVisible(true);
        }
    });
}

private class TableCellRenderer extends DefaultTableCellRenderer {

    /**
     * Returns the default table cell renderer.
     *
     * @param table      the <code>JTable</code>
     * @param value      the value to assign to the cell at <code>[row, column]</code>
     * @param isSelected true if cell is selected
     * @param hasFocus   true if cell has focus
     * @param row        the row of the cell to render
     * @param column     the column of the cell to render
     * @return the default table cell renderer
     */
    public Component getTableCellRendererComponent(JTable table,
                                                   Object value,
                                                   boolean isSelected,
                                                   boolean hasFocus,
                                                   int row,
                                                   int column) {
        if (value instanceof JComboBox) {
            return (Component) value;
        }
        return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

    }
}

}

YaRiK
  • 698
  • 6
  • 13
  • thanks, but you can only overwrite something if you know where to find the InputMap binding the KeyStroke to the Object (the Object then binds to the Action in the ActionMap). My prob is that I can't find the InputMap. This also means I can't delete the binding. – mike rodent Apr 29 '14 at 22:48
  • The defaults should be in the JTable UI delegate so check the source. You can still overwrite JTable.processKeyBundings. If I put a break point to editCellAt() the stack trace shows the path. BTW after debugging the code for a while I can see that Ctrl-F6 has no significance. Any key pressed will trigger editing, it just happanes that these keys do not produce printable chars. Try the same with F7 or F8. So in short, overwrite processKeyBinding, add action and add client property table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE); – YaRiK Apr 30 '14 at 14:59
  • doing so has the side-effect of not starting an edit with _any_ key - fine as long as you can live with it :-) – kleopatra Apr 30 '14 at 15:57
  • @kleopatra F2 still works as it's included in the InputMap list (see above). Also no doubt one could put a KeyListener on the JTable which would start editing with all keys which actually print something. In my opinion that should really be the default behaviour with autoStartsEdit = True... – mike rodent May 01 '14 at 05:52
  • any key _except_ F2, you nitpicker - that's typically my role :-) By design, it's up to the editor to decide whether or not it wants to start(or continue, fwim) an edit on a particular keyStroke, the table can't have much part in it. So you could try to implement its isCellEditable(EventObject) accordingly - not entirely trivial for several reasons (same as the table the editor doesn't know overly much about the editingComponent's internals: a textField has no public api to decide whether or not a given pressed key will result in a valid typed key) – kleopatra May 01 '14 at 08:00
  • another approach is to terminate the edit on focus lost (table.putClientProperty("terminateEditOnFocusLost", true) - then the edit will start on pressed and immediatly will be terminated when the keyBinding for switching to another component is activated – kleopatra May 01 '14 at 08:03
  • Thanks! In fact I'm getting some just-not-quite-right behaviour now, and it should perhaps be borne in mind that I'm an anti-mouser. When I put the focus programmatically (not using mouse) on the table cell, initially even the F2 doesn't work: nothing happens. After I move the selection (up/down key), F2 DOES start to work. I consider you the Swing super expert: any idea what I might do about this? I'm going to put up an SSCCE hopefully... – mike rodent May 01 '14 at 11:31