I have followed Vogella's tutorial on Table Viewer and using the StyledCellLabelProvider presented there. It creates a CHECKED or UNCHECKED icon on the third column
col = createTableViewerColumn(titles[3], bounds[3], 3);
col.setLabelProvider(new ColumnLabelProvider() {
#Override
public String getText(Object element) {
return null;
}
#Override
public Image getImage(Object element) {
if (((Person) element).isMarried()) {
return CHECKED;
} else {
return UNCHECKED;
}
}
});
col.setEditingSupport(new MarriedEditingSupport(viewer));
The column images get painted accordingly when the table is created but how can I change the icon when the user clicks a cell ? (I want to simulate a check box)
You can use an EditingSupport class based on CheckboxCellEditor for this:
class MarriedEditingSupport extends EditingSupport
{
private final CheckboxCellEditor _editor;
MarriedEditingSupport(TableViewer viewer)
{
super(viewer);
_editor = new CheckboxCellEditor(viewer.getTable());
}
#Override
protected CellEditor getCellEditor(Object element)
{
return _editor;
}
#Override
protected boolean canEdit(Object element)
{
return true;
}
#Override
protected Object getValue(Object element)
{
return Boolean.valueOf(((Person)element).isMarried());
}
#Override
protected void setValue(Object element, Object value)
{
((Person)element).setMarried(((Boolean)value).booleanValue());
getViewer().update(element, null);
}
}
Related
I have a textual editor that extends AbstractTextEditor and I also have an Outline that needs to be saved when its content is modified by the user. I am currently using a Saveable which is added to the editor.
If the editor was marked as 'dirty' and it is saved, the Saveableis saved as well. However, if the Saveable's state changes to 'dirty', the * next to the file name does not appear. The save button in the top menu bar does show, but when I click it, nothing happens.
This is my implementation:
public class MyTextEditor extends AbstractTextEditor {
...
public void setOutlineSaveable(Saveable saveable) {
this.outlineSaveable = saveable;
ISaveablesLifecycleListener lifecycleListener = (ISaveablesLifecycleListener)getSite().getService(ISaveablesLifecycleListener.class);
lifecycleListener.handleLifecycleEvent( new SaveablesLifecycleEvent(this, SaveablesLifecycleEvent.POST_OPEN, new Saveable[] {saveable}, false));
}
#Override
public Saveable[] getSaveables() {
if(outlineSaveable != null) {
// copy Saveables from super.getSaveables() to a new array
Saveable[] superSaveables = super.getSaveables();
Saveable[] res = new Saveable[superSaveables.length + 1];
int i = 0;
for(; i < superSaveables.length; i++) {
res[i] = superSaveables[i];
}
res[i] = outlineSaveable;
return res;
}
else
return super.getSaveables();
}
public void saveableDirty() {
firePropertyChange(PROP_DIRTY);
}
}
My ContentOutlinePage:
public class GraphicalOutlinePage extends ContentOutlinePage {
...
private GraphicalOutlineSaveable saveable;
public Saveable getSaveable() {
return saveable;
}
class GraphicalOutlineSaveable extends Saveable {
private boolean dirty = false;
private IEditorPart editor;
public GraphicalOutlineSaveable(IEditorPart editor) {
this.editor = editor;
}
#Override
public void doSave(IProgressMonitor monitor) throws CoreException {
viewer.doSave(monitor);
dirty = false;
}
#Override
public boolean equals(Object obj) {
System.err.println("GraphicalOutline.GraphicalOutlineSaveable.equals");
return obj instanceof GraphicalOutlineSaveable && ((Saveable)obj).getName() == getName();
}
#Override
public ImageDescriptor getImageDescriptor() {
return editor.getEditorInput().getImageDescriptor();
}
#Override
public String getName() {
return "Graphical Outline: " + editor.getEditorInput().getName();
}
#Override
public String getToolTipText() {
return "";
}
#Override
public boolean isDirty() {
System.err.println("GraphicalOutlinePage.GraphicalOutlineSaveable.isDirty: " + dirty);
return dirty;
}
public void setDirty() {
System.err.println("GraphicalOutlinePage.GraphicalOutlineSaveable.setDirty");
dirty = true;
// notify text editor about property change
if(editor instanceof AbstractTextEditor) {
((MyTextEditor)editor).saveableDirty();
}
}
#Override
public int hashCode() {
return viewer.hashCode();
}
}
}
vieweris a GraphicalViewerdisplayed in the ContentOutlinePage.
Somewhere in another class, I then call:
textEditor.setSaveable(grOutlinePage.getSaveable());
You may need to override the main editor isDirty() method and test each of the Saveable objects dirty flags.
It seems that the handling of multiple Saveables is not done as cleanly is it might have been.
have a look the following working code:
class MyType{
SimpleStringProperty myname;
SimpleObjectProperty<Color> mycolor;
}
TableColumn col;
arr = FXCollections.observableArrayList(new ArrayList<MyType>());
tblColName.setCellValueFactory(new PropertyValueFactory("myname"));
// Use the cell-factory provided by TextFieldTableCell.
tblColName.setCellFactory(TextFieldTableCell.forTableColumn());
tblColName.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent>() {
#Override
public void handle(TableColumn.CellEditEvent cellEditEvent) {
((MyType) cellEditEvent.getRowValue()).myname.set((String) cellEditEvent.getNewValue());
}
});
However, as soon as I am using a custom TableCell, the code in setOnEditCommit is not called anymore:
public class ColorPickerTableCell<S> extends TableCell<S, Color>{
private ColorPicker cp;
public ColorPickerTableCell(){
cp = new ColorPicker(Color.BLACK);
cp.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
commitEdit(cp.getValue());
updateItem(cp.getValue(), isEmpty());
}
});
setGraphic(cp);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setEditable(true);
}
#Override
protected void updateItem(Color item, boolean empty) {
super.updateItem(item, empty);
cp.setVisible(!empty);
this.setItem(item);
}
public static <T> Callback<TableColumn<Color, T>, TableCell<Color, T>> forTableColumn(){
return new Callback<TableColumn<Color, T>, TableCell<Color, T>>() {
#Override
public TableCell<Color, T> call(TableColumn<Color, T> colorTTableColumn) {
return new ColorPickerTableCell();
}
};
}
}
A slight change of the code above...
TableColumn col;
arr = FXCollections.observableArrayList(new ArrayList<MyType>());
tblColName.setCellValueFactory(new PropertyValueFactory("myname"));
// Use the cell-factory provided by TextFieldTableCell.
tblColName.setCellFactory(ColorPickerTableCell.forTableColumn());
tblColName.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent>() {
#Override
public void handle(TableColumn.CellEditEvent cellEditEvent) {
throw new NotImplementedException(); // is never thrown.
}
});
... makes the code not work anymore. The exception is never thrown. I think that I am doing something wrong in the design of ColorPickerTableCell, but I cannot imagine what. How can I make JavaFX call my OnEditCommit ?
You need first to go to edit state with statEdit(); , if you now commit an event will be fired
I've recently had the same problem. Unfortunately, I haven't found any way of trigerring that event from the ColorPicker control. However, I came up with the following workaround.
First of all, I created a Color wrapper class:
public class ColorWrapper {
private Color color;
....
}
I replaced the Color instance with the wrapper instance in my model class M. Next, I implemented setCellFactory method in the following way:
myColumn.setCellFactory(new Callback<TableColumn<M, ColorWrapper>, TableCell<M, ColorWrapper>>() {
#Override
public TableCell<M, ColorWrapper> call(TableColumn<M,ColorWrapper> arg0) {
return new TableCell<M, ColorWrapper>(){
private ColorPicker colorPicker;
private ColorPicker createPicker(){
colorPicker = new ColorPicker();
colorPicker.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent evt) {
ColorPicker cp = (ColorPicker)evt.getSource();
ColorWrapper cw = (ColorWrapper)cp.getUserData();
cw.setColor(cp.getValue());
}
});
return colorPicker;
}
#Override
protected void updateItem(ColorWrapper value, boolean empty) {
super.updateItem(value, empty);
if(empty){
return;
}
if(colorPicker == null){
colorPicker = createPicker();
colorPicker.setUserData(value);
}
colorPicker.setValue(value.getColor());
setGraphic(colorPicker);
}
};
}
});
As you can see, I just made use of setUserData/getUserData methods of the ColorPicker class and that's it. It works.
I had the same problem for CheckBoxTableCell and DatePickerTableCell and ColorPickerTableCells :-(
I deal it like that: on the events of the controls I get back the POJO objects in use by the "((Inputs)getTableView().getItems().get(getTableRow().getIndex()" and I update similary like is it done in the OnEditCommit method...
So for me it's look like this (update the color):
((Inputs) getTableView().getItems().get(
getTableRow().getIndex())
).setColor(cp.getValue());
Here is example with ColorPickerCell
:
public class ColorPickerTableCell<Inputs> extends TableCell<Inputs, Color>{
private ColorPicker cp;
public ColorPickerTableCell(){
cp = new ColorPicker();
cp.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
commitEdit(cp.getValue());
updateItem(cp.getValue(), isEmpty());
((Inputs) getTableView().getItems().get(
getTableRow().getIndex())
).setColor(cp.getValue());
}
});
setGraphic(cp);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setEditable(true);
}
#Override
protected void updateItem(Color item, boolean empty) {
super.updateItem(item, empty);
cp.setVisible(!empty);
this.setItem(item);
cp.setValue(item);
}
}
With this simple JavaFX's POJO:
public ObjectProperty<Color> color = new SimpleObjectProperty<Color>();
this.color = new SimpleObjectProperty(color);
public ObjectProperty<Color> colorProperty() {
return color;
}
public void setColor(Color color2) {
color.set(color2);
}
I do not know if it's a good way to achive that but it worked for me... Note that the JavaFX's POJO is only accessible within an "ActionEvent" request (combobox, datepicker, colorpicker, etc..)
Regards,
To elaborate on justcode's answer, here is my class where I had the problem and solved it:
public class DeleteButtonCell extends TableCell<Menu, Menu> {
private Button deleteButton;
public DeleteButtonCell() {
deleteButton = new Button();
deleteButton.setId("trash-button");
deleteButton.setOnAction((e) -> {
startEdit();
commitEdit((Menu) this.getTableView().getItems().get(this.getIndex()));
});
}
#Override
protected void updateItem(Menu t, boolean empty) {
super.updateItem(t, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(deleteButton);
}
}
}
I implemented a CellTree structure in java gwt which opens it's childs when clicking on the little arrow in the cell contains. Is it possible to open the subtree also when just clicking on the cell itself and not explicitly on the arrow?
public class SomeCellTree extends Composite {
private SelectionModel selectionModel;
private CellTree cellTree;
private class SomeTreeViewModel implements TreeViewModel {
#Override
public <T> NodeInfo<?> getNodeInfo(T value) {
...
}
#Override
public boolean isLeaf(Object value) {
...
}
}
public SomeCellTree(...) {
this.selectionModel = new SingleSelectionModel();
this.selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
#Override
public void onSelectionChange(SelectionChangeEvent event) {
...
}
});
cellTree = new CellTree(new SomeTreeViewModel(),null,RESOURCES);
initWidget(cellTree);
}
}
You have to use recursion to find the node for the selected DTO and then you have to call setChildOpen to open the subtree. Something like this:
this.selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
#Override
public void onSelectionChange(SelectionChangeEvent event) {
T selectedObj = selectionModel.getSelectedObject();
TreeNode rootNode = cellTree.getRootTreeNode();
openTree(rootNode,T);
}
});
private void openTree(rootNode node,T object) {
for (int i = 0; i < node.getChildCount(); i++) {
if (!node.isChildLeaf(i)) {
find(node.setChildOpen(i, true));
if (node.getValue().equals(T))
return;
}
}
}
I have a Cell Table of which the last column is a checkbox. I would like to add a Check-All-Box as a footer that when clicked selects every checkbox for every row. Somehow it won't work here is what I got so far:
Column<Object, Boolean> select = new Column<Object, Boolean>(new CheckboxCell()) {
#Override
public Boolean getValue(Object object) {
return msm.isSelected(object);
}
};
select.setFieldUpdater(new FieldUpdater<Object, Boolean>() {
public void update(int index, Object object, Boolean value) {
msm.setSelected(object, value);
}
});
final Header selectAllHeader = new Header(new CheckboxCell()) {
#Override
public Boolean getValue(){
return msm.getSelectedSet().size() == getRowCount();
}
};
selectAllHeader.setUpdater(new ValueUpdater<Boolean>() {
#Override
public void update(Boolean value) {
for (Object o : getVisibleItems) {
msm.setSelected(o, value);
}
}
});
//works
addColumn(select, selectAllHeader);
//does not work
//addColumn(select, HEADER, selectAllHeader);
I used a solution proposed here:
http://code.google.com/p/google-web-toolkit/issues/detail?id=7014
It works nicely. You can use it in your footer.
The default behavior of a JTable is to append to the contents when you start typing, and to place the caret at the clicked location when clicking. I want the behavior of both these things to change, so the contents is replaced when I edit a cell, either by typing or by clicking and then typing. When I click a cell and then change the caret position, however, I want the contents to stay so I can change it.
I know how to select all when the cell becomes editing, by replacing the cell editor with one that selects all inside a SwingUtilities.invokeLater (see elsewhere), but that causes the typing behavior to break. When I do this and start typing in a cell, first the typed character is appended to the string, then it is selected (but the selection is invisible!) and when typing another character the contents gets replaced by that.
Is there a way to replace the contents immediately when typing in a highlighted (but not editing) cell, but select all when clicking a cell?
Here is the code I use for the CellEditor:
public class TextFieldCellEditor extends JTextField implements TableCellEditor
{
private CellEditorListener cellEditorListener = null;
private boolean isInteger = false;
private Object oldValue;
// Start editing
#Override
public Component getTableCellEditorComponent(JTable table, Object obj, boolean isSelected, int row, int column)
{
Color color2 = DefaultLookup.getColor(this, ui, "Table.alternateRowColor");
super.setBackground(color2 != null && (row & 1) == 1? color2 : table.getBackground());
super.setForeground(table.getForeground());
super.setBorder(DefaultLookup.getBorder(this, ui, "Table.focusCellHighlightBorder"));
super.setText(obj.toString());
isInteger = obj instanceof Integer;
if (isInteger)
{
super.setHorizontalAlignment(SwingConstants.RIGHT);
oldValue = obj;
}
// SwingUtilities.invokeLater(new Runnable()
// {
// public void run()
// {
// TextFieldCellEditor.this.selectAll();
// }
// });
return this;
}
// Retrieve e dited value
#Override
public Object getCellEditorValue()
{
if (isInteger)
{
// Try to convert to integer. If input is invalid, revert.
try
{
return new Integer(super.getText());
}
catch (NumberFormatException e)
{
return oldValue;
}
}
return super.getText();
}
#Override
public boolean isCellEditable(EventObject e)
{
return true;
}
#Override
public boolean shouldSelectCell(EventObject e)
{
return true;
}
#Override
public boolean stopCellEditing()
{
cellEditorListener.editingStopped(new ChangeEvent(this));
return true;
}
#Override
public void cancelCellEditing()
{
cellEditorListener.editingCanceled(new ChangeEvent(this));
}
#Override
public void addCellEditorListener(CellEditorListener celleditorlistener)
{
cellEditorListener = celleditorlistener;
}
#Override
public void removeCellEditorListener(CellEditorListener celleditorlistener)
{
if (cellEditorListener == cellEditorListener) cellEditorListener = null;
}
}
In your getTableCellEditorComponent() implementation, add the following:
if (isSelected) {
this.selectAll();
}
As an aside, why not extend AbstractCellEditor or DefaultCellEditor(JTextField textField)? See also How to Use Tables: Using Other Editors.
Addendum: See also Table Select All Renderer and Table Select All Editor.
The cleanest solution I could find for this case was to overwrite the JTable's editCellAt and inform the CellEditor of how the edit was triggered:
#Override
public boolean editCellAt(int row, int column, EventObject e) {
cellEditor.setKeyTriggered(e instanceof KeyEvent);
return super.editCellAt(row, column, e);
}
And here is the relevant CellEditor code:
public class MyCellEditor extends DefaultCellEditor {
private boolean keyTriggered;
public MyCellEditor() {
super(new JTextField());
final JTextField textField = (JTextField) getComponent();
textField.addFocusListener(new FocusAdapter() {
#Override
public void focusGained(FocusEvent e) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if (!keyTriggered) {
textField.selectAll();
}
}
});
}
});
}
public void setKeyTriggered(boolean keyTriggered) {
this.keyTriggered = keyTriggered;
}
#Override
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column) {
final JTextField textField = (JTextField)
super.getTableCellEditorComponent(table, value, isSelected, row, column);
textField.selectAll();
return textField;
}
}