Java: do file menu clicks take focus? - java

I had a problem with tables whereby a cell being edited did not store its new value until the user pressed enter or tab, whereas I wanted it to commit the value as soon as focus was lost as I was finding that selecting 'file -> save' whilst editing a cell caused that cell's data to be lost.
I found that you can simply set
table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
which works well for every change of focus except when the file menu is selected, in which case the cell stays in edit mode.
My question is, does setting this property not behave as one might expect it to, or does focus work differently for menu items by default in Java?

If you don't want to make the menu items focusable then you will need to add code like the following in your Save Action:
if (table.isEditing())
table.getCellEditor().stopCellEditing();

If you look at the JMenuItem source code there is a function call being made to initFocusability() witch in turns calls setFocusable(false).
Java Doc for initFocusability()
Inititalizes the focusability of the the JMenuItem.
JMenuItem's are focusable, but subclasses may want to be,
this provides them the and invoke something else, or nothing at all.
Refer to {#link javax.swing.JMenu#initFocusability} for the motivation
of this.
Java Doc from JMenu.JMenu#initFocusability
Overriden to do nothing. We want JMenu to be focusable, but JMenuItem
doesn't want to be, thus we override this do nothing. We don't invoke
setFocusable(true) after super's constructor has completed as this has
the side effect that JMenu will be considered traversable via the
keyboard, which we don't want. Making a Component traversable by the
keyboard after invoking setFocusable(true) is OK, as setFocusable is
new API and is speced as such, but internally we don't want to use it
like this else we change the keyboard traversability.
So it sounds like you need to call setFocusable(true);.

I have found that a combination of the two answers above has solved this problem. I have a focusable menu bar, and have added a DefaultCellEditor with an overridden getTableCellEditorComponent method that adds a focus listener to stop editing:
table.setDefaultEditor(String.class, new DefaultCellEditor(new JTextField()){
#Override
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected,
int row, int column)
{
Component c = super.getTableCellEditorComponent(table, value, isSelected, row, column);
c.addFocusListener(new FocusListener()
{
public void focusGained(FocusEvent e)
{
}
public void focusLost(FocusEvent e)
{
stopCellEditing();
}
});
return c;
}
});

Related

How to add a table listener to a JTable?

I am having problems with fixing something in my program. Basically I know how to use action Listeners but there is no option to add one to a JTable. How is this done?
Basically I want to add an action Listener to my table so that every time a value is changed it will update that field in my data base.
I.E.
JTable.addActionListener (new ActionListener) {
// text is changed
updateDataBase();
};
You should add a listener to the TableModel:
yourtableObject.getModel().addTableModelListener(new TableModelListener() {
public void tableChanged(TableModelEvent e) {
// your code goes here, whatever you want to do when something changes in the table
}
});
TableModelEvent contains row and column number and type of modification.
TableModelEvent is used to notify listeners that a table model has changed.
Start by taking a look at How to Use Tables
What you will want to do is register a TableModelListener with the JTable's model and monitor for changes there
You may also find How to Write a Table Model Listener of some use
The kind of thing you are look for is
TableModel#getType equals TableModelEvent.UPDATE
TableModel#getFirstRow and TableModel#getLastRow are typically equals (singularly that a single row was update), this may or may not be relevant, that's up to you to decide
TableModel#getColumn is not equal to TableModelEvent.ALL_COLUMNS, this signifies that a single cell was updated. Again, this may or may not be important, but if the cell was edited by the user, this will be set
Take a look at javax.swing.event.TableModelEvent for more details
If you want to have an event when there is a change in selection you can use:
table.getSelectionModel()addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent l) {
//action
}
});
Source: http://www.java2s.com/Tutorial/Java/0240__Swing/TableSelectionEventsandListeners.htm

Prevent Java Swing JTable losing focus when invalid data entered

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();
}

JTree, always display all nodes in "edit mode"

I'm displaying a tree of custom objects, and I've got custom custom CellTreeEditor and CellTreeRenderer set.
Now what I really want is to always display all objects as in "edit mode". Right now I have the CellTreeRenderer.getTreeCellRendererComponent() and CellTreeEditor.getTreeCellEditorComponent() implemented almost identically. This kind of works, but I still have to click a node to focus it before I can do any editing.
Is there any more sensible way of doing this, perhaps like saying no renderer should never be used, defaulting to my CellTreeEditor?
******UPDATE****
To clearify: What I have is a tree looking like this (and yes, it also looks like crap, but that's beside the point):
Right now, I accomplish this by having a renderer and an editor that returns identical components from getTreeCell[Renderer|Editor]Component().
If I click on the down-arrow on the ComboBox supplied by the renderer, it will flicker slighty as it opens the dropdown, but then be interrupted and replaced by my editor component. This means I have to click it again to open the dropdown. This is the behaviour I want to avoid.
Expanding my comment: no, you don't want to have your editor shared across cells (nasty thingies start to happen) Instead, add a TreeCellListener which listens for changes in the lead (aka: focused) selection path and then explicitly start editing on that path
final JXTree tree = new JXTree();
tree.setEditable(true);
tree.expandAll();
TreeSelectionListener l = new TreeSelectionListener() {
#Override
public void valueChanged(TreeSelectionEvent e) {
final TreePath path = e.getNewLeadSelectionPath();
if (path != null) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
tree.startEditingAtPath(path);
}
});
}
}
};
tree.addTreeSelectionListener(l);
The trick to really make it work is the usual: wrap the custom reaction into an invokeLater to be sure the tree's internal update is complete

How to create a drop-down list in java swing with multiple item selection?

I'm aware of JList and JComboBox. I need the combo box drop down functionality with multiple selection functionality that JList provides.
This is because the contents of the list are too huge to be displayed using a simple list. I also need to select multiple items, otherwise I would have been content with JComboBox.
Any suggestions?
When using multi-select, it's better to use a list than a combo box. As GUI metaphors go, people expect a combo box to be single select, whereas lists can be either.
the contents of the list are too huge to be displayed using a simple list
Place the JList in a JScrollPane. You can call setVisibleRowCount(int) on the JList to specify how many rows at a time should be shown.
You can make a custom cell renderer for the combobox and add checkboxes to that components, so you can check and uncheck them. You have to make something like this:
public class MyComboBoxRenderer implements ListCellRenderer {
private String[] items;
private boolean[] selected;
public MyComboBoxRenderer(String[] items){
this.items = items;
this.selected = new boolean[items.lenght];
}
public Component getListCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int index) {
// Create here a JLabel with the text
// Create here a JCheckBox
// Add them to a layoutmanager
return this;
}
public void setSelected(int i, boolean flag)
{
this.selected[i] = flag;
}
}
If your data has a hierarchical character, consider NetBeans' Outline component, discussed in Announcing the new Swing Tree Table and in this answer. Here's the Current Development Version of the API.
To achieve the described functionality, I finally decided to "abuse" the JMenuBar and add to it several JCheckBoxMenuItems. The GUI then perfectly fits the purpose (at least for me), it is just the handling of the ItemEvent that risks to become a bit annoying.
(finally there, I defined some bit logic over the items, and then may restrict myself to handling only one type of event)

Making a JTable cell editable - but *not* by double clicking

I am trying to add a column to a JTable with the following behaviour (similar to Windows Explorer and similar shells):
The cell can be clicked once to select it, as usual.
The cell can be double-clicked to perform a separate action (launching an external program.)
The cell value (a string) can still be edited, by single-clicking a second time (after a pause) or by pressing F2 when the cell is highlighted.
Double-clicking must not trigger editing of the cell, but I would like to leave any other default editing triggers operational if possible.
I have tried adding a MouseListener to the table, and consuming all MouseEvents, but this does not work - if I return true from isCellEditable() then my listener never receives any click events but if I return false then F2 no longer works.
Can this be achieved using only event listeners? I would prefer not to mess with the PLAF functions if I can avoid it.
The DefaultCellEditor has a setClickCountToStart() method to control mouse clicks for editing. The default is 2. Changing this will have no effect on F2 functionality.
Therefore you can set editing to be a triple click.
Not sure exactly how to handle two single clicks to start editing but I guess you would use a Timer to keep track of the first click and then do the editing if the second single click is within you time interval.
You will have to make your own cellEditor and ovveride
public boolean isCellEditable( EventObject e )
You can distinguish between single and double click with the clickCount on the eventObject
If its a single Click and its on a selected cell you can return true otherwise return false;
retrieve row and column with
int row = ( (JTable) e.getSource() ).rowAtPoint(e.getPoint());
int column = ( (JTable) e.getSource() ).columnAtPoint(e.getPoint());
to enable F2 you can add custom inputMap en actionMap entries
similar too
table.getInputMap().put(KeyStroke.getKeyStroke("DOWN"), "doMyArrowDown");
table.getTable().getActionMap().put("doMyArrowDown", new ArrowDownAction());
and from your action you can then fire the cellediting yourself
table.editCellAt(row, column );
I have solved this by wrapping the existing CellEditor with a Proxy and intercepting calls to isCellEditable, returning false for all mouse events and delegating all other calls to the original CellEditor.
This is slightly more complex than camickr's solution but works for all editors (I have 4 in all.)

Categories