how do I simulate "onStartCellEditing" for DefaultCellEditor
Asked Answered
S

2

1

CellEditorListener has "editingStopped" and "editingCancelled". But how might I implement a piece of code which needs to run when a cell editing session starts?

A typical example might be where you want the text of a JTextField editor component to go selectAll() when you start editing. I'm tempted to think the thing to do is override one of the methods of DefaultCellEditor, such as getTableCellEditorComponent or getCellEditorValue or getComponent, but none of these explicitly says they are called at the start of an edit session.

Conversely, we do know that JTable.getCellEditor returns the editor if we are editing, but null if not. This is because the component is made into a child object of JTable when editing starts. It also seems to get the focus at the beginning of an editing session, so you might perhaps think of add a FocusListener to the editor component (JTextField, etc.). But is this guaranteed? Also, maybe focus can be lost and then return during an editing session.

There's a way of listening for the "addition" (as a child object) of this editor component: ContainerListener. Unless someone tells me different this appears to be the most direct and rational way of getting a call that an editing session has begun. But it seems odd that there isn't a more direct way...

Splatter answered 11/10, 2015 at 16:52 Comment(0)
W
2

A typical example might be where you want the text of a JTextField editor component to go selectAll() when you start editing.

You can override the editCellAt(...) method of JTable to select the text once editing has started:

@Override
public boolean editCellAt(int row, int column, EventObject e)
{
    boolean result = super.editCellAt(row, column, e);
    final Component editor = getEditorComponent();

    if (editor != null && editor instanceof JTextComponent)
    {
        ((JTextComponent)editor).selectAll();

        if (e == null)
        {
            ((JTextComponent)editor).selectAll();
        }
        else if (e instanceof MouseEvent)
        {
            SwingUtilities.invokeLater(new Runnable()
            {
                public void run()
                {
                    ((JTextComponent)editor).selectAll();
                }
            });
        }
    }

    return result;
}
Woody answered 11/10, 2015 at 17:6 Comment(3)
Thanks: I didn't know editCellAt fired following user action. A version I've just tried which omits the testing of 'e' makes my failing unit test pass. This is caused by a java.awt.Robot-simulated keyPress on KeyEvent.VK_F2. I'm going to have to do a bit of experimenting to understand the absolute necessity of your spawning of a new Runnable, which I avoid unless there is absolutely no other way. And indeed to determine whether editor is ever null. But I'm aware of your rep, so no doubt you could give chapter and verse on this!Splatter
@mikerodent, I'm going to have to do a bit of experimenting to understand the absolute necessity of your spawning of a new Runnable - you can check out Table Select All Editor. The comment in the source code explains why I added the invokeLater(...).Woody
thanks. 2nd mouse click, right. I don't suppose there's any chance that the invokeLater Runnable might in fact run before the 2nd click? Also, I suppose one might use the setting of DefCellEditor.getClickCountToStart() 1) to decide not to spawn (if ==1) and 2) to anticipate and cancel a collapse of the caret (if ==2)... just thinking out loud, but you've probably considered these and decided that it's over-thinking the issue...Splatter
S
1

Rob Camick's answer is great, but I have another one for "completists" to peruse.

I use Jython but it should be simple enough for Java people to understand. In Python/Jython you can add arbitrary "attributes" to (almost) any object. So in editCellAt I add an attribute "start_editing" which then signals to the caret listener added to the JTextField editor component that a session has just begun. If the caret dot and mark are equal (collapsed), if the click-count-to-start-editing == 2, and if the editor has the "start_editing" attr, you select-all (again), and you also remove the "start_editing" attr... it works (!), without spawning a new Runnable.

class DatesTable( javax.swing.JTable ):
    def editCellAt(self, row, column, event_obj ):
        result = self.super__editCellAt( row, column, event_obj )
        if self.editorComponent:
            self.editorComponent.requestFocus() # explanation below
            self.editorComponent.selectAll()
            if isinstance( event_obj, java.awt.event.MouseEvent ):
                self.cellEditor.start_editing = None
        return result

class DatesTableCellEditor( javax.swing.DefaultCellEditor ):
    def __init__( editor_self, table, *args, **kvargs ):
        jtf = javax.swing.JTextField()
        class JTFCaretListener( javax.swing.event.CaretListener ):
            def caretUpdate( self, caret_event ):
                if hasattr( editor_self, 'start_editing' ):                     
                    del editor_self.start_editing
                    if caret_event.dot == caret_event.mark and editor_self.clickCountToStart == 2:
                        caret_event.source.selectAll()
        jtf.addCaretListener( JTFCaretListener())
        javax.swing.DefaultCellEditor.__init__( editor_self, jtf, **kvargs )

A similar result could be achieved in Java, clearly, using a private field of the editor, or something along those lines.

NB why have I put "requestFocus" in there? If you press key F2 on an editable table cell the editing starts and the editor component (typically a JTextField) gets focus automatically. However you can also start editing just by typing into the table cell. It took me much head-scratching to work out that, oddly, if you do this you do indeed start editing, but the editor component does not get focus automatically. So this is a way to "resolve this anomaly".

NB2 One final point about my solution. In a real-world implementation you also need to set a timer to remove the attribute "start_editing" after a certain finite time (e.g. 0.5 s). Otherwise you might click once, which would not start an editing session (if click-to-start == 2), and then click again 10 seconds later, after having started editing by another means, to find that selectAll happens confusingly and inexplicably. This attribute must be made to be like the tape in Mission Impossible: self-destruct after x seconds...

Splatter answered 12/10, 2015 at 21:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.