2

I'm having problems with a JComboBox used as a CellEditor for a JTable. I want after editing the JComboBox and pressing tab to show an OptionsDialog and, if a specific option is selected, the focus to remain on the JComboBox. The problem is that the focus moves to the next cell because of tab and I cannot return it to the JComboBox
Below is one of my test cases:

import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;

public class TestFocus {

    public static void main(String[] args) {

        TestFocus test = new TestFocus();
        test.go();

    }

    public void go() {

        //create the frame
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // create and add a tabbed pane to the frame
        JTabbedPane tabbedPane = new JTabbedPane();
        frame.getContentPane().add(tabbedPane);
        //create a table and add it to a scroll pane in a new tab
        JTable table = new JTable(new DefaultTableModel(new Object[] {"A", "B"}, 5));
        JScrollPane scrollPane = new JScrollPane(table);
        tabbedPane.addTab("test", scrollPane);

        // create a simple JComboBox and set is as table cell editor on column A
        Object[] comboElements = {"aaaaa1", "aaaaaa2", "b"};
        final JComboBox comboBox = new JComboBox(comboElements);
        comboBox.setEditable(true);
        table.getColumn("A").setCellEditor(new DefaultCellEditor(comboBox));

        // add an action listener for when the combobox is edited to display an options dialog
        comboBox.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (e.getActionCommand().equals("comboBoxEdited")) {
                    // display an options pane
                    Object[] options = {"Yes", "No"};
                    System.out.println(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());
                    int response = JOptionPane.showOptionDialog(SwingUtilities.getWindowAncestor(comboBox),
                            "Do you want to return the focus to the ComboBox?",
                            "This is just a test",
                            JOptionPane.YES_NO_OPTION,
                            JOptionPane.QUESTION_MESSAGE,
                            null,
                            options,
                            options[0]);
                    comboBox.requestFocusInWindow();
                    if (response == 0) {
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                comboBox.requestFocusInWindow();
                            }
                        });
                    }
                    System.out.println(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());
                }

            }
        });

        // pack and show frame
        frame.pack();
        frame.setVisible(true);

    }
}
Alex Burdusel
  • 3,015
  • 5
  • 38
  • 49
  • 1
    please for why reason(s) JComboBox as TableCellEditor with JOptionPane created inside from ...., an aside FocusLost by default terminating TableCellEditor – mKorbel Nov 12 '13 at 19:34
  • This is just a test case. I'm using inside a project something similar: when an user fills an entry that does not exist inside the `JComboBox` I'm asking him to confirm the adding. In case they do not want to add and press Cancel I want the focus to return to the JComboBox so they can modify. – Alex Burdusel Nov 12 '13 at 19:38
  • okay, now I understand your second question :-) You need a custom editor, but none that modifies the table, only itself. – kleopatra Nov 14 '13 at 14:11

1 Answers1

2

I'm asking him to confirm the adding

Then you should be creating a custom editor and override the stopCellEditing() method.

Here is an example that makes sure the data entered is exactly 5 characters.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.table.*;

public class TableEdit extends JFrame
{
    TableEdit()
    {
        JTable table = new JTable(5,5);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());

        JScrollPane scrollpane = new JScrollPane(table);
        add(scrollpane);

        //  Use a custom editor

        TableCellEditor fce = new FiveCharacterEditor();
        table.setDefaultEditor(Object.class, fce);

        add(new JTextField(), BorderLayout.NORTH);
    }

    class FiveCharacterEditor extends DefaultCellEditor
    {
        FiveCharacterEditor()
        {
            super( new JTextField() );
        }

        public boolean stopCellEditing()
        {
            JTable table = (JTable)getComponent().getParent();

            try
            {
                String editingValue = (String)getCellEditorValue();

                if(editingValue.length() != 5)
                {
                    JTextField textField = (JTextField)getComponent();
                    textField.setBorder(new LineBorder(Color.red));
                    textField.selectAll();
                    textField.requestFocusInWindow();

                    JOptionPane.showMessageDialog(
                        null,
                        "Please enter string with 5 letters.",
                        "Alert!",JOptionPane.ERROR_MESSAGE);
                    return false;
                }
            }
            catch(ClassCastException exception)
            {
                return false;
            }

            return super.stopCellEditing();
        }

        public Component getTableCellEditorComponent(
            JTable table, Object value, boolean isSelected, int row, int column)
        {
            Component c = super.getTableCellEditorComponent(
                table, value, isSelected, row, column);
            ((JComponent)c).setBorder(new LineBorder(Color.black));

            return c;
        }

    }

    public static void main(String [] args)
    {
        JFrame frame = new TableEdit();
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible(true);
    }
}
camickr
  • 321,443
  • 19
  • 166
  • 288
  • Thanks camickr, So no easier way than extending `DefaultCellEditor`? Any ideea of why `comboBox.requestFocusInWindow();` is not doing anything in my example? I'm trying to understand the logic behind. – Alex Burdusel Nov 12 '13 at 19:49
  • When you tab away from the cell the editing of the cell is stopped so the editor is removed from the table. I don't know if you would be able to invoke editCellAt(...) method of JTable to reinvoke the editor. But this would be a total hack since know you somehow also need to know which cell was last being edited. The easier way is the way I suggested. – camickr Nov 12 '13 at 19:57
  • @camickr +1 but I can generating funny `Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException at javax.swing.JTable.columnMarginChanged(JTable.java:4592)... ` that is reason for invokeLater ???, EDIT really I moved with column by mistake phaaaaaa, out of idea now – mKorbel Nov 12 '13 at 20:43
  • @mKorbel, I have not played with the OP's original idea. I don't think it is a good approach. I don't know why you get an exception. – camickr Nov 12 '13 at 21:20
  • @camickr put there 3-4 chars with intention to display JOptionPane, then resize column with TableCellEditor – mKorbel Nov 12 '13 at 22:22
  • @mKorbel, I see what you mean. The problem is that when the column is resized the `columnMarginChanged` method of JTable gets called multiple times and ultimately causes an Exception because the `isEditing()` method returns true, yet the `getCellEditor()` return false. So somehow these methods are out of sync. I don't know why. I also don't know an easy solution. – camickr Nov 13 '13 at 04:10
  • @camickr I followed your recommendations but I am encountering another problem. I created a new stack here: http://stackoverflow.com/questions/19977908/jcombobox-as-jtable-celleditor-with-overriden-stopcellediting-modifies-wrong-tab – Alex Burdusel Nov 14 '13 at 12:40
  • @Burfee you hit a patch of dirt with your requirement .. the editor implementations (DefaultCellEditor and even worse the comboCellEditor in the comboBox) are very sub-optimal, so it's hard (read: I couldn't do it on-the-fly) to achieve. If you are pressed, going with whatever dirty hack you come up might indeed be the best option for now - doing it right later. – kleopatra Nov 14 '13 at 15:31