# JTable Handling Fokus on Keystroke und Maus Doubleclick



## Volker II (3. Jul 2006)

Hallo Zusammen.

Ich habe eine Tabelle, in der einige Spalten als default Editor den JTextField Editor haben. Die einzelnen Zellen fange ich dann über key typed ab, um so just-in-time die Eingaben zu validieren. Das funktioniert auch insoweit, als dass der Fokus zum Editieren der Zellen über die Tastaur erfolgt. Sobald aber ein Doubleclick eine Zelle aktiviert, scheint JAVA zu vergessen, dass der default Editor der JTextField ist (so erkläre ich mir das Verhalten wenigstens), denn nach dem Fokus auf Maus Doubleclick verliere ich die Kontrolle der anschliessenden Tastatureingaben.

Welche Seiteneffekte treten auf, wenn eine JTable-Zelle den Fokus über den Doubleclick erfährt und wie unterscheide ich einen doubleclick- von einem mouseclick event?

Danke


----------



## Beni (3. Jul 2006)

Die "isCellEditable"-Methode vom CellEditor sollte da was mitbekommen.

Aber füg dem Document deines JTextFields doch einen DocumentListener hinzu. Der wird immer informiert, wenn sich irgendwas am Text verändert hat.


----------



## Volker II (3. Jul 2006)

Zu meinem Verständis (warum gibt es so keine Swing-Doku mit Tiefenwirkung?). 

Warum soll ich einer Tabelle, die ja ihrerseits Listener hat, eigene Listener hinzufügen. Taugen die "eigenen" so wenig oder bekommen die nicht alles mit?

Das Textfield habe ich in dem Sinne gar nicht. Ich habe eine Tabelle mit ca 270 Zellen in diversen Spalten und Reihen und keins davon ist ein echtes JTextField. Das ist doch nur ein virtuelles "Feld", dem ein Editor zugeordnet wird. In meinem Fall ein JTextField-Editor. Falls durch double Mausklick diese Zuordnung zum JTextField-Editor verloren gehen sollte, warum sollte dann mein Listener zu dieser virtuellen Instanz dennoch vorhanden sein?

Ausserdem, in meinem Source checke ich vor meiner just-in-time Validation, dass a: die Zelle an der Position editierbar ist und b ob der Wert ungleich null ist.

Die Frage bleibt also an sich erhalten, was passiert alles versteckt, wenn eine Tabellenzelle den Fokus über doubleclick erhält im Unterschied zur Aktierung durch Tasten - egal welche? 

Danke


----------



## Beni (3. Jul 2006)

Ich weiss nicht, inwiefern du den Aufbau einer JTable verstehst (eigentlich steht alles in der API, aber man muss sich tatsächlich längere Zeit durchwühlen):

Eine JTable besitzt einerseits das TableModel, welches alle Daten speichert.
Andererseits gibt es CellRenderer, welche einzelne Zellen zeichnen.

Und dann gibt es CellEditoren. Die Editoren werden z.b. mit einem Doppelklick aktiviert (das ist IMHO die Standardeinstellung). Sobald ein Editor aktiviert ist, geht der Fokus an den Editor über. Der Editor selbst ist eine unabhängige Komponente: er bekommt von der JTable ein Wert, und irgendwann wird er der JTable mitteilen, dass der Wert editiert wurde, und nun in das Model zurückgeschrieben werden kann.
Alle Tests, ob ein Wert korrekt ist, haben in der Editor-Klasse zu erfolgen, die JTable ist dabei rein passiv. Es ist auch nicht notwendig, der JTable irgendwelche zusätzlichen Listeners hinzuzufügen.
Ein Editor entscheidet selbst, wann er aktiviert werden soll: mit Hilfe der "isCellEditable"-Methode.

Vielleicht hilft auch dieser Link weiter.


----------



## Volker II (3. Jul 2006)

Nun habe ich durch:


```
private class FormListener implements 
        java.awt.event.KeyListener, 
        java.awt.event.MouseListener {

        public void keyTyped(java.awt.event.KeyEvent evt) {
            if (evt.getSource() == dTAB) {
                javaTAS.this.dTABKeyTyped(evt);
            }
        }

        public void mouseClicked(java.awt.event.MouseEvent evt) {
            if (evt.getSource() == dTAB) {
                javaTAS.this.DTAB_MOUSE(evt);
            }
        }

        public void mousePressed(java.awt.event.MouseEvent evt) {
            if (evt.getSource() == dTAB) {
                javaTAS.this.DTAB_MOUSE(evt);
            }
        }

        public void mouseReleased(java.awt.event.MouseEvent evt) {
            if (evt.getSource() == dTAB) {
                javaTAS.this.DTAB_MOUSE(evt);
            }
        }
    }
```

wobei dieser Teil das Produkt der NB IDE 5.0 ist und diesen Testschnipsel von mir


```
private void DTAB_MOUSE(java.awt.event.MouseEvent evt) {
        System.out.println(evt.toString());
        System.out.println(evt.getClickCount());
        evt.consume();
    }
```



```
public void actionPerformed(java.awt.event.KeyEvent evt) {
        String _ctype = evt.getSource().getClass().getName();
            
        if (_debug) {
            System.out.println("KE: " + _ctype);
        }

        if (_ctype.compareTo("javax.swing.JTable") == 0) {
            int _row = dTAB.getEditingRow();
            int _col = dTAB.getEditingColumn();
            
            if (_row != -1 && _col != -1) {
                if (_col <= 12 && _col >= 1) {
                    if (dTAB.isCellEditable(_row, _col) && dTAB.getCellEditor().getCellEditorValue() != null) {
                        chkNumeric(evt, dTAB.getCellEditor().getCellEditorValue().toString(), 5);
                    }
                }
            }
        }
    }
```


```
public void chkNumeric(java.awt.event.KeyEvent evt, String tabValue, int max) {
        char    c = evt.getKeyChar();
        int     i = tabValue.indexOf(":");
        int     k = tabValue.length();
        boolean b = true;

        if (_debug) {
            System.out.println("TabValue: " + tabValue + " KeyValue = " + c + " Pos = " + k);
        }
            
        if ((b) && (k >= max))
            b = false;
        
        if ((b) && (! (Character.isDigit(c)) && (c != ':') 
                 && (c != java.awt.event.KeyEvent.VK_BACK_SPACE) 
                 && (c != java.awt.event.KeyEvent.VK_DELETE)))
           b = false;
        
        if ((b) && (c == ':') && (i != -1))
           b = false;

        if ((b) && (c == ':') && (k != 2))
           b = false;

        if ((b) && (k == 2) && (c != ':'))
            b = false;

        if ((b) && (k == 0) && (c > '2'))
            b = false;

        if ((b) && (k == 1) && (tabValue.compareTo("2") == 0) && (c > '3'))
            b = false;

        if ((b) && (k == 3) && (c > '5'))
            b = false;
        
        if (! b) {
            evt.consume();
            getToolkit().beep();
        }
    }
```


versucht, das Mouse event zu blocken, aber es lässt sich nicht blocken?!? Zumindest so nicht...

Der nun fast vollständige Code zu diesem Problem macht den Zusammenhang ersichtlich. Aktiviere ich per Keyboard ein editierbares Feld kann ich diesen Felder just-in-time nur valide Uhrzeiten mit Doppelpunkt an Position 2 erfassen. Das funktioniert so! Aktiviere ich eine Zelle über doublemouseclick funktioniert es nicht.


----------



## Volker II (3. Jul 2006)

Hi Benni



			
				Beni hat gesagt.:
			
		

> Ich weiss nicht, inwiefern du den Aufbau einer JTable verstehst (eigentlich steht alles in der API, aber man muss sich tatsächlich längere Zeit durchwühlen):



Wäre ich mir da sicher, würde ich entweder sicher einen bug melden oder es würde funzen...



			
				Beni hat gesagt.:
			
		

> Die Editoren werden z.b. mit einem Doppelklick aktiviert (das ist IMHO die Standardeinstellung). Sobald ein Editor aktiviert ist, geht der Fokus an den Editor über. Der Editor selbst ist eine unabhängige Komponente: er bekommt von der JTable ein Wert, und irgendwann wird er der JTable mitteilen, dass der Wert editiert wurde, und nun in das Model zurückgeschrieben werden kann.



Aber es scheint ein Unterschied zu sein, ob ich den Editor über Taste, das ist ja möglich, oder über doublemouse click aktiviert wird. Im ersten Falle habe ich in meinem Programm die Kontrolle über den Editor bzw. die keystrokes während der Eingabe im anderen Fall nicht.

Die Frage bleibt also erstma, was macht den Unterschied?


...mit 


```
dTAB.setSurrendersFocusOnKeystroke(false);
```

funktioniert es per keyboard

...mit


```
dTAB.setSurrendersFocusOnKeystroke(true);
```

funktioniert es *auch* per keyboard nicht

erklär mir doch noch ein wenig zum autonomen editor....


----------



## Volker II (3. Jul 2006)

Beni,

wenn ich Deinem Tutorial folge, wäre ein eigener EditorListener die richtige Wahl, der auf Events aller Art (jede Mausaktion mit Values / jede Taste mit Values) reagiert (von Mouse/Keyboard).

Auch wenn es meine Hausarbeit ist, der kick dazu in die richtige Richtung fehlt dazu in Deinem Tutorial oder überlese ich das an einer Stelle.


----------



## Beni (3. Jul 2006)

Entschuldige, dass ich mich nicht mehr gemeldet habe; war unterwegs.

Ich habe mal so eine Tabelle implementiert, wie ich deine Beschreibung verstanden habe. Bei einem Doppelklick, oder sobald man etwas tippt, wird geschrieben (jedenfalls läufts so bei mir). Es wird auch copy&paste unterstützt.


```
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableModel;

public class Test2 extends JTable{
    public static void main( String[] args ){
        JFrame frame = new JFrame();
        DefaultTableModel model = new DefaultTableModel( new Object[]{ "A", "B" }, 0);
        model.addRow( new Object[]{ "1", "2" });
        model.addRow( new Object[]{ "3", "4" });
        frame.add( new JScrollPane( new Test2( model )));
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );
    }
    
    public Test2( TableModel model ){
        super( model );
        
        // Hier wird der Editor eingesetzt
        setDefaultEditor( Object.class, new TestEditor());
    }
    
    // Der Editor selbst ist ein einfaches JTextField
    private class TestEditor extends JTextField implements TableCellEditor, DocumentListener{
        private List<CellEditorListener> listeners = new ArrayList<CellEditorListener>();
        private boolean online = false;
        
        public TestEditor(){
            getDocument().addDocumentListener( this );
        }

        public Component getTableCellEditorComponent( JTable table, Object value, boolean isSelected, int row, int column ) {
            setText( value.toString() );
            online = true;
            return this;
        }

        public void addCellEditorListener( CellEditorListener l ) {
            listeners.add( l );
        }

        public void cancelCellEditing() {
            online = false;
            ChangeEvent event = new ChangeEvent( this );
            for( CellEditorListener listener : listeners.toArray( new CellEditorListener[ listeners.size() ] ))
                listener.editingCanceled( event );
        }

        public Object getCellEditorValue() {
            return getText();
        }

        public boolean isCellEditable( EventObject anEvent ) {
            if( anEvent instanceof MouseEvent )
                return ((MouseEvent)anEvent).getClickCount() > 1;
                
            return true;
        }

        public void removeCellEditorListener( CellEditorListener l ) {
            listeners.remove( l );
        }

        public boolean shouldSelectCell( EventObject anEvent ) {
            return true;
        }

        public boolean stopCellEditing() {
            // falls der eingegebene Text nicht ok ist, müsste man 
            // das hier abfangen: man gibt einfach nur "false" zurück
            online = false;
            ChangeEvent event = new ChangeEvent( this );
            for( CellEditorListener listener : listeners.toArray( new CellEditorListener[ listeners.size() ] ))
                listener.editingStopped( event );
            
            return true;
        }

        public void changedUpdate( DocumentEvent e ) {
            print();
        }

        public void insertUpdate( DocumentEvent e ) {
            print();
        }

        public void removeUpdate( DocumentEvent e ) {
            print();
        }
        
        public void print(){
            // selbstverständlich wären hier auch irgendwelche graphischen Effekte denkbar...
            if( online )
                System.out.println( "Current value: " + getText() );
        }
    }
}
```


----------



## Volker II (4. Jul 2006)

Beni.

Kein Problem und Danke für die viele Arbeit. Wo bei mir offensichtlich ein dickes Brett den Blick auf die Welt vernagelt... Wo zum Teufel finde ich den Punkt bzw. wo "konstruiere" ich die Verbindung zu Key- und Mouse-Events im Editor dran?


Ich habe jetzt einfach mal eine Klasse aufgebaut in meine bestehende Klasse javaTAS, die so aussieht:


```
private class myEditorListener implements java.awt.event.KeyListener, 
        java.awt.event.MouseListener, 
        javax.swing.event.CellEditorListener {

        private boolean _isDebug = false;
        private char    _isDigit = 0x00;
        private int     _isCode  = -1;
    
        private void setDebug(boolean _debug) {
            this._isDebug = _debug;
        }
    
        private void setKeyValues(java.awt.event.KeyEvent evt) {
            this._isDigit = evt.getKeyChar();
            this._isCode  = evt.getKeyCode();
        }

        private boolean getDebug() {
            return this._isDebug;
        }

        public void keyReleased(java.awt.event.KeyEvent evt) {
        }

        public void keyPressed(java.awt.event.KeyEvent evt) {
        }
    
        public void keyTyped(java.awt.event.KeyEvent evt) {
            if (this.getDebug()) {
                System.out.println(evt.getComponent().getClass().toString());
                System.out.println(evt.getKeyChar());
            }
        
            setKeyValues(evt);
            actionPerformed(evt);
        }

        public void mouseExited(java.awt.event.MouseEvent evt) {
        }

        public void mouseEntered(java.awt.event.MouseEvent evt) {
        }

        public void mouseClicked(java.awt.event.MouseEvent evt) {
            if (this.getDebug()) {
                System.out.println(evt.getComponent().getClass().toString());
            }
        }

        public void mousePressed(java.awt.event.MouseEvent evt) {
            if (this.getDebug()) {
                System.out.println(evt.getComponent().getClass().toString());
            }
        }

        public void mouseReleased(java.awt.event.MouseEvent evt) {
            if (this.getDebug()) {
                System.out.println(evt.getComponent().getClass().toString());
            }
        }

        public void editingCanceled(javax.swing.event.ChangeEvent evt) {
        }

        public void editingStopped(javax.swing.event.ChangeEvent evt) {
        }
    }

    ...

    public void actionPerformed(java.awt.event.KeyEvent evt) {
        String _ctype = evt.getSource().getClass().getName();
            
        if (_debug) {
            System.out.println("KE: " + _ctype);
        }

        if (_ctype.compareTo("javax.swing.JTable") == 0) {
            int _row = dTAB.getEditingRow();
            int _col = dTAB.getEditingColumn();
            
            if (_row != -1 && _col != -1) {
                if (_col <= 12 && _col >= 1) {
                    if (dTAB.isCellEditable(_row, _col) && dTAB.getCellEditor().getCellEditorValue() != null) {
                        chkNumeric(evt, dTAB.getCellEditor().getCellEditorValue().toString(), 5);
                    }
                }
            }
        }
    }

    ...

    public void setTabDefaults(boolean _setting) {
        myEditorListener dCEL = new myEditorListener();
        dCEL.setDebug(true);
        
        dTAB.getTableHeader().setEnabled(_setting);
        dTAB.getTableHeader().setResizingAllowed(_setting);
        dTAB.getTableHeader().setReorderingAllowed(_setting);
        dTAB.setDragEnabled(_setting);
        dTAB.setColumnSelectionAllowed(_setting);
        dTAB.setRowSelectionAllowed(_setting);
        
        dTAB.getColumnModel().getColumn( 2).setCellEditor(new DefaultCellEditor(new JTextField()));
        dTAB.getColumnModel().getColumn( 2).getCellEditor().addCellEditorListener(dCEL);

    ...
    }
```




und dachte, es funzt. Machts aber nicht. Der Editor übernimmt diesen Listener nicht und die darin enthaltenen Methoden. Also auf keyTyped wird nicht reagiert. 

Bin schon reichlich konfus - diese Hitze, das Problem....  :autsch:


----------



## Volker II (10. Jul 2006)

hallo beni... :wink: irgendeine bemerkung?!?

ich habe jetzt wesentliche Teile Deines Codes adaptiert:


```
//~--- JDK imports ------------------------------------------------------------

import java.lang.Integer;
import java.lang.String;
import java.util.*;

import javax.swing.*;

//~--- classes ----------------------------------------------------------------

public class JDateField extends JTextField
        implements javax.swing.event.DocumentListener,
                   java.awt.event.FocusListener, java.awt.event.KeyListener,
                   java.awt.event.MouseListener,
                   javax.swing.event.CellEditorListener {
    protected boolean _isDebug = true;
    protected char    _isDigit = 0x00;
    protected int     _isCode  = -1;
    protected JTable  _isTable = new JTable();

    //~--- constructors -------------------------------------------------------

    public JDateField() {
        getDocument().addDocumentListener(this);
    }

    //~--- methods ------------------------------------------------------------

    public void changedUpdate(javax.swing.event.DocumentEvent evt) {
        System.out.println("cU: " + evt.getDocument().toString());
        print();
    }

    private void chkDayTime(java.awt.event.KeyEvent evt, String tabValue,
                            int max) {
        char    c = evt.getKeyChar();
        int     i = tabValue.indexOf(":");
        int     k = tabValue.length();
        boolean b = true;

        if (_isDebug) {
            System.out.println("TabValue: " + tabValue + " KeyValue = " + c
                               + " Pos = " + k);
        }

        if ((b) && (k >= max)) {
            b = false;
        }

        if ((b) && (!(Character.isDigit(c)) && (c != ':')
                && (c != java.awt.event.KeyEvent.VK_BACK_SPACE)
                && (c != java.awt.event.KeyEvent.VK_DELETE))) {
            b = false;
        }

        if ((b) && (c == ':') && (i != -1)) {
            b = false;
        }

        if ((b) && (c == ':') && (k != 2)) {
            b = false;
        }

        if ((b) && (k == 2) && (c != ':')) {
            b = false;
        }

        if ((b) && (k == 0) && (c > '2')) {
            b = false;
        }

        if ((b) && (k == 1) && (tabValue.compareTo("2") == 0) && (c > '3')) {
            b = false;
        }

        if ((b) && (k == 3) && (c > '5')) {
            b = false;
        }

        if (!b) {
            evt.consume();
            getToolkit().beep();
        }
    }

    public void editingCanceled(javax.swing.event.ChangeEvent evt) {
        System.out.println("eCc: " + evt.getSource().toString());
    }

    public void editingStopped(javax.swing.event.ChangeEvent evt) {
        System.out.println("eSt: " + evt.getSource().toString());
    }

    public void focusGained(java.awt.event.FocusEvent evt) {
        System.out.println("fG: " + evt.getSource().toString());
    }

    public void focusLost(java.awt.event.FocusEvent evt) {
        System.out.println("fL: " + evt.getSource().toString());
    }

    public void insertUpdate(javax.swing.event.DocumentEvent evt) {
        System.out.println("iU: " + evt.getDocument().toString());
        print();
    }

    public void keyPressed(java.awt.event.KeyEvent evt) {
        System.out.println("kP: " + evt.getSource().toString());
    }

    public void keyReleased(java.awt.event.KeyEvent evt) {
        System.out.println("kR: " + evt.getSource().toString());
    }

    public void keyTyped(java.awt.event.KeyEvent evt) {
        int    _row = -1;
        int    _col = -1;
        String _val = "none";

        System.out.println("kT: " + evt.getSource().toString());
        this.setKeyValues(evt);

        if (this._isTable != null) {
            if (this._isTable.isEditing()) {
                _row = this._isTable.getEditingRow();
                _col = this._isTable.getEditingColumn();
                _val = this._isTable.getValueAt(_row, _col).toString();
                chkDayTime(evt, _val, 5);
            }
        }

        if (this.getDebug()) {
            System.out.println(_row);
            System.out.println(_col);
            System.out.println(_val);
        }
    }

    public void mouseClicked(java.awt.event.MouseEvent evt) {
        System.out.println("mCl: " + evt.getSource().toString());
    }

    public void mouseEntered(java.awt.event.MouseEvent evt) {}

    public void mouseExited(java.awt.event.MouseEvent evt) {}

    public void mousePressed(java.awt.event.MouseEvent evt) {
        System.out.println("mPr: " + evt.getSource().toString());
    }

    public void mouseReleased(java.awt.event.MouseEvent evt) {
        System.out.println("mRe: " + evt.getSource().toString());
    }

    public void print() {}

    public void removeUpdate(javax.swing.event.DocumentEvent evt) {
        System.out.println("rU: " + evt.getDocument().toString());
        print();
    }

    //~--- get methods --------------------------------------------------------

    private boolean getDebug() {
        return this._isDebug;
    }

    //~--- set methods --------------------------------------------------------

    private void setDebug(boolean _debug) {
        this._isDebug = _debug;
    }

    public void setJTable(javax.swing.JTable _JTable) {
        _isTable = _JTable;
    }

    private void setKeyValues(java.awt.event.KeyEvent evt) {
        this._isDigit = evt.getKeyChar();
        this._isCode  = evt.getKeyCode();
    }
}


//~ Formatted by Jindent --- [url]http://www.jindent.com[/url]
```


und dies dann so verwurstet...:


```
private void setTabDefaults(boolean _setting) {
        JDateField dEDIT = new JDateField();
        
        dEDIT.setJTable(dTAB);

        ...
        
        dTAB.getColumnModel().getColumn( 2).setCellEditor(new DefaultCellEditor (dEDIT));
        dTAB.getColumnModel().getColumn( 3).setCellEditor(new DefaultCellEditor (dEDIT));
        dTAB.getColumnModel().getColumn( 4).setCellEditor(new DefaultCellEditor (dEDIT));
}
```

Selbstverständlich wird auch an einer Stelle setTabDefaults aufgerufen - das schenke ich mir dann an dieser Stelle. Es scheint nun zu funktionieren, zumindest bekomme ich System.out ... Infos der DocumentListener. Verstehe ich das nun so, dass die ganzen Events wie Mouse, Key oder Fokus völlig absolet sind, wenn die Tabellenzelle im Zugriff des Zelleditors sind, oder gibt es einen Weg sie doch (zumindest Key-Eevents) abzufangen?


----------

