Can i use JCheckbox to show "mixed state" - java

In windows it is possible to show a grayed out JCheckbox, to show that the collection of data which it represents not all items have the same value.
Is this even possible with a JCheckBox?
How do i go about this?
(Hoping there's a way to not override it)
Thanks

JIDE Common Layer has a TristateCheckBox.

It's possible with some of work.
I have this code from some years ago. Is based in some examples I found in internet, but I cannot find any reference to the original creator, so I apologize
import javax.swing.*;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;
import java.awt.event.*;
/**
* Maintenance tip - There were some tricks to getting this code
* working:
*
* 1. You have to overwite addMouseListener() to do nothing
* 2. You have to add a mouse event on mousePressed by calling
* super.addMouseListener()
* 3. You have to replace the UIActionMap for the keyboard event
* "pressed" with your own one.
* 4. You have to remove the UIActionMap for the keyboard event
* "released".
* 5. You have to grab focus when the next state is entered,
* otherwise clicking on the component won't get the focus.
* 6. You have to make a TristateDecorator as a button model that
* wraps the original button model and does state management.
*/
public class TristateCheckBox extends JCheckBox {
/** This is a type-safe enumerated type */
public static class State { private State() { } }
public static final State NOT_SELECTED = new State();
public static final State SELECTED = new State();
public static final State DONT_CARE = new State();
private final TristateDecorator model;
public TristateCheckBox(String text, Icon icon, State initial){
super(text, icon);
// Add a listener for when the mouse is pressed
super.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
grabFocus();
model.nextState();
}
});
// Reset the keyboard action map
ActionMap map = new ActionMapUIResource();
map.put("pressed", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
grabFocus();
model.nextState();
}
});
map.put("released", null);
SwingUtilities.replaceUIActionMap(this, map);
// set the model to the adapted model
model = new TristateDecorator(getModel());
setModel(model);
setState(initial);
}
public TristateCheckBox(String text, State initial) {
this(text, null, initial);
}
public TristateCheckBox(String text) {
this(text, DONT_CARE);
}
public TristateCheckBox() {
this(null);
}
/** No one may add mouse listeners, not even Swing! */
public void addMouseListener(MouseListener l) { }
/**
* Set the new state to either SELECTED, NOT_SELECTED or
* DONT_CARE. If state == null, it is treated as DONT_CARE.
*/
public void setState(State state) { model.setState(state); }
/** Return the current state, which is determined by the
* selection status of the model. */
public State getState() { return model.getState(); }
public void setSelected(boolean b) {
if (b) {
setState(SELECTED);
} else {
setState(NOT_SELECTED);
}
}
/**
* Exactly which Design Pattern is this? Is it an Adapter,
* a Proxy or a Decorator? In this case, my vote lies with the
* Decorator, because we are extending functionality and
* "decorating" the original model with a more powerful model.
*/
private class TristateDecorator implements ButtonModel {
private final ButtonModel other;
private TristateDecorator(ButtonModel other) {
this.other = other;
}
private void setState(State state) {
if (state == NOT_SELECTED) {
other.setArmed(false);
setPressed(false);
setSelected(false);
} else if (state == SELECTED) {
other.setArmed(false);
setPressed(false);
setSelected(true);
} else { // either "null" or DONT_CARE
other.setArmed(true);
setPressed(true);
setSelected(true);
}
}
/**
* The current state is embedded in the selection / armed
* state of the model.
*
* We return the SELECTED state when the checkbox is selected
* but not armed, DONT_CARE state when the checkbox is
* selected and armed (grey) and NOT_SELECTED when the
* checkbox is deselected.
*/
private State getState() {
if (isSelected() && !isArmed()) {
// normal black tick
return SELECTED;
} else if (isSelected() && isArmed()) {
// don't care grey tick
return DONT_CARE;
} else {
// normal deselected
return NOT_SELECTED;
}
}
/** We rotate between NOT_SELECTED, SELECTED and DONT_CARE.*/
private void nextState() {
State current = getState();
if (current == NOT_SELECTED) {
setState(SELECTED);
} else if (current == SELECTED) {
setState(DONT_CARE);
} else if (current == DONT_CARE) {
setState(NOT_SELECTED);
}
}
/** Filter: No one may change the armed status except us. */
public void setArmed(boolean b) {
}
/** We disable focusing on the component when it is not
* enabled. */
public void setEnabled(boolean b) {
setFocusable(b);
other.setEnabled(b);
}
/** All these methods simply delegate to the "other" model
* that is being decorated. */
public boolean isArmed() { return other.isArmed(); }
public boolean isSelected() { return other.isSelected(); }
public boolean isEnabled() { return other.isEnabled(); }
public boolean isPressed() { return other.isPressed(); }
public boolean isRollover() { return other.isRollover(); }
public void setSelected(boolean b) { other.setSelected(b); }
public void setPressed(boolean b) { other.setPressed(b); }
public void setRollover(boolean b) { other.setRollover(b); }
public void setMnemonic(int key) { other.setMnemonic(key); }
public int getMnemonic() { return other.getMnemonic(); }
public void setActionCommand(String s) {
other.setActionCommand(s);
}
public String getActionCommand() {
return other.getActionCommand();
}
public void setGroup(ButtonGroup group) {
other.setGroup(group);
}
public void addActionListener(ActionListener l) {
other.addActionListener(l);
}
public void removeActionListener(ActionListener l) {
other.removeActionListener(l);
}
public void addItemListener(ItemListener l) {
other.addItemListener(l);
}
public void removeItemListener(ItemListener l) {
other.removeItemListener(l);
}
public void addChangeListener(ChangeListener l) {
other.addChangeListener(l);
}
public void removeChangeListener(ChangeListener l) {
other.removeChangeListener(l);
}
public Object[] getSelectedObjects() {
return other.getSelectedObjects();
}
}
}

My colleague who this question came from thought of this;
Create a dummy JCheckBox which is disabled and selected. set the same size as the real one.
Create an Icon which' paint method actually paints the dummy JCheckbox.
Set the original JCheckBox' Icon to the one painting the dummy.
Remove the icon as soon as the JCheckBox is clicked.
++ No overridden JCheckBox
-- not a real tri-state Combo
I think he's satisfied.
Thanks for the help

Related

Editable JComboBox firing ActionListener when losing focus

I am writing a class (UIPromptComboBox) that extends JComboBox. The combobox is editable and for one application of the class it is implemented with a controlling ActionListener.
Currently, when the combobox is edited it fires the ActionListener which is good. However this ActionListener is also fired when I deselect the combobox and I cannot distinguish between the two events nor do I want it to fire when the combobox is deselected.
Implementing Class
private void addUIField() {
// Initialise and place combobox
this.myGuiTextField = new UIPromptComboBox();
myGuiTextField.setSize(COMBO_WIDTH, defaultHeight);
GuiUtils.positionControl(myPanel, myGuiTextField, myTop, PROMPT_X_LOC);
//Add action listener
myGuiTextField.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
if (evt.getActionCommand().equals("comboBoxEdited")) {
newUIcreated((UIPromptComboBox) evt.getSource());
}
}
private void newUIcreated(UIPromptComboBox alteredGuiTextField) {
try {
UIPrompt uip = alteredGuiTextField.getUIPrompt(((PowerPointTextItem) myPPTRef).getValue());
if (!simInfo.isInPrompts(uip)) {
simInfo.addUIPrompt(uip);
alteredGuiTextField.addNewUIPrompt(uip);
}
} catch (MissingPowerpointItem ex) {
Exceptions.printStackTrace(ex);
}
}
});
}
Class that extends JComboBox
public class UIPromptComboBox extends JComboBox {
public UIPromptComboBox(UIPrompt[] items) {
super(items);
this.setEditable(true);
}
public UIPromptComboBox() {
this.setEditable(true);
this.setEnabled(false);
}
/**
* returns either the selected UI prompt or a new prompt using the example
* text
*
* #param exampleText only used if new prompt is created
* #return UI prompt selected
*/
public UIPrompt getUIPrompt(String exampleText) {
UIPrompt uIPrompt = null;
Object returnedItem = this.getSelectedItem();
if (returnedItem instanceof UIPrompt) {
uIPrompt = (UIPrompt) returnedItem;
} else if (returnedItem instanceof String) {
uIPrompt = new UIPrompt((String) returnedItem, exampleText);
}
return uIPrompt;
}
public void addNewUIPrompt(UIPrompt newPrompt) {
ActionListener[] actionListerners = this.getActionListeners();
this.removeActionListener(this);
this.addItem(newPrompt);
this.setSelectedItem(newPrompt);
for (ActionListener al : actionListerners) {
this.addActionListener(al);
}
}
/**
* Used for displaying a report value sentence
* i.e. a string that is not associated with UI Prompts
* #param newText report value sentence
*/
public void setText(String newText) {
this.removeAllItems();
this.addItem(newText);
this.setSelectedItem(newText);
}
/**
* For when the UI prompts can be added on construction
*
* #param currentUIs list of UI promts
*/
public void addItems(UIPrompt[] currentUIs) {
this.removeAllItems();
DefaultComboBoxModel boxModel = new DefaultComboBoxModel(currentUIs);
this.setModel(boxModel);
}
}
The multiple firing due to losing focus is causing multiple objects to be created and added to the list. I think I may have implemented the ActionListener incorrectly. Thank you for your help
as you stated you only want the event to fire if the user presses enter. a better way to implement that, would be using an keylistener instead of an action listener.
myGuiTextField.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
newUIcreated((UIPromptComboBox) evt.getSource());
}
}
private void newUIcreated(UIPromptComboBox alteredGuiTextField) {
try {
UIPrompt uip = alteredGuiTextField.getUIPrompt(((PowerPointTextItem) myPPTRef).getValue());
if (!simInfo.isInPrompts(uip)) {
simInfo.addUIPrompt(uip);
alteredGuiTextField.addNewUIPrompt(uip);
}
} catch (MissingPowerpointItem ex) {
Exceptions.printStackTrace(ex);
}
}
});
this should now only fire your event newUIcreated, once the users presses enter and at no other time. replace your action listener with this
I have now finally found the issue.
The displaying of the UIPrompt included the addition of a string that sometimes contained a new line character.
The action of clicking another field was triggering the render of the UIPrompt however when this contained a newline character it was triggering the ActionListener again. This what the reason for the repeated action of comboBoxEdited.

How to commit when clicking outside an editable TableView cell in JavaFX?

I have a table cell factory responsible for creating an editable cell in a JavaFX TableView.
I'm trying to implement some added functionality to the tableview so that when the user clicks outside the editable cell a commit is made (the edited text is saved, and not discarded as per the default tableview behavior.)
I added an textField.focusedProperty() event handler, where I commit the text from the text field. However, when one clicks outside the current cell cancelEdit() gets called and calling commitEdit(textField.getText()); has no effect.
I have come to realize that once cancelEdit() is called the TableCell.isEditing() returns false and so the commit will never happen.
How can I make so that when the user clicks outside the editable cell the text is committed?
After committing an setOnEditCommit() event handler will take care of the validation and database logic. I haven't included it here since it will most likely complicate things even further.
// EditingCell - for editing capability in a TableCell
public static class EditingCell extends TableCell<Person, String> {
private TextField textField;
public EditingCell() {
}
#Override public void startEdit() {
super.startEdit();
if (textField == null) {
createTextField();
}
setText(null);
setGraphic(textField);
textField.selectAll();
}
#Override public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
#Override public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(textField.getText());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (!newValue) {
commitEdit(textField.getText());
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
Since I could not find kuaw26's source code (dead link) I developed my own solution for java 8. I found out that the TextField in the code above never receives a keyReleased event for the esc-key, therefore his code does not work.
Unfortunately I needed to duplicate code from TextFieldTableCell and CellUtils and adapt it, since TextFieldTableCell uses a private TextField and CellUtils is package protected. This is probably not the best OO way.
Here is my solution:
// package yourLib;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.Event;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.util.Callback;
import javafx.util.StringConverter;
import javafx.util.converter.DefaultStringConverter;
/**
* A class containing a {#link TableCell} implementation that draws a
* {#link TextField} node inside the cell. If the TextField is
* left, the value is commited.
*
*/
public class AcceptOnExitTableCell<S,T> extends TableCell<S,T> {
/***************************************************************************
* *
* Static cell factories *
* *
**************************************************************************/
/**
* Provides a {#link TextField} that allows editing of the cell content when
* the cell is double-clicked, or when
* {#link TableView#edit(int, javafx.scene.control.TableColumn)} is called.
* This method will only work on {#link TableColumn} instances which are of
* type String.
*
* #return A {#link Callback} that can be inserted into the
* {#link TableColumn#cellFactoryProperty() cell factory property} of a
* TableColumn, that enables textual editing of the content.
*/
public static <S> Callback<TableColumn<S,String>, TableCell<S,String>> forTableColumn() {
return forTableColumn(new DefaultStringConverter());
}
/**
* Provides a {#link TextField} that allows editing of the cell content when
* the cell is double-clicked, or when
* {#link TableView#edit(int, javafx.scene.control.TableColumn) } is called.
* This method will work on any {#link TableColumn} instance, regardless of
* its generic type. However, to enable this, a {#link StringConverter} must
* be provided that will convert the given String (from what the user typed
* in) into an instance of type T. This item will then be passed along to the
* {#link TableColumn#onEditCommitProperty()} callback.
*
* #param converter A {#link StringConverter} that can convert the given String
* (from what the user typed in) into an instance of type T.
* #return A {#link Callback} that can be inserted into the
* {#link TableColumn#cellFactoryProperty() cell factory property} of a
* TableColumn, that enables textual editing of the content.
*/
public static <S,T> Callback<TableColumn<S,T>, TableCell<S,T>> forTableColumn(
final StringConverter<T> converter) {
return list -> new AcceptOnExitTableCell<S,T>(converter);
}
/***************************************************************************
* *
* Fields *
* *
**************************************************************************/
private TextField textField;
private boolean escapePressed=false;
private TablePosition<S, ?> tablePos=null;
/***************************************************************************
* *
* Constructors *
* *
**************************************************************************/
/**
* Creates a default TextFieldTableCell with a null converter. Without a
* {#link StringConverter} specified, this cell will not be able to accept
* input from the TextField (as it will not know how to convert this back
* to the domain object). It is therefore strongly encouraged to not use
* this constructor unless you intend to set the converter separately.
*/
public AcceptOnExitTableCell() {
this(null);
}
/**
* Creates a TextFieldTableCell that provides a {#link TextField} when put
* into editing mode that allows editing of the cell content. This method
* will work on any TableColumn instance, regardless of its generic type.
* However, to enable this, a {#link StringConverter} must be provided that
* will convert the given String (from what the user typed in) into an
* instance of type T. This item will then be passed along to the
* {#link TableColumn#onEditCommitProperty()} callback.
*
* #param converter A {#link StringConverter converter} that can convert
* the given String (from what the user typed in) into an instance of
* type T.
*/
public AcceptOnExitTableCell(StringConverter<T> converter) {
this.getStyleClass().add("text-field-table-cell");
setConverter(converter);
}
/***************************************************************************
* *
* Properties *
* *
**************************************************************************/
// --- converter
private ObjectProperty<StringConverter<T>> converter =
new SimpleObjectProperty<StringConverter<T>>(this, "converter");
/**
* The {#link StringConverter} property.
*/
public final ObjectProperty<StringConverter<T>> converterProperty() {
return converter;
}
/**
* Sets the {#link StringConverter} to be used in this cell.
*/
public final void setConverter(StringConverter<T> value) {
converterProperty().set(value);
}
/**
* Returns the {#link StringConverter} used in this cell.
*/
public final StringConverter<T> getConverter() {
return converterProperty().get();
}
/***************************************************************************
* *
* Public API *
* *
**************************************************************************/
/** {#inheritDoc} */
#Override public void startEdit() {
if (! isEditable()
|| ! getTableView().isEditable()
|| ! getTableColumn().isEditable()) {
return;
}
super.startEdit();
if (isEditing()) {
if (textField == null) {
textField = getTextField();
}
escapePressed=false;
startEdit(textField);
final TableView<S> table = getTableView();
tablePos=table.getEditingCell();
}
}
/** {#inheritDoc} */
#Override public void commitEdit(T newValue) {
if (! isEditing())
return;
final TableView<S> table = getTableView();
if (table != null) {
// Inform the TableView of the edit being ready to be committed.
CellEditEvent editEvent = new CellEditEvent(
table,
tablePos,
TableColumn.editCommitEvent(),
newValue
);
Event.fireEvent(getTableColumn(), editEvent);
}
// we need to setEditing(false):
super.cancelEdit(); // this fires an invalid EditCancelEvent.
// update the item within this cell, so that it represents the new value
updateItem(newValue, false);
if (table != null) {
// reset the editing cell on the TableView
table.edit(-1, null);
// request focus back onto the table, only if the current focus
// owner has the table as a parent (otherwise the user might have
// clicked out of the table entirely and given focus to something else.
// It would be rude of us to request it back again.
// requestFocusOnControlOnlyIfCurrentFocusOwnerIsChild(table);
}
}
/** {#inheritDoc} */
#Override public void cancelEdit() {
if(escapePressed) {
// this is a cancel event after escape key
super.cancelEdit();
setText(getItemText()); // restore the original text in the view
}
else {
// this is not a cancel event after escape key
// we interpret it as commit.
String newText=textField.getText(); // get the new text from the view
this.commitEdit(getConverter().fromString(newText)); // commit the new text to the model
}
setGraphic(null); // stop editing with TextField
}
/** {#inheritDoc} */
#Override public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
updateItem();
}
/***************************************************************************
* *
* // djw code taken and adapted from package protected CellUtils. *
* *
**************************************************************************/
private TextField getTextField() {
final TextField textField = new TextField(getItemText());
// Use onAction here rather than onKeyReleased (with check for Enter),
// as otherwise we encounter RT-34685
textField.setOnAction(event -> {
if (converter == null) {
throw new IllegalStateException(
"Attempting to convert text input into Object, but provided "
+ "StringConverter is null. Be sure to set a StringConverter "
+ "in your cell factory.");
}
this.commitEdit(getConverter().fromString(textField.getText()));
event.consume();
});
textField.setOnKeyPressed(t -> { if (t.getCode() == KeyCode.ESCAPE) escapePressed = true; else escapePressed = false; });
textField.setOnKeyReleased(t -> {
if (t.getCode() == KeyCode.ESCAPE) {
// djw the code may depend on java version / expose incompatibilities:
throw new IllegalArgumentException("did not expect esc key releases here.");
}
});
return textField;
}
private String getItemText() {
return getConverter() == null ?
getItem() == null ? "" : getItem().toString() :
getConverter().toString(getItem());
}
private void updateItem() {
if (isEmpty()) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getItemText());
}
setText(null);
setGraphic(textField);
} else {
setText(getItemText());
setGraphic(null);
}
}
}
private void startEdit(final TextField textField) {
if (textField != null) {
textField.setText(getItemText());
}
setText(null);
setGraphic(textField);
textField.selectAll();
// requesting focus so that key input can immediately go into the
// TextField (see RT-28132)
textField.requestFocus();
}
}
You could do it by overriding the method commitEdit as next:
#Override
public void commitEdit(T item) {
// This block is necessary to support commit on losing focus, because
// the baked-in mechanism sets our editing state to false before we can
// intercept the loss of focus. The default commitEdit(...) method
// simply bails if we are not editing...
if (!isEditing() && !item.equals(getItem())) {
TableView<S> table = getTableView();
if (table != null) {
TableColumn<S, T> column = getTableColumn();
CellEditEvent<S, T> event = new CellEditEvent<>(
table, new TablePosition<S,T>(table, getIndex(), column),
TableColumn.editCommitEvent(), item
);
Event.fireEvent(column, event);
}
}
super.commitEdit(item);
}
This workaround comes from https://gist.github.com/james-d/be5bbd6255a4640a5357#file-editcell-java-L109
Here's how I did it - I binded the textField's text property with the text property of the cell (bidirectional).
class EditingCell<S, T> extends TableCell<S, T> {
private final TextField mTextField;
public EditingCell() {
super();
mTextField = new TextField();
mTextField.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if( event.getCode().equals(KeyCode.ENTER) )
commitEdit((T)mTextField.getText());
}
});
mTextField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if( !newValue )
commitEdit((T)mTextField.getText());
}
});
mTextField.textProperty().bindBidirectional(textProperty());
}
#Override
public void startEdit() {
super.startEdit();
setGraphic(mTextField);
}
#Override
public void cancelEdit() {
super.cancelEdit();
setGraphic(null);
}
#Override
public void updateItem(final T item, final boolean empty) {
super.updateItem(item, empty);
if( empty ) {
setText(null);
setGraphic(null);
}
else {
if( item == null ) {
setGraphic(null);
}
else {
if( isEditing() ) {
setGraphic(mTextField);
setText((String)getItem());
}
else {
setGraphic(null);
setText((String)getItem());
}
}
}
}
}
I created my own workaround (but for JavaFX 2). Main idea - transform cancelEdit() to commitEdit(). With possible validation of committed text via validator.
/** Validator. */
public interface TextColumnValidator<T> {
boolean valid(T rowVal, String newVal);
}
/**
* Special table text field cell that commit its content on focus lost.
*/
public class TextFieldTableCellEx<S> extends TextFieldTableCell<S, String> {
/** */
private final TextColumnValidator<S> validator;
/** */
private boolean cancelling;
/** */
private boolean hardCancel;
/** */
private String curTxt = "";
/** Create cell factory. */
public static <S> Callback<TableColumn<S, String>, TableCell<S, String>>
cellFactory(final TextColumnValidator<S> validator) {
return new Callback<TableColumn<S, String>, TableCell<S, String>>() {
#Override public TableCell<S, String> call(TableColumn<S, String> col) {
return new TextFieldTableCellEx<>(validator);
}
};
}
/**
* Text field cell constructor.
*
* #param validator Input text validator.
*/
private TextFieldTableCellEx(TextColumnValidator<S> validator) {
this.validator = validator;
}
/** {#inheritDoc} */
#Override public void startEdit() {
super.startEdit();
curTxt = "";
hardCancel = false;
Node g = getGraphic();
if (g != null) {
final TextField tf = (TextField)g;
tf.textProperty().addListener(new ChangeListener<String>() {
#Override public void changed(ObservableValue<? extends String> val, String oldVal, String newVal) {
curTxt = newVal;
}
});
tf.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override public void handle(KeyEvent evt) {
if (KeyCode.ENTER == evt.getCode())
cancelEdit();
else if (KeyCode.ESCAPE == evt.getCode()) {
hardCancel = true;
cancelEdit();
}
}
});
// Special hack for editable TextFieldTableCell.
// Cancel edit when focus lost from text field, but do not cancel if focus lost to VirtualFlow.
tf.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override public void changed(ObservableValue<? extends Boolean> val, Boolean oldVal, Boolean newVal) {
Node fo = getScene().getFocusOwner();
if (!newVal) {
if (fo instanceof VirtualFlow) {
if (fo.getParent().getParent() != getTableView())
cancelEdit();
}
else
cancelEdit();
}
}
});
Platform.runLater(new Runnable() {
#Override public void run() {
tf.requestFocus();
}
});
}
}
/** {#inheritDoc} */
#Override public void cancelEdit() {
if (cancelling)
super.cancelEdit();
else
try {
cancelling = true;
if (hardCancel || curTxt.trim().isEmpty())
super.cancelEdit();
else if (validator.valid(getTableView().getSelectionModel().getSelectedItem(), curTxt))
commitEdit(curTxt);
else
super.cancelEdit();
}
finally {
cancelling = false;
}
}
}
Update: this code was written as a part of Apache Ignite Schema Import GUI Utility.
See full version of TableCell code: https://github.com/apache/ignite/blob/ignite-1.9/modules/schema-import/src/main/java/org/apache/ignite/schema/ui/Controls.java
Also you could build this utility (it is a very simple utility with 2 screens) and play with it under Java7/javaFx2 & Java8/JavaFx8.
I tested - it works under both of them.

JTable losing focus after a button press

I have a jtable with a custom editor and renderer applied to a column to turn the column's contents into a Button, however once i press on any of the buttons, the table loses focus, and the only interactive object in the panel is the button that was just clicked on, which can be clicked on repeatedly.
Here is the button renderer code:
public class ButtonRenderer implements TableCellRenderer {
private Border dsd_originalBorder;
private int in_mnemonic;
private Border dsd_focusBorder;
private JButton dsd_renderButton;
/**
* empty constructor
*/
public ButtonRenderer() {
dsd_renderButton = new JButton();
}
public Component getTableCellRendererComponent(JTable dsd_table, Object dsd_value,
boolean bo_isSelected, boolean bo_hasFocus, int in_row, int in_column) {
if (!WorkplaceConstants.STR_INACTIVE.equals(dsd_table.getValueAt(in_row, dsd_table
.getColumn(Main.hm_language.get(Language.STATUS))
.getModelIndex())))
return new JLabel("");
if (bo_isSelected) {
dsd_renderButton.setForeground(dsd_table.getSelectionForeground());
dsd_renderButton.setBackground(dsd_table.getSelectionBackground());
} else {
dsd_renderButton.setForeground(dsd_table.getForeground());
dsd_renderButton.setBackground(UIManager.getColor("Button.background"));
}
if (bo_hasFocus) {
dsd_renderButton.setBorder(dsd_focusBorder);
} else {
dsd_renderButton.setBorder(dsd_originalBorder);
}
// renderButton.setText( (value == null) ? "" : value.toString() );
if (dsd_value == null) {
dsd_renderButton.setText("");
dsd_renderButton.setIcon(null);
} else if (dsd_value instanceof Icon) {
dsd_renderButton.setText("");
dsd_renderButton.setIcon((Icon) dsd_value);
} else {
dsd_renderButton.setText(dsd_value.toString());
dsd_renderButton.setIcon(null);
}
return dsd_renderButton;
}
/**
* returns the mnemonic to activate the button when the cell has focus
*
* #return the mnemonic
*/
public int m_getMnemonic() {
return in_mnemonic;
}
/**
* The mnemonic to activate the button when the cell has focus
*
* #param p_in_mnemonic
* the mnemonic
*/
public void m_setMnemonic(int p_in_mnemonic) {
this.in_mnemonic = p_in_mnemonic;
dsd_renderButton.setMnemonic(p_in_mnemonic);
}
/**
* Get foreground color of the button when the cell has focus
*
* #return the foreground color
*/
public Border m_getFocusBorder() {
return dsd_focusBorder;
}
/**
* The foreground color of the button when the cell has focus
*
* #param dsd_focusBorder
* the foreground color
*/
public void m_setFocusBorder(Border dsd_focusBorder) {
this.dsd_focusBorder = dsd_focusBorder;
}
Here is the button Editor code:
public class ButtonEditor extends AbstractCellEditor implements TableCellEditor, ActionListener, MouseListener{
private JTable dsd_table;
private int in_mnemonic;
private Border dsd_originalBorder;
private Border dsd_focusBorder;
private JButton dsd_editButton;
private Object dsd_editorValue;
private boolean bo_isButtonColumnEditor;
/**
* Constructor
* #param dsdp_table the table to which the editor is going to be applied.
* #param action the action which is to be executed when the button is clicked
*/
public ButtonEditor(JTable dsdp_table) {
this.dsd_table = dsdp_table;
dsd_editButton = new JButton();
dsd_editButton.setFocusPainted( false );
dsd_editButton.addActionListener( this );
dsd_originalBorder = dsd_editButton.getBorder();
m_setFocusBorder( new LineBorder(Color.BLUE) );
dsdp_table.addMouseListener( this );
}
public Object getCellEditorValue() {
return dsd_editorValue;
}
/**
* returns the mnemonic to activate the button when the cell has focus
*
* #return the mnemonic
*/
public int m_getMnemonic()
{
return in_mnemonic;
}
/**
* The mnemonic to activate the button when the cell has focus
*
* #param in_mnemonic the mnemonic
*/
public void m_setMnemonic(int in_mnemonic)
{
this.in_mnemonic = in_mnemonic;
dsd_editButton.setMnemonic(in_mnemonic);
}
/**
* Get foreground color of the button when the cell has focus
*
* #return the foreground color
*/
public Border m_getFocusBorder() {
return dsd_focusBorder;
}
/**
* The foreground color of the button when the cell has focus
*
* #param dsdp_focusBorder
* the foreground color
*/
public void m_setFocusBorder(Border dsdp_focusBorder) {
this.dsd_focusBorder = dsdp_focusBorder;
dsd_editButton.setBorder(dsdp_focusBorder);
}
public void actionPerformed(ActionEvent arg0) {
int in_modelRow = dsd_table.convertRowIndexToModel( dsd_table.getEditingRow() );
fireEditingStopped();
// Here i start a custom thread to run in the background.
}
public void mouseClicked(MouseEvent arg0) {
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
if (dsd_table.isEditing() && dsd_table.getCellEditor() == this)
bo_isButtonColumnEditor = true;
}
public void mouseReleased(MouseEvent arg0) {
if (bo_isButtonColumnEditor && dsd_table.isEditing())
dsd_table.getCellEditor().stopCellEditing();
bo_isButtonColumnEditor = false;
}
public void addCellEditorListener(CellEditorListener arg0) {
}
public void cancelCellEditing() {
}
public void removeCellEditorListener(CellEditorListener arg0) {
}
public boolean shouldSelectCell(EventObject arg0) {
return false;
}
public boolean stopCellEditing() {
return false;
}
public Component getTableCellEditorComponent(JTable dsd_table, Object dsd_value,
boolean bo_isSelected, int in_row, int in_column) {
if (!WorkplaceConstants.STR_INACTIVE.equals(dsd_table.getValueAt(in_row, dsd_table
.getColumn(Main.hm_language.get(Language.STATUS))
.getModelIndex())))
return new JLabel("");
if (dsd_value == null)
{
dsd_editButton.setText( "" );
dsd_editButton.setIcon( null );
}
else if (dsd_value instanceof Icon)
{
dsd_editButton.setText( "" );
dsd_editButton.setIcon( (Icon)dsd_value );
}
else
{
dsd_editButton.setText( dsd_value.toString() );
dsd_editButton.setIcon( null );
}
bo_isButtonColumnEditor = true;
this.dsd_editorValue = dsd_value;
dsd_editButton.setBorder(dsd_originalBorder);
return dsd_editButton;
}
public boolean isCellEditable(EventObject e){
return true;
}
public Border m_getoriginalBorder() {
return dsd_originalBorder;
}
public void m_set_originalBorder(Border dsd_originalBorder) {
this.dsd_originalBorder = dsd_originalBorder;
}
}
Here is how i am assigning the editor and renderer to the table
public static void m_setButtonColumnConfiguration(JTable table) {
ButtonEditor dsd_btn_edit = new ButtonEditor(table);
dsd_btn_edit.m_setMnemonic(KeyEvent.VK_D);
table.getColumn(/*i get the identifier for the column here*/).setCellEditor(dsd_btn_edit);
ButtonRenderer dsd_btn_rend = new ButtonRenderer();
dsd_btn_rend.m_setMnemonic(KeyEvent.VK_D);
table.getColumn(/*i get the identifier for the column here*/)
.setCellRenderer(dsd_btn_rend);
}
Button, however once i press on any of the buttons, the table loses focus
Yes, anytime you click on a component it gets focus, so this makes sense.
and the only interactive object in the panel is the button that was just clicked on,
Not sure I understand this statement. You can click on the table again and it will regain focus. You can then navigate around the table.
Maybe you are just suggesting the button renderer/editor isn't working the way you expect. We can't test this because you didn't post a SSCCE.
Anyway, check out Table Button Column for another example of a button renderer/editor that responds to mouse clicks or key board events.

How to handle RowSorter-Synchronization in JTables with RowHeader?

Following Situation: I have a J(X)Table with RowHeader (As guidline I used one of Rob Camicks great Examples). All worked as expected.
By requirement the data I receive from server already contains a tablerownumber, which I have to show in the rowheader and the data should be filterable. So I extended the example, and I added a filter. When I filtered the view I saw gaps in my row numbers (for example: 1, 3, 6,..), which is the desired effect.
To be able to filter and sort the table by my own tablerow, I added a TableRowSorter. And here I started to get confused. The Example uses the same TableModel and SelectionModel for mainTable and rowHeaderTable:
setModel( main.getModel() );
setSelectionModel( main.getSelectionModel() );
This is great, since I don’t have to synchronize them. But concerning TableRowSorter I suddenly wasn’t sure, if I also can or even have to use the same TableRowSorter-Instance or if I have to create a TableRowSorter for each table. First I added the same to both Tables, since this seemed practically, but then I got IndexOutOfBound-Exceptions in many cases. After some digging I found out that this is because the TableRowSorter gets updated twice at each TableModelEvent, because each table (RowHeader and MainTable) notifies the TableRowSorter about tablechanges on its own.
Now I am not sure which the right way to go is. Following solutions came into my mind: Should I add a second TableRowSorter (one for each table) and synchronize these, or should I wrap the TableModel within the RowHeaderTable and let it not fireing any Events? Or maybe I should create my own kind of RowHeaderTable which doesn’t notify Sorters about changes at all?
Here's a quick (beware: not formally tested! the usage example works fine, though) implementation of a wrapping RowSorter.
does nothing on receiving notification of model changes
delegates all status queries
listens to wrapped rowSorter and propagates its events
It's client's responsibility to keep it in synch with the rowSorter used in the main table
Usage example (in terms of SwingX test infrastructure and with SwingX sortController/table):
public void interactiveRowSorterWrapperSharedXTable() {
final DefaultTableModel tableModel = new DefaultTableModel(list.getElementCount(), 2) {
#Override
public Class<?> getColumnClass(int columnIndex) {
return Integer.class;
}
};
for (int i = 0; i < tableModel.getRowCount(); i++) {
tableModel.setValueAt(i, i, 0);
tableModel.setValueAt(tableModel.getRowCount() - i, i, 1);
}
final JXTable master = new JXTable(tableModel);
final TableSortController<TableModel> rowSorter = (TableSortController<TableModel>) master.getRowSorter();
master.removeColumn(master.getColumn(0));
final JXTable rowHeader = new JXTable(master.getModel());
rowHeader.setAutoCreateRowSorter(false);
rowHeader.removeColumn(rowHeader.getColumn(1));
rowHeader.setRowSorter(new RowSorterWrapper<TableModel>(rowSorter));
rowHeader.setSelectionModel(master.getSelectionModel());
// need to disable selection update on one of the table's
// otherwise the selection is not kept in model coordinates
rowHeader.setUpdateSelectionOnSort(false);
JScrollPane scrollPane = new JScrollPane(master);
scrollPane.setRowHeaderView(rowHeader);
JXFrame frame = showInFrame(scrollPane, "xtables (wrapped sortController): shared model/selection");
Action fireAllChanged = new AbstractAction("fireDataChanged") {
#Override
public void actionPerformed(ActionEvent e) {
tableModel.fireTableDataChanged();
}
};
addAction(frame, fireAllChanged);
Action removeFirst = new AbstractAction("remove firstM") {
#Override
public void actionPerformed(ActionEvent e) {
tableModel.removeRow(0);
}
};
addAction(frame, removeFirst);
Action removeLast = new AbstractAction("remove lastM") {
#Override
public void actionPerformed(ActionEvent e) {
tableModel.removeRow(tableModel.getRowCount() - 1);
}
};
addAction(frame, removeLast);
Action filter = new AbstractAction("toggle filter") {
#Override
public void actionPerformed(ActionEvent e) {
RowFilter filter = rowSorter.getRowFilter();
if (filter == null) {
rowSorter.setRowFilter(RowFilter.regexFilter("^1", 1));
} else {
rowSorter.setRowFilter(null);
}
}
};
addAction(frame, filter);
addStatusMessage(frame, "row header example with RowSorterWrapper");
show(frame);
}
The RowSorterWrapper:
/**
* Wrapping RowSorter for usage (f.i.) in a rowHeader.
*
* Delegates all state queries,
* does nothing on receiving notification of model changes,
* propagates rowSorterEvents from delegates.
*
* Beware: untested!
*
* #author Jeanette Winzenburg, Berlin
*/
public class RowSorterWrapper<M> extends RowSorter<M> {
private RowSorter<M> delegate;
private RowSorterListener rowSorterListener;
public RowSorterWrapper(RowSorter<M> delegate) {
this.delegate = delegate;
delegate.addRowSorterListener(getRowSorterListener());
}
/**
* Creates and returns a RowSorterListener which re-fires received
* events.
*
* #return
*/
protected RowSorterListener getRowSorterListener() {
if (rowSorterListener == null) {
RowSorterListener listener = new RowSorterListener() {
#Override
public void sorterChanged(RowSorterEvent e) {
if (RowSorterEvent.Type.SORT_ORDER_CHANGED == e.getType()) {
fireSortOrderChanged();
} else if (RowSorterEvent.Type.SORTED == e.getType()) {
fireRowSorterChanged(null); }
}
};
rowSorterListener = listener;
}
return rowSorterListener;
}
#Override
public M getModel() {
return delegate.getModel();
}
#Override
public void toggleSortOrder(int column) {
delegate.toggleSortOrder(column);
}
#Override
public int convertRowIndexToModel(int index) {
return delegate.convertRowIndexToModel(index);
}
#Override
public int convertRowIndexToView(int index) {
return delegate.convertRowIndexToView(index);
}
#Override
public void setSortKeys(List keys) {
delegate.setSortKeys(keys);
}
#Override
public List getSortKeys() {
return delegate.getSortKeys();
}
#Override
public int getViewRowCount() {
return delegate.getViewRowCount();
}
#Override
public int getModelRowCount() {
return delegate.getModelRowCount();
}
#Override
public void modelStructureChanged() {
// do nothing, all work done by delegate
}
#Override
public void allRowsChanged() {
// do nothing, all work done by delegate
}
#Override
public void rowsInserted(int firstRow, int endRow) {
// do nothing, all work done by delegate
}
#Override
public void rowsDeleted(int firstRow, int endRow) {
// do nothing, all work done by delegate
}
#Override
public void rowsUpdated(int firstRow, int endRow) {
// do nothing, all work done by delegate
}
#Override
public void rowsUpdated(int firstRow, int endRow, int column) {
// do nothing, all work done by delegate
}
}

Swing dependent components and event system

I have to build a complex GUI in JAVA with Swing (for the moment I have near 80 classes).
The graphic partv of the application is split as follows: a first series of tabs (eg "Management", "Administration", "Configuration"), then a second level (for example, "User", "Group", "Game"). For now I'm two grade levels (one for each level of tabs). The next level is a JPanel that manages a business object (my whole GUI is built around my business model), at this level there are 2 type of JPanel: who manages simple objects (eg, "User", "Category" , "Game", "Level") and those which manages objects "composite primary key" (eg "User_Game" which represent the form of a double-entry table for each game level for all users).
My second level of tabs can contain multiple JPanel.
When my JPanel manages a single object is composed of a JTable and two buttons (Add and Remove) on which I put events, if not it is a simple JTable. When I have foreign keys (eg "Group" for "User" and "Category" to "Game" or "Level" to "User_Game") it is a JComboBox that takes its information directly from JTableModel. When it comes to managing a JTable object to "composite primary key" the columns and rows also directly dependent models (eg "Game" and "User" "User_Game").
Each has its own JTable model that deals with the persistence layer (Hibernate for information) and other TableModel.
To manage changes (such as adding, modifying or deleting a "User") I use the code below:
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
/*
* This class listens for changes made to the data in the table via the
* TableCellEditor. When editing is started, the value of the cell is saved
* When editing is stopped the new value is saved. When the oold and new
* values are different, then the provided Action is invoked.
*
* The source of the Action is a TableCellListener instance.
*/
public class TabCellListener implements PropertyChangeListener, Runnable
{
private JTable table;
private Action action;
private int row;
private int column;
private Object oldValue;
private Object newValue;
/**
* Create a TableCellListener.
*
* #param table the table to be monitored for data changes
* #param action the Action to invoke when cell data is changed
*/
public TabCellListener(JTable table, Action action)
{
this.table = table;
this.action = action;
this.table.addPropertyChangeListener( this );
this.table.getModel().addTableModelListener(new ModelListenerTableGui(this.table, this.action));
}
/**
* Create a TableCellListener with a copy of all the data relevant to
* the change of data for a given cell.
*
* #param row the row of the changed cell
* #param column the column of the changed cell
* #param oldValue the old data of the changed cell
* #param newValue the new data of the changed cell
*/
private CellListenerTableGui(JTable table, int row, int column, Object oldValue, Object newValue)
{
this.table = table;
this.row = row;
this.column = column;
this.oldValue = oldValue;
this.newValue = newValue;
}
/**
* Get the column that was last edited
*
* #return the column that was edited
*/
public int getColumn()
{
return column;
}
/**
* Get the new value in the cell
*
* #return the new value in the cell
*/
public Object getNewValue()
{
return newValue;
}
/**
* Get the old value of the cell
*
* #return the old value of the cell
*/
public Object getOldValue()
{
return oldValue;
}
/**
* Get the row that was last edited
*
* #return the row that was edited
*/
public int getRow()
{
return row;
}
/**
* Get the table of the cell that was changed
*
* #return the table of the cell that was changed
*/
public JTable getTable()
{
return table;
}
//
// Implement the PropertyChangeListener interface
//
#Override
public void propertyChange(PropertyChangeEvent e)
{
// A cell has started/stopped editing
if ("tableCellEditor".equals(e.getPropertyName()))
{
if (table.isEditing())
processEditingStarted();
else
processEditingStopped();
}
}
/*
* Save information of the cell about to be edited
*/
private void processEditingStarted()
{
// The invokeLater is necessary because the editing row and editing
// column of the table have not been set when the "tableCellEditor"
// PropertyChangeEvent is fired.
// This results in the "run" method being invoked
SwingUtilities.invokeLater( this );
}
/*
* See above.
*/
#Override
public void run()
{
row = table.convertRowIndexToModel( table.getEditingRow() );
column = table.convertColumnIndexToModel( table.getEditingColumn() );
oldValue = table.getModel().getValueAt(row, column);
newValue = null;
}
/*
* Update the Cell history when necessary
*/
private void processEditingStopped()
{
newValue = table.getModel().getValueAt(row, column);
// The data has changed, invoke the supplied Action
if ((newValue == null && oldValue != null) || (newValue != null && !newValue.equals(oldValue)))
{
// Make a copy of the data in case another cell starts editing
// while processing this change
CellListenerTableGui tcl = new CellListenerTableGui(
getTable(), getRow(), getColumn(), getOldValue(), getNewValue());
ActionEvent event = new ActionEvent(
tcl,
ActionEvent.ACTION_PERFORMED,
"");
action.actionPerformed(event);
}
}
}
And the following action:
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import javax.swing.Action;
public class UpdateTableListener<N> extends AbstractTableListener implements Action
{
protected boolean enabled;
public UpdateTableListener(AbstractTableGui<N> obs)
{
super(obs);
this.enabled = true;
}
#Override
public void actionPerformed(ActionEvent e)
{
if (null != e && e.getSource() instanceof CellListenerTableGui)
{
TabCellListener tcl = (TabCellListener)e.getSource();
this.obs.getModel().setValueAt(tcl.getNewValue(), tcl.getRow(), tcl.getColumn());
int sel = this.obs.getModel().getNextRequiredColumn(tcl.getRow());
if (sel == -1)
this.obs.getModel().save(tcl.getRow());
}
}
#Override
public void addPropertyChangeListener(PropertyChangeListener arg0)
{
}
#Override
public Object getValue(String arg0)
{
return null;
}
#Override
public boolean isEnabled()
{
return this.enabled;
}
#Override
public void putValue(String arg0, Object arg1)
{
}
#Override
public void removePropertyChangeListener(PropertyChangeListener arg0)
{
}
#Override
public void setEnabled(boolean arg0)
{
this.enabled = arg0;
}
}
This code works well, data are well persisted.
Then I add this code to refresh dependent components:
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import javax.swing.Action;
public class ChangeTableListener implements Action
{
protected AbstractTableGui table;
public ChangeTableListener(AbstractTableGui table)
{
this.table = table;
}
#Override
public void actionPerformed(ActionEvent arg0)
{
this.table.getModel().fireTableDataChanged();
this.table.repaint();
}
#Override
public void addPropertyChangeListener(PropertyChangeListener arg0)
{
}
#Override
public Object getValue(String arg0)
{
return null;
}
#Override
public boolean isEnabled()
{
return false;
}
#Override
public void putValue(String arg0, Object arg1)
{
}
#Override
public void removePropertyChangeListener(PropertyChangeListener arg0)
{
}
#Override
public void setEnabled(boolean arg0)
{
}
}
My TableModel.fireTableDataChanged rebuild JTable content (calle super.fireTableDataChanged and fireTableStructureChanged), JTable.repaint reset the Renderers, and it works for Combobox (forein keys) and it update well title on double-entry tables, but it can't add or delete columns or rows on double-entry tables.
Moreover I see more high latency if there is the slightest change.
My question is simple: how do you manage inter-dependent components?
For your help,
In advance,
Thanks.
Edit :
Here an example of TableCellEditor.
import javax.swing.DefaultCellEditor;
import javax.swing.JTextField;
public class TextColumnEditor extends DefaultCellEditor
{
public TextColumnEditor()
{
super(new JTextField());
}
public boolean stopCellEditing()
{
Object v = this.getCellEditorValue();
if(v == null || v.toString().length() == 0)
{
this.fireEditingCanceled();
return false;
}
return super.stopCellEditing();
}
}
An example of TableModel :
import java.util.ArrayList;
public class GroupModelTable extends AbstractModelTable<Groups>
{
protected GroupsService service;
public GroupModelTable(AbstractTableGui<Groups> content)
{
super(content, new ArrayList<String>(), new ArrayList<Groups>());
this.headers.add("Group");
this.content.setModel(this);
this.service = new GroupsService();
this.setLines(this.service.search(new Groups()));
}
public Object getValueAt(int rowIndex, int columnIndex)
{
switch (columnIndex)
{
case 0:
return this.lines.get(rowIndex).getTitle();
default:
return "";
}
}
public void setValueAt(Object aVal, int rowIndex, int columnIndex)
{
switch (columnIndex)
{
case 0:
this.lines.get(rowIndex).setTitle(aVal.toString());
break;
default:
break;
}
}
#Override
public Groups getModel(int line, int column)
{
return null;
}
#Override
public Groups getModel(int line)
{
return this.lines.get(line);
}
public boolean isCellEditable(int row, int column)
{
return true;
}
#Override
public GroupModelTableGui newLine()
{
this.lines.add(new Groups());
return this;
}
#Override
public int getNextRequiredColumn(int row)
{
Groups g = this.getModel(row);
if (g != null && g.getTitle() != null && g.getTitle().length() > 0)
return -1;
return 0;
}
#Override
public void save(int row)
{
Groups g = this.getModel(row);
if (g != null)
{
try
{
if (g.getId() == null)
this.service.create(g);
else
this.service.update(g);
}
catch (Exception e)
{
}
}
}
#Override
public void removeRow(int row)
{
Groups g = this.getModel(row);
if (g != null)
{
try
{
if (g.getId() != null)
this.service.delete(g);
super.removeRow(row);
}
catch (Exception e)
{
}
}
}
}
An example of Table :
public class GroupTable extends AbstractTable<Groups>
{
public GroupTable()
{
new GroupModelTableGui(this);
new CellListenerTableGui(this.getContent(), new UpdateTableListenerGui<Groups>(this));
this.getContent().getColumnModel().getColumn(0).setCellEditor(new TextColumnEditorGui());
}
}
I hope it will help you to understand :/
Your TabCellListener is unfamiliar to me. Your TableCellEditor should not interact with the TableModel directly. It should implement getTableCellEditorComponent() and getCellEditorValue(), as shown in this example. When the editor concludes, the model will have the new value. Your TableModel should handle persistence. More than one view may listen to a single TableModel via TableModelListener.
Addendum: Your CellEditor, TextColumnEditor, probably shouldn't invoke fireEditingCanceled(). Pressing Escape should be sufficient to revert the edit, as shown in this example. You might also look at the related tutorial section and example.

Categories