Prevent Java Swing JTable losing focus when invalid data entered - java

We currently have a focus problem with a JTable/JTextEditor in java swing. The JTable has a custom cell editor which is a JTextField.
The issue is when a cell is being edited and contains invalid data, and the user clicks on a JButton, the text field will stop editing and the JButton actionPerformed (clicked) is called. The JTable#setValueAt handles validation so if the data in the JTextField is invalid, the underlying TableModel is not updated.
Ideally, we do not want to let the JButton click occur. Focus should remain with the JTable or the JTextField.
Clicking the button will perform a submit action and close the frame the table is in. As the validation in the TableModel#setValueAt does not update the value, it submits the old value.
Can this be done? I am still fairly new to Swing so I am not aware what to check.
Unfortunately, our code is not straight forward. The UI is constructed from XML in such a way that the button knows nothing about anything else on a form (this is code I have inherited).
In .net you could stop a control losing focus by handling a Validating event and setting a cancel flag. Is there a similar mechanism with Java.

Validating the input after editing has concluded, in setValueAt(), may be inconveniently late. The editor itself can preclude navigation for invalid values, as shown in this example that links to the corresponding tutorial section.
For valid values, you can make the table commit when losing focus:
table.putClientProperty("terminateEditOnFocusLost", true);

Can you try using inputverifier on the editor component, i.e. text field?

When the focus is lost from a component, the lost focus method is called (more reference in http://docs.oracle.com/javase/tutorial/uiswing/events/focuslistener.html). Therefore, you may call the validation method when you lose the focus.
If you do not need to be aware of the specific field being edited, you can also perform validation inside your button and prevent the submission if it is not sucessful.

I'd achieved a similar functionality by overriding the stopCellEditing method in my JTable's CellEditor.
#Override
public boolean stopCellEditing() {
String s = (String) getCellEditorValue();
if (s != null) {
if (!testYourValue()) {
Toolkit.getDefaultToolkit().beep();
return false;
}
}
return super.stopCellEditing();
}

Related

Java swing requestFocusInWindow()

I know there are many questions on how to focus on a certain text field and so on, but it seems that the issue I am facing is a bit different. So, I have a JTextField which has functionality to autocomplete the text if something that is currently in it has been typed before by the user. I also have a set of buttons that perform insertion of some predefined portion of text into the text field when pressed. The problem is that every time any new text appears in the text field, the autocomplete can trigger and append the text that was used by the user previously. In order to it more friendly, I decided to select the part appended by the autocomplete. All the code is executed in the ED thread. Consider the case when the text field was not in focus :
Both code samples are in the actionPerformed method of the button.
// does not work
if (textField.requestFocusInWindow()) {
textField.getDocument().insertString(...);
}
The insertString() is overriden and has all the logic to select appended string by the autocomplete. So, the only thing I need is that the text field is selected before the string is inserted.
I tried :
// does work
textField.requestFocusInWindow();
SwingUtilities.invokeLater(() -> {
textField.getDocument().insertString(...);
});
The official doc says :
The call to the requestFocusInWindow method initiates the focus transfer, but it does not immediately move the focus ...
This sort of makes sense, since to acquire the focus, a call must be made to the window manager of the clients operating system (that's how I understand that, correct me if I am wrong).
Is the second example working mainly because both events (getting focus and inserting the string) are added to the event queue and the insertion appears after the focus request (actually at the end of the queue) or am I missing something? Is it a valid solution? Can it be done better?
Note: simply adding a listener to the text field ( to react when it is in focus) is not a solution, since the logic I described here is only a part of the whole functionality used.

how to cancel jtextarea event?

here is the scenario. I've a jtextArea in my project. On keypressed event of jtextArea I check whether pressed key is valid or not by using getKeyChar() method of KeyEvent. If character is valid character(according to my conditions) then it should be inserted otherwise it shouldn't be. I've searched but I couldn't find a way to cancel the event in case character is invalid. In C# this is possible but in java is there any way to cancel the event so that character isn't inserted in jtextarea?
here is some simple code using evt.consume() but it's not working
private void txtArea1KeyPressed(java.awt.event.KeyEvent evt) {
evt.consume();
return;
}
Don't use KeyListeners with text components, There is no guarantee in what order the events may be triggered, so the key may already have updated the underlying Document by the time your listener is notified.
It also doesn't deal with what happens when the user pastes text into the field or the field is update by the program
Instead, use a DocumentFilter, which is doesn't to allow to filter out content been pushed to the components underlying Document model
See Implementing a Document Filter and DocumentFilter Examples
End your event handler with
event.consume()
event.consume() docs

Bind JButton text to property

I am Swing novice. I made a small app using JTree, JCheckBox, JList and JButton. I am using the associated model classes to store the presentation state. Works fine, except for the JButton. I'd like to update the button's text according to a property of my model. Basically, it will change from log in/to log off whether the user is currently logged in. Unfortunatly, I can't find a setText method in the ButtonModel interface. Can anybody tell me how to do this ?
Thanks. PW.
For a JButton you can use an Action as your model. The Action interface defines keys which are used by the JButton to retrieve its state from the Action, including one for the text: Action.NAME
If you look at the source code, the constructor with the Action (or simply the setAction method) uses more information from the Action then what can be retrieved from a ButtonModel. Same for other constructors, e.g.
public JButton(String text, Icon icon) {
// Create the model
setModel(new DefaultButtonModel());
// initialize
init(text, icon);
}
So it seems that you are correct in your assessment that the ButtonModel does not contain all information used to visualize a typical JButton

capturing input from JTextInput when user doesn't press Enter

In my Swing app, I have a screen that has a bunch of JTextFields. Each JTextField uses an ActionListener's actionPerformed method to copy the user-entered text to my data model object.
This method seems to only be called if the user presses Enter. How can I copy the user-entered text to my data model object if the user doesn't press Enter but instead 1) tabs between fields or 2) uses the mouse to click from one field to the next?
If you only want to perform an action when the user moves away from the field (not on every character changing in the field) then listen to the focus events:
JTextField textField = ...
textField.addFocusListener(new FocusAdapter(){ void focusLost(FocusEvent e)
{ doSomething(); } );
You might want to take a look at JFormattedTextField which handles this kind of thing for you.
muJTextField.addFocusListener(/* focus listener here */); for focus changes
myJTextField.getDocument().addDocumentListener(/* document listener here */); for document changes
For document changes use changeUpdate()
the problem with mouseclick, is that the component you click on must grab focus, else focus lost will not be called...
i had the same problem, so i used a timer to commit my code, every x milliseconds...if you sure that focus lost will be called when you click on some other component, a simple focus listener will do the trick...

focus issue using a JComboBox as a cell editor in a JTable

I'm having issues with the following code, where I use a JComboBox to change a String value in a table cell. The JComboBox works fine, but if I click in the box and then click away without selecting anything the JComboBox's dropdown remains visible, even if I delete the row. Clicking on another Swing component like a JButton often causes it to go away, but not always.
TableColumn col = myTable.getColumnModel().getColumn(0);
JComboBox eq = new JComboBox();
eq.addItem("==");
eq.addItem("!=");
DefaultCellEditor editor = new DefaultCellEditor(eq);
col.setCellEditor(editor);
Edit:
I had neglected to mention that earlier I set:
myTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
If I comment this line out or set it false, then clicking on other Swing components does NOT cause the box to vanish. With it in, clicking on anything that takes focus causes the box to go away, making the problem less annoying but possibly masking the cause of the behavior.
Am I doing something wrong here, or forgetting a step? Alternately, is there a way to force it to close itself?
Thanks!
To understand this you'll need to understand what goes on with an editable table. A short bit of theory:
Every cell has a potential renderer and editor. The renderer just tells the cell how to draw and does not interact with events. The editor however is a component that can interact with events. When an event happens that triggers an edit, the editor component is added on top of the table. When the edit finishes, the component is removed.
In order to get the component to go away, you'll have to make sure the cell is not still in the "editing" state. This is why terminateEditOnFocusLast causes the JComboBox to vanish. If you want other things to get the box to go, you'll need to probably call removeEditor() in response to certain events, possibly focus, or cell selection.
To really get a handle on what happens I'd recommend having a quick look at the source code to removeEditor(), editCellAt() etc., and maybe step through once in a debugger. It's possible you've overridden some of the event handling code, or are calling it when you shouldn't. The editor/event handling code in JTable is fairly fragile, and it's quite easy by accident to get calls to happen in the wrong order with funny side effects.
Also, Java very subtly changed the event and focus behaviour of JTable between versions once, I think it was between 1.4 and 1.5, when the focus handling for swing changed. So the first thing I'd recommend trying is your code with a different Java version. The bug may have been caused by Sun (some of our complicated editor code had to be changed) and if it differs between releases it is easier to report to Sun.
I know this question is old but for reference here is my solution. I extend the DefaultCellEditor and listen for the JComboBox to be canceled then force the editor to cancel.
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
public class ComboBoxCellEditor extends DefaultCellEditor {
public ComboBoxCellEditor(JComboBox comboBox) {
super(comboBox);
comboBox.addPopupMenuListener(new PopupMenuListener() {
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
}
public void popupMenuCanceled(PopupMenuEvent e) {
cancelCellEditing();
}
});
}
}
Then ...
DefaultCellEditor editor = new ComboBoxCellEditor(combobox);
column.setCellEditor(editor);

Categories