Detecting if the selected JComboBox item is enabled or disabled - java

I have a JComboBox filled with some random items, that can change at run time. I have set them as JLabels using getListCellRendererComponent(...). I have also set some of those Jlabels as setEnabled( false ).
When user selects an item from that JComboBox, is there a way i can detect if it is enabled or disabled ?

The renederer just paints them so there will not be an isEnabled value to check. Instead use the same logic that the cellRenderer uses to determine if a value is allowed, when you are looking at the selection. Or possibly remove those values from the list instead of making the disabled.

The items in the list/combo (or any of the collections components) are supposed to be representations of business objects with some state. Then implement renderers which are aware of the different state and make them (the renderer) map the state into appropriate visual representation.
// the item
public class Valve {
private boolean open;
private boolean canOperate;
private Point location;
// getters and methods as appropriate
public boolean isOpen() { ...
...
}
// custom renderer
Component getListCellRendererComponent(....) {
// normal config, assuming you subclass DefaultListCellRender
super.getListCellRendererComponent(...)
if (value instanceof Valve) {
configFromValve((Valve) value)
}
return this;
}
private void configFromValve(Valve valve) {
setText("P: (" + valve.location().x + "," + valve.getLocation().y + ")");
setIcon(valve.isOpen() ? openIcon : closedIcon);
setEnabled(valve.canOperate());
}

Related

JavaFX8: CheckboxListCell in ListView unchecks cells when scrolling [duplicate]

I am using cell factory for listview with checkboxes like:
listView.setCellFactory(CheckBoxListCell.forListView(new Callback < Bean, ObservableValue < Boolean >> () {
#Override
public ObservableValue < Boolean > call(Bean item) {
BooleanProperty observable = new SimpleBooleanProperty();
observable.addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
if (!beanChoices.contains(item.toString())) {
beanChoices.add(item.toString());
observable.setValue(true);
//listView.scrollTo(listView.getItems().size() - 1);
}
} else if (wasSelected) {
if (beanChoices.contains(item.toString())) {
beanChoices.remove(item.toString());
observable.setValue(false);
}
}
});
/* [Code] which compares values with bean item string value and select observable to true for that for edit mode
but here the observer not called for beanItem that are under scrollpane of listview. But on scroll it gets called. */
return observable;
}
}));
It works fine but not for all cases.
Case: When I have say more than 10 entries, the scrollpane comes. Say I have beanChoices to be checked that are at 8 or 9 index(you have to scroll to view them). The listener is not called for the items not visible(that are under scrollpane). On Debug, I found that listener is called when I scroll down.
Problem: when I get checked values from beanChoices for above case, it return empty.
Detail: I have beanChoices which I need to make checked for listview items (edit mode). When I update without changing anything. (Assume that the value which is under the scrollpane of listview will be selected and added to beanChoices)
The Callback is used to retrieve the property for the checked state when the item is associated with a cell. The item may be removed from a cell and put in a new one at any time. This is how ListView (and similar controls like TableView) works. CheckBoxListCell simply gets the checked state property every time a new item is associated with the cell.
The return value is also used to set the initial state of the CheckBox. Since you do not properly initialize the property with the correct value the initial state is not preserved.
Also note that it makes little sense to update the value of the property to the new value in the change listener. It happens anyway.
Since BooleanProperty is a wrapper for primitive boolean the possible values are true and false; the ChangeListener only gets called when !Objects.equals(oldValue, newValue) you can be sure that isNowSelected = !wasSelected.
Of course you also need to return the value:
#Override
public ObservableValue < Boolean > call(Bean item) {
final String value = item.toString();
BooleanProperty observable = new SimpleBooleanProperty(beanChoices.contains(value));
observable.addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
beanChoices.add(value);
} else {
beanChoices.remove(value);
}
});
return observable;
}
I also recommend using a Collection of Beans instead of relying on the string representation of the objects. toString many not produce unique results and Beans.equals would be the better choice to compare the objects.

How to display two fields in gxt ComboBox?

I have an object with two essential fields that must be displayed for the user together in ComboBox something kind of:
MyObject {
Long id;
Integer from;
Integer to;
...
}
My properties look like :
MyObjectProperties {
ModelKeyProvider<MyObject> id();
LabelProvider<MyObject> from();
LabelProvider<MyObject> to();
...
}
I'm trying to display **from - to** in each combobox cell, is it possible using GXT 3 ComboBox?
That's not working for me because i can't use LabelProvider for an int and can't merge two wroperties in same field!
LabelProvider is in fact the only clean way to go, but you need only one LabelProvider:
LabelProvider<MyObject> labelProvider = new LabelProvider<MyObject>(){
#Override
public String getLabel(MyObject item){
return item.getFrom() + " - " + item.getTo();
}
}
And assign it at ComboBox creation time:
ComboBox<MyObject> cb = new ComboBox<MyObject>(store, labelProvider);

Fast replacement for JComboBox / BasicComboBoxUI?

I've got a JComboBox that potentially can have thousands of items. They're sorted, and there's find-as-you-type, so in principle it's not completely unusable.
In practice, it's pretty unusable with just a couple of hundred items. I managed to improve the initial display performance using setPrototypeDisplayValue(), but BasicListUI still insists on configuring the list cell renderer for every item in the box (see BasicListUI.updateLayoutState()).
This, or something like it, is apparently a known issue to Sun; it has been for going on eight years now, so I'm not holding my breath.
Short of implementing my own UI, has anyone got a workaround?
JList might be a better choice, as it uses a fly-weight approach to rendering and appears to support find-as-you-type.
If you use JComboBox, add entries to the model before the component itself starts listening. This SortedComboBoxModel uses a simple insertion sort that is acceptable for a few thousand entries:
class SortedComboBoxModel extends DefaultComboBoxModel {
/** Add elements by inserting in lexical order. */
#Override
public void addElement(Object element) {
this.insertElementAt(element, 0);
}
/** Insert in lexical order by name; ignore index. */
#Override
public void insertElementAt(Object element, int index) {
String name = element.toString();
for (index = 0; index < this.getSize(); index++) {
String s = getElementAt(index).toString();
if (s.compareTo(name) > 0) {
break;
}
}
super.insertElementAt(element, index);
}
}
Here's the hack that I came up with. The drawbacks are:
if you want to maintain the look and feel, you have to separately subclass each BasicComboBoxUI extension you care about
you have to use reflection to load your UI classes, since (for instance) a subclass of WindowsComboBoxUI won't load on Linux
it won't work with L&Fs (e.g. MacOS?) that don't extend BasicComboBoxUI
it makes assumptions about the ListCellRenderer that may not always be warranted
I'm still open to cleaner solutions.
class FastBasicComboBoxUI extends BasicComboBoxUI {
#Override
public void installUI(JComponent c) {
super.installUI(c);
Object prototypeValue = this.comboBox.getPrototypeDisplayValue();
if (prototypeValue != null) {
ListCellRenderer renderer = comboBox.getRenderer();
Component rendererComponent = renderer
.getListCellRendererComponent(this.listBox,
prototypeValue, 0, false, false);
if (rendererComponent instanceof JLabel) {
// Preferred size of the renderer itself is (-1,-1) at this point,
// so we need this hack
Dimension prototypeSize = new JLabel(((JLabel) rendererComponent)
.getText()).getPreferredSize();
this.listBox.setFixedCellHeight(prototypeSize.height);
this.listBox.setFixedCellWidth(prototypeSize.width);
}
}
}
}
I'm still open to cleaner solutions.
Later
Turns out this only solved some of the problems. Initial display of a combo box with a large number of items could still be really slow. I had to make sure the popup list box immediately gets a fixed cell size, by moving the code into the ComboPopup itself, as follows. Note that, as above, this depends on the prototype value.
#Override
protected ComboPopup createPopup() {
return new BasicComboPopup(comboBox) {
#Override
protected JList createList() {
JList list = super.createList();
Object prototypeValue = comboBox.getPrototypeDisplayValue();
if (prototypeValue != null) {
ListCellRenderer renderer = comboBox.getRenderer();
Component rendererComponent = renderer
.getListCellRendererComponent(list, prototypeValue, 0, false, false);
if (rendererComponent instanceof JLabel) {
// Preferred size of the renderer itself is (-1,-1) at this point,
// so we need this hack
Dimension prototypeSize = new JLabel(((JLabel) rendererComponent)
.getText()).getPreferredSize();
list.setFixedCellHeight(prototypeSize.height);
list.setFixedCellWidth(prototypeSize.width);
}
}
return list;
}
};
}

CheckboxCellEditor shows text and not a check box

I'm using the following
org.eclipse.jface.viewers.CheckboxCellEditor.CheckboxCellEditor(Composite parent)
I'm creating a table viewer with cellEditors and doing the following
CellEditor[] editors = new CellEditor[columnNames.length];
editors[7] = new CheckboxCellEditor(table);
I have a CellModifier that has the following
public Object getValue(Object element, String property) {
Object result = null;
...
result = Boolean.valueOf(task.isDfRequested());
return result;
}
public void modify(Object element, String property, Object value) {
item.isSelected(((Boolean)value).booleanValue());
}
Finally I have a LabelProvider that has the following
public String getColumnText(Object element, int columnIndex) {
String result = "";
try {
result = Boolean.toString(item.isSelected());
} catch (Exception ex) { }
break;
However, in my UI instead of having a check box I have the word true or false && clicking it results in switching state to false or true. Any ideas on why I don't have a checkbox??
I've searched in the source code of CheckboxCellEditor class and in the constructor the control associated to the CellEditor is created in the createControl(Composite parent) method. This method is abstract in CellEditor class and it's implemented like this in CheckboxCellEditor:
protected Control createControl(Composite parent) {
return null;
}
So a control is not created, that's why you don't see the checkbox. In the documentation of the Class you can read:
Note that this implementation simply
fakes it and does does not create any
new controls. The mere activation of
this editor means that the value of
the check box is being toggled by the
end users; the listener method
applyEditorValue is immediately called
to signal the change.
I solved this using a ComboBoxCellEditor with yes and no items.
Regards.
Well, I have no idea how SWT works or what component you are even talking about.
But I do know that when using Swing you can have custom editors for a column in a JTable. If you don't tell the table the class of data for the column then the toString() method of the data is invoked. But if you tell the table that Boolean data is displayed in the column then the table will use the check box editor.
Sounds like a similiar symptom, but I don't know your particular solution.
What I've decided to do is to just implement a dirty hack others have been using.
Create two images of check boxes, one checked the other not checked. Switch the state between the two based on the boolean.
It's not perfect, but for now it gets the job done

Can a Jtable save data whenever a cell loses focus?

The high level: I have a JTable that the user can use to edit data.
Whenever the user presses Enter or Tab to finish editing, the data is saved (I'm asusming that "saved" really means "the TableModel's setValueAt() method is called".)
If the user leaves the cell in any other way after making an edit, the new data is not saved and the value stays the way it was. So, for example, if the user changes a value and then clicks on some other widget on the screen, the change doesn't "stick."
I believe that this is the default behavior for a JTable full of Strings, yes?
For a variety of reasons, the desired behavior is for the cell to save any and all edits whenever the user leaves the cell. What's the best/right way to get Swing to do this?
Table Stop Editing explains whats happening and gives a couple simple solutions.
One of the simple solutions proposed
table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
is good only for String columns. The problem is if I have, for example, Float type of the column being edited, enter an empty string in corresponding cell and then click on any other control of the window – Java throws NullPointerException in CellEditorRemover.propertyChange() method of JTable.java. It uses getCellEditor() call to stop or cancel editing but it returns null in this case. If the value entered is not empty or if I remove terminateEditOnFocusLost flag everything is fine. Probably, the situation described is a bug.
I hope I can provide a solution based on one of the previous posts. It’s not so trivial as I supposed before but seems to me it works.
I had to inherit my own cell editor from default cell editor and my own text field from JTextField which has FocusListener. This focus listener works fine when editing cell loses a focus, and a focus gained by another control of the window. But in the case of cell selection changes focus listener is “deaf”. That’s why I also have to remember previously valid value before editing start to restore it if the entered value will be invalid.
See the code below. Tested with Double, Float and Integer, but I hope this will also work with Byte and String.
Text field with focus listener:
public class TextFieldCell extends JTextField {
public TextFieldCell(JTable cellTable) {
super(); // calling parent constructor
final JTable table = cellTable; // this one is required to get cell editor and stop editing
this.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
}
// this function successfully provides cell editing stop
// on cell losts focus (but another cell doesn't gain focus)
public void focusLost(FocusEvent e) {
CellEditor cellEditor = table.getCellEditor();
if (cellEditor != null)
if (cellEditor.getCellEditorValue() != null)
cellEditor.stopCellEditing();
else
cellEditor.cancelCellEditing();
}
});
}
}
Default cell editor class:
class TextFieldCellEditor extends DefaultCellEditor {
TextFieldCell textField; // an instance of edit field
Class<?> columnClass; // specifies cell type class
Object valueObject; // for storing correct value before editing
public TextFieldCellEditor(TextFieldCell tf, Class<?> cc) {
super(tf);
textField = tf;
columnClass = cc;
valueObject = null;
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
TextFieldCell tf = (TextFieldCell)super.getTableCellEditorComponent(table, value, isSelected, row, column);
if (value != null) {
tf.setText(value.toString());
}
// we have to save current value to restore it on another cell selection
// if edited value couldn't be parsed to this cell's type
valueObject = value;
return tf;
}
#Override
public Object getCellEditorValue() {
try {
// converting edited value to specified cell's type
if (columnClass.equals(Double.class))
return Double.parseDouble(textField.getText());
else if (columnClass.equals(Float.class))
return Float.parseFloat(textField.getText());
else if (columnClass.equals(Integer.class))
return Integer.parseInt(textField.getText());
else if (columnClass.equals(Byte.class))
return Byte.parseByte(textField.getText());
else if (columnClass.equals(String.class))
return textField.getText();
}
catch (NumberFormatException ex) {
}
// this handles restoring cell's value on jumping to another cell
if (valueObject != null) {
if (valueObject instanceof Double)
return ((Double)valueObject).doubleValue();
else if (valueObject instanceof Float)
return ((Float)valueObject).floatValue();
else if (valueObject instanceof Integer)
return ((Integer)valueObject).intValue();
else if (valueObject instanceof Byte)
return ((Byte)valueObject).byteValue();
else if (valueObject instanceof String)
return (String)valueObject;
}
return null;
}
It the code of table initialization you have to add the following:
myTable.setDefaultEditor(Float.class, new TextFieldCellEditor(new TextFieldCell(myTable), Float.class));
myTable.setDefaultEditor(Double.class, new TextFieldCellEditor(new TextFieldCell(myTable), Double.class));
myTable.setDefaultEditor(Integer.class, new TextFieldCellEditor(new TextFieldCell(myTable), Integer.class));
Hope, this will help somebody who have the same problem.
You need to add a focus listener. Given that JTable is basically a container of its cell components, you actually want the focus listener for every cell in your table that needs to behave in the way you indicated.
To do this, you will need to create custom cell editor, which wraps the cell component that has a registered focus listener. And when you get the callback for the loss of focus event, you do the data save, as you require.
This pretty much details most of what you need to do. The details of implementing the focus listener is not there, but that is fairly straightforward.
Lets say you do use a JTextComponent as your cell component. Then:
public void focusLost(FocusEvent e) {
JTextComponent cell = (JTextComponent) e.getSource();
String data = cell.getText();
// TODO: save the data for this cell
}
[p.s. edit]:
The thread that is calling you with this event is the dispatch thread. Do NOT use it for actions with high latency. But if you are just flipping bits in the heap, it should be ok.

Categories

Resources