JTable cells by default are in append mode when the cell is double clicked on.
Is there a way to make the cell default to overwrite mode instead, IOW, select the contents of the cell so that if the user started typing the old text would be replaced by the new text without having to manually erase it?
You should be able to do this by creating your own TableCellEditor, which can be assigned to the parent table using setCellEditor(). This object is a factory that is called by the JTable whenever the user starts editing a cell, to create the field used to do the actual editing. You can return your own JTextField, and simply avoid setting the old value to achieve what your asking. You'll also have to attach a listener to the text field to update the value in the table when the user has finished typing.
You might find the Table Select All Editor helpful.
I have implemented the following method [addDeletePreviousOnEditBehavior] that works fine! Note that you need to use some TableCellEditor. To do so add the following code:
JTable table=new JTable();
JTextField field=new JTextField();
addDeletePreviousOnEditBehavior(field);
table.setCellEditor(new DefaultCellEditor(field));
The method is:
public static void addDeletePreviousOnEditBehavior(final JComponent field) {
field.addFocusListener(new FocusListener() {
#Override
public void focusGained(FocusEvent fe) {
field.putClientProperty(DELETE_ON_EDIT, true);
}
#Override
public void focusLost(FocusEvent fe) {
}
});
field.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent ke) {
}
#Override
public void keyPressed(KeyEvent ke) {
if ((!(ke.isActionKey()
|| isSpecial(ke.getKeyCode())))
&& ((Boolean) field.getClientProperty(DELETE_ON_EDIT))) {
System.out.println("Key:" + ke.getKeyCode() + "/" + ke.getKeyChar());
field.putClientProperty(DELETE_ON_EDIT, false);
if (field instanceof JFormattedTextField) {
((JFormattedTextField) field).setValue(null);
}
if (field instanceof JTextComponent) {
((JTextComponent) field).setText(null);
}
}
}
#Override
public void keyReleased(KeyEvent ke) {
// do nothing
}
});
}
Related
I want to create a JDialog where the text in the textfields is selected but only if the focus is gained from keyboard (TAB, CTRL+TAB). I have found several topics on this matter but had problems with implementing it.
Here is one which I was trying.
And my code:
public class Dialogg extends JDialog implements FocusListener, MouseListener {
private boolean focusFromMouse = false;
public Dialogg() {
JTextField tf1 = new JTextField("text1");
JTextField tf2 = new JTextField("text2");
tf1.addMouseListener(this);
tf2.addMouseListener(this);
tf1.addFocusListener(this);
tf2.addFocusListener(this);
}
#Override
public void focusGained(FocusEvent e) {
if (!focusFromMouse) {
JTextField tf = (JTextField) e.getComponent();
tf.selectAll();
focusFromMouse = true;
}
}
#Override
public void focusLost(FocusEvent e) {
focusFromMouse = false;
}
#Override
public void mouseClicked(MouseEvent e) {
focusFromMouse = true;
}
}
It does not work as intended, it does not matter what is focus source the text always highlights. When I run the code and follow it step by step it turns out that focusGained code happens before mouseClicked code so the flag is not reset when it should. Any hints?
EDIT:
As suggested by M. Prokhorov I have deleted less relevant (for the question) lines from the code.Thank you.
EDIT 2:
I am trying to wrap focus listener as suggested by camickr. It looks like this now:
tf1.addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent evt){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if (!focusFromMouse){
tf1.selectAll();
focusFromMouse=true;
}
}
});
}
public void focusLost(FocusEvent evt){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
focusFromMouse=false;
}
});
}
});
public void mouseClicked(MouseEvent e) {
focusFromMouse=true;
I am printing line after each event to see the action order and still mouseClicked happens last. What am I doing wrong?
EDIT 3:
OK, I have found a solution which fulfils requirements of my simple Dialog.
I could not find a way of doing this with use of invokeLater or EventQueue. Vladislav's method works but as I understand it restricts the user to only use the keyboard.
I have used the initial approach but I have added an auxiliary variable and few conditions which allow to pass the flag "unharmed" trough Events that should not change the flag at given moment. It may not be subtle or universal but works for my app. Here is the code:
public void focusGained(FocusEvent e) {
if(!focusFromMouse){
if (higlight){
JTextField tf = (JTextField) e.getComponent();
tf.selectAll();
focusFromMouse=false;
}
}
}
public void focusLost(FocusEvent e) {
if (focusFromMouse){
higlight=false;
focusFromMouse=false;
}else{
higlight=true;
}
}
public void mousePressed(MouseEvent e) {
focusFromMouse=true;
}
At the first, by default, focus on JTextField is requested by mouse-press event, not by mouse-click.
So, this method:
public void mouseClicked(MouseEvent e) {
focusFromMouse = true;
}
is useless because the mouse-click event is triggered after the mouse-press event.
One way to solve your problem is to remove all native MouseListeners from JTextField:
...
for( MouseListener ml : tf1.getMouseListeners() ){
tf1.removeMouseListener(ml);
}
for( MouseMotionListener mml : tf1.getMouseMotionListeners() ){
tf1.removeMouseMotionListener(mml);
}
...
Another way is to handle all mouse events and consume those of them, which are triggered by JTextField:
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
if( event.getSource() == tf1 ){
((MouseEvent)event).consume();
}
}
}, AWTEvent.MOUSE_EVENT_MASK);
When I run the code and follow it step by step it turns out that focusGained code happens before mouseClicked
Wrap the code in the FocusListener in a SwingUtilities.invokeLater(). The will place the code on the end of the Event Dispatch Thread (EDT), so the code will run after the variable in the MouseListener has been set.
See Concurrency in Swing for more information about the EDT.
Edit:
Just noticed the other answer. You might be able to do something simpler. Istead of listener for mouseClicked, listen for mousePressed. A mouseClicked event is only generated AFTER the mouseReleased event, so by that time the FocusListener logic has already been executed, even when added to the end of the EDT.
Edit 2:
If the above doesn't work then you might be able to use the EventQueue.peek() method to see if a MouseEvent is on the queue. This might even be easier than worrying about using the invokeLater.
Hi I badly need some help I already search about Jtextfield to be filled before jbutton enables, DocumentListener most people use to determined if Jtextfield is being populated. I tried DocumentListener and it works but all I want is all Jtextfield must be not empty before the Jbutton enables here is my code.
Ftext.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void insertUpdate(DocumentEvent e) {
change();
}
#Override
public void removeUpdate(DocumentEvent e) {
change();
}
#Override
public void changedUpdate(DocumentEvent e) {
change();
}
private void change(){
if (Ftext.getText().equals("") && Mtext.getText().equals("") && Ltext.getText().equals("") && Addtext.getText().equals("")) {
SaveButton.setEnabled(false);
} else {
SaveButton.setEnabled(true);
}
}
});
if (Ftext.getText().equals("") && Mtext.getText().equals("") && Ltext.getText().equals("") && Addtext.getText().equals(""))
Means that all the fields must be empty. Some times you need to read this logic aloud...
"if field is empty AND field is empty AND field is empty..."
If you used || (or) instead, it would mean that if any one of the fields was empty the statement would be true for example...
if (Ftext.getText().equals("") ||
Mtext.getText().equals("") ||
Ltext.getText().equals("") ||
Addtext.getText().equals("")) {...
You should also consider using .getText().isEmpty() or .getText().trim().isEmpty() if the fields shouldn't contain just spaces.
You might also consider writing a single DocumentListener implementation instead of creating a new anonymous class for each field
public class FieldValidationHandler implements DocumentListener() {
private List<JTextField> monitorFields;
public FieldValidationHandler(JTextField... fields) {
monitorFields = Arrays.asList(fields);
for (JTextField field : monitorFields) {
field.getDocument().addDocumentListener(this);
}
}
#Override
public void insertUpdate(DocumentEvent e) {
change();
}
#Override
public void removeUpdate(DocumentEvent e) {
change();
}
#Override
public void changedUpdate(DocumentEvent e) {
change();
}
private void change(){
boolean enabled = true;
for (JTextField field : monitorFields) {
if (field.getText().trim().isEmpty()) {
enabled = false;
break;
}
}
SaveButton.setEnabled(enabled);
}
}
Then you'd just create a single instance...
FieldValidationHandler handler = new FieldValidationHandler(Ftext, Mtext, Ltext, Addtext);
Now, this approach is a little sneaky, in that it adds the DocumentListener to the fields you specify via the constructor automatically.
Another approach might be to have some kind "Validation" controller, that you would pass to this handler and it would call some kind of "validate" method when change was called.
This would separate the listener from the fields, but this is all a matter of context at the time.
I would personally have a "register" and "unregister" process which would allow you to add or remove fields as you need to
I need to override the enter key functionality on a JTable. At present the default behaviour is to move the row selection down one row when the user presses the 'Enter' key. I want to disable this and get it to do something different based on their selection. The problem is that it seems to move down before it goes into my keylistener which takes in the row selection - this therefore opens another window with the wrong row selected.
This is my code so far...:
public class MyJTable extends JTable {
public MyJTable(){
setRowSelectionAllowed(true);
addListeners()
}
public void addListeners(){
addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {}
#Override
public void keyPressed(KeyEvent e) {}
#Override
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_ENTER) {
openChannel();
}
}
});
}
public void openChannel(){
for (int selectedRow : getSelectedRows()){
//Code to open channel based on row selected
}
}
}
+1 to #Robin's answer
Adding to my comment...
Swing uses KeyBindings simply replace exisitng functionality by adding a new KeyBinding to JTable (the beauty happens because of JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT):
private void createKeybindings(JTable table) {
table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "Enter");
table.getActionMap().put("Enter", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
//do something on JTable enter pressed
}
});
}
simply call this method and pass JTable instance to override standard functionality of JTable ENTER
This is implemented using key bindings, which is preferred over key listeners. I strongly suggest you do the same: replace your key listener by a key binding.
The solution is replace the entry in the InputMap to point to your own Action (which you insert in the action map), or to just replace the appropriate entry in the action map.
The key bindings tutorial contains more info
Is it possible that when I clicked the textfield it would clear the recent text that was inputed there?. Mine was like, suppose these are textfields.
Name: Last Name First Name Middle Initial
Then I would click the Last Name and it would be cleared, same as First Name and Middle Initial. thanks for reading, hope you can help me.
Consider a FocusListener, one where all the text is selected:
myTextField.addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent fEvt) {
JTextField tField = (JTextField)fEvt.getSource();
tField.selectAll();
}
});
By selecting all of the text, you give the user the option of either typing and thus deleting the current text and replacing it with the new text, or using the mouse or arrow keys to keep the current text and possibly change it.
I think Hovercraft is right. Better to use a FocusListener for this purpose.
I would write a utility class that could deal with this, I've done something similar for auto select. Means I don't have to extend every text component that comes along or mess around with lost of small focus listeners that do the same thing.
public class AutoClearOnFocusManager extends FocusAdapter {
private static final AutoClearOnFocusManager SHARED_INSTANCE = new AutoClearOnFocusManager();
private AutoClearOnFocusManager() {
}
public static AutoClearOnFocusManager getInstance() {
return SHARED_INSTANCE;
}
#Override
public void focusGained(FocusEvent e) {
Component component = e.getComponent();
if (component instanceof JTextComponent) {
((JTextComponent)component).setText(null);
}
}
public static void install(JTextComponent comp) {
comp.addFocusListener(getInstance());
}
public static void uninstall(JTextComponent comp) {
comp.removeFocusListener(getInstance());
}
}
Then you just need to use
JTextField textField = new JTextField("Some text");
AutoClearOnFocusManager.install(textField);
If you're just looking to supply a "prompt" (text inside the field that prompts the user), you could also look at the Prompt API
Why don't use the mouseClicked event?
So, you can have something like
jTextFieldMyText.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
jTextFieldMyTextMouseClicked(evt);
}
});
private void jTextFieldMyTextMouseClicked(java.awt.event.MouseEvent evt) {
jTextFieldMyText.setText("");
}
In the case of focus
jTextFieldMyText.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusGained(java.awt.event.FocusEvent evt) {
jTextFieldMyTextFocusGained(evt);
}
});
private void jTextFieldMyTextFocusGained(java.awt.event.MouseEvent evt) {
jTextFieldMyText.setText("");
}
If deleting text inmediatelly isn't what's wanted, use selectAll() instead of setText("") as suggested many times
I have an editable JTable and have set a DefaultCellEditor like so:
colModel.getColumn( 1 ).setCellEditor( new DefaultCellEditor( txtEditBox ) {
// ...
#Override
public void cancelCellEditing() {
super.cancelCellEditing();
// handling the event
}
// ...
}
However, when pressing escape while editing a cell in this column, though the editing mode is finished, this method is not called. Any ideas why? Am I doing something wrong? Is there a way to handle this (other than manually adding a KeyListener that is)?
The official way: You can register a CellEditorListener: AbstractCellEditor.addCellEditorListener(...). If the editing is canceled, editingCanceled(ChangeEvent e) should be called. Due to a SUN bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6788481, editingCanceled is not called :(
As workaround you can register your own action for the ESCAPE key and handle it yourself. But it will not work for resize events.
Another solution (quick and dirty;-)): Overwrite the methode JTable.removeEditor() and insert your code after the super call.
I had this problem too. I wrote another workaround that involves ActionListener and FocusListener. This is it:
public class TableEditorListenerHelper {
// dealing with events
private final EventListenerList listeners = new EventListenerList();
private ChangeEvent changeEvent;
// cell editor that we're helping
private CellEditor editor;
// transient state
private boolean editing = false;
private JTable table;
public TableEditorListenerHelper(CellEditor editor, JTextField field) {
this.editor = editor;
field.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
fireEditingStopped();
}
});
field.addFocusListener(new FocusListener() {
#Override public void focusGained(FocusEvent e) {
editing = true;
}
#Override public void focusLost(FocusEvent e) {
JTable table = TableEditorListenerHelper.this.table;
if (editing && isEditing(table)) {
fireEditingCanceled();
}
}
private boolean isEditing(JTable table) { // a hack necessary to deal with focuslist vs table repaint
return table != null && table.isEditing();
}
});
}
public void setTable(JTable table) {
this.table = table;
}
public void addCellEditorListener(CellEditorListener l) {
listeners.add(CellEditorListener.class, l);
}
public void removeCellEditorListener(CellEditorListener l) {
listeners.remove(CellEditorListener.class, l);
}
public CellEditorListener[] getCellEditorListeners() {
return listeners.getListeners(CellEditorListener.class);
}
protected void fireEditingCanceled() {
for (CellEditorListener l : getCellEditorListeners()) {
l.editingCanceled(getOrCreateEvent());
}
resetEditingState();
}
protected void fireEditingStopped() {
for (CellEditorListener l : getCellEditorListeners()) {
l.editingStopped(getOrCreateEvent());
}
resetEditingState();
}
private void resetEditingState() {
table = null;
editing = false;
}
private ChangeEvent getOrCreateEvent() {
return changeEvent = changeEvent == null ? new ChangeEvent(editor) : changeEvent;
}
Here you can find a little more complete solution.
Another way fix this bug:
jTable.addPropertyChangeListener("tableCellEditor", e -> {
Object o = e.getOldValue();
if (o instanceof DefaultCellEditor) {
((DefaultCellEditor) o).cancelCellEditing();
}
});