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

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.)

Related

How can I check to see if a valueChanged event of a Swing JList was changed via click or mouse?

Here's some example code, using Groovy's swingbuilder to create the code for the valueChanged event of a JList:
mainList.valueChanged = { event ->
if (event.isAdjusting) {
index = mainList.selectedIndex
otherList.clearSelection()
otherIndex = otherList.selectedIndex
} else {
mainListSelected = true
clearJList(otherList)
}
}
I have two JList's, and this function kind of controls which list is allowed to be selected via the mainListSelected variable. We also then have to change int eindex we want to use from the selection based on whether or not it's an index from mainList or otherList
I've read about event.isAdjusting, and it only fires twice like this on a mouse click event. With this knowledge, you would think I would just move everything out of there, but I need certain things to happen differently if the mouse is what causes the event as opposed to using arrows. However, with this code, using arrow key navigation prevents the index from ever changing.

JavaFX Preventing TableMenuButton from hiding all columns

I want to use Table Menu Button (table.setTableMenuButtonVisible(true);) for hiding and showing specified columns in TableView. When I deselect all columns, [+] button hides, "No columns in table" pops in and user is unable to show any column.
I've tried to prevent hiding all columns by listening to table.getVisibleLeafColumns() and showing last hidden column, but then in ChoiceBox from Menu Button this column is unselected.
Definitely a bug (you might consider reporting it in fx' jira). The hack-around you mentioned in your question seems to work with a little trick borrowed from Swing: delay the reversion of visibility to some future:
ListChangeListener<? super TableColumn> visibleColumnsListener = c -> {
while (c.next()) {
// very last remove
if (c.wasRemoved() && !c.wasReplaced()) {
TableColumn column = c.getRemoved().get(0);
// delay reverting visibility
Platform.runLater(() -> {
column.setVisible(true);
});
}
}
};
It may be dirtier than its analogue in Swing, though, execution of the runnable is at "some unspecified time in future" and doesn't state its relation to normal (originating from the ui) events.
Reported as RT-38907 and just fixed (was duplicate: RT-37616), should bubble up in 8u40 ea in a week or two.

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

Java Swing - make two JLists "siblings" - i.e. only one item in either can be selected

I have a JPanel which contains two JLists - both can have an item in them selected - as expected.
What I'd love to be able to do is have it so that only one item in either to be selected
Can anyone help me with either
a) "marrying" them so this can be the case
b) giving me some tips for the best practice to write listeners which can preside over them both and unselect all the elements of one when the other is selected - I'd rather avoid this if possible as I can see it getting ugly!!
Thanks :)
I think the best solution, also for the user, is putting a radio button next with a category label to each list, so you clearly disable the other each time you select one.
I can imagine the user clicking values on the first list, then clicking on the next one and seeing all the values he clicked are gone, with logical frustration...
Then when you are taking the values from the form, just take the enabled ones
The listener is not that difficult nor ugly to write. I would
make sure the lists only support single selection
add the same selection listener to both lists' selection model
This listener can be implemented as
public void valueChanged(ListSelectionEvent e){
if ( e.isAdjusting()) return;
ListSelectionModel sourceSelectionModel = (ListSelectionModel) e.getSource();
if ( !sourceSelectionModel.isSelectionEmpty() ){
//still need to implement the findOtherSelectionModel method
ListSelectionModel other = findOtherSelectionModel( sourceSelectionModel );
other.clearSelection();
}
}
Note that clearing the selection will trigger the listener again, but due to the isSelectionEmpty check you will not end up with a loop. Another approach would be to disable the listener (e.g. with a boolean flag) right before you call clearSelection on the other list.

How do I make a listener that fires when the USER selects an item in a JComboBox

I'm looking for a listener that fires ONLY when the user, the one who's using the program, selects an item in the JComboBox. I don't want to use ActionListener or ItemListener because those also fire when I select an item through the program. And I can't use MouseListener either because it only fires when I click the JComboBox, not when I select an item.
I was wondering what the easiest way to do this is? Currently, my solution is messy. When I change the selected item of the jcombobox through code, I set a flag to true. And in my action listener, it only executes if the flag is false.
A) I would recommend you to temporarily remove the listener when you perform the selection programatically.
B) If your programatic change is not an effect of another GUI event you could solve it the following ugly/non-robust/error-prone/"hacky" way: Check EventQueue.isEventDispatchThread() to find out if the click was triggered by the GUI thread (the user).
C) (Oops I just reread your question and saw that you've already discovered the method described below. Basically I would say that this (or the the method described above) is your best alternative.)
Another option is to have a boolean flag called something like nonUserSelection which you set to true before you select a value programatically and reset to false afterwards. In the action listener you simply add an
if (nonUserSelection)
return;

Categories