I am using a tree table model in my app which extends AbstractTreeTableModel in order to create a JXTreeTable. Below is my model.
import org.jdesktop.swingx.treetable.AbstractTreeTableModel;
import org.jdesktop.swingx.treetable.DefaultTreeTableModel;
import org.jdesktop.swingx.treetable.TreeTableModel;
import javax.swing.tree.TreeModel;
public class MyDataModel extends AbstractTreeTableModel{
static protected String[] columnNames = { "Field", "Value" };
static protected Class<?>[] columnTypes = { Object.class, Object.class};
public MyDataModel(MyDataNode rootNode) {
super(rootNode);
root = rootNode;
}
#Override
public Object getChild(Object parent, int index) {
return ((MyDataNode) parent).getChildren().get(index);
}
#Override
public int getChildCount(Object parent) {
return ((MyDataNode) parent).getChildren().size();
}
#Override
public int getIndexOfChild(Object parent, Object child) {
return 0;
}
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public String getColumnName(int column) {
return columnNames[column];
}
#Override
public Class<?> getColumnClass(int column) {
return columnTypes[column];
}
#Override
public Object getValueAt(Object node, int column) {
MyDataNode mNode=(MyDataNode)node;
Object obj =mNode.getNodeDataObject();
if(column==0){
return mNode.getName();
}
else if (column==1){
if(obj instanceof Field){
Field field=(Field)mNode.getNodeDataObject();
if(field.getFieldDef().getListValue().size()>0){
return field.getFieldDef().getListValue();
}
else
return mNode.getDefaultValue();
}
else
return mNode.getDefaultValue();
}
return null;
}
#Override
public boolean isCellEditable(Object node, int column) {
//only allow Field values to be editable
if(((MyDataNode)node).getNodeDataObject() instanceof Field && column==1)
return true;
else
return false;
}
#Override
public boolean isLeaf(Object node) {
MyDataNode mNode=(MyDataNode)node;
Object obj=mNode.getNodeDataObject();
if(obj instanceof Field){
Field field=(Field)obj;
if(field.getFieldDef().getDataType().equalsIgnoreCase("MULTIPLE_MESSAGE")){
return false;
}
else
return true;
}
return false;
}
#Override
public void setValueAt(Object aValue, Object node, int column) {
MyDataNode mNode=(MyDataNode)node;
if (mNode.getNodeDataObject() instanceof Field && column == 1) {
Field field = (Field) mNode.getNodeDataObject();
field.setDefaultValue(aValue);
field.setSelectedValue(aValue);
}
}
}
This is how I use the JXTreeTable in my app
MyDataModel treeTableModel = new MyDataModel(createDataStructure(message));
jTreeTable = new JXTreeTable(treeTableModel);
private static MyDataNode createDataStructure(Message message) {
//setting fields as children of the root
nodeList = new ArrayList<>();
for (int index=0;index<message.getListFields().size() ; index++) {
if(message.getListFields().get(index).getFieldDef()
.getDataType().equalsIgnoreCase("MULTIPLE_MESSAGE")){
nodeList.add(new MyDataNode(message.getListFields()
.get(index).getFieldDef().getfName(), "", childMessagesRoot,
message.getListFields().get(index)));
}
else{
nodeList.add(new MyDataNode(message.getListFields()
.get(index).getFieldDef().getfName(), (String)(message.getListFields().
get(index).getDefaultValue()),
null,message.getListFields().get(index)));
}
}
//setting the Message to the root of the tree
root = new MyDataNode(message.getMsgName(), "", nodeList,message);
return root;
}
when I need to add a new node to the JXTreeTable, I try to get its model and use insertNodeInto() function call but the model doesn't support the insertNodeInto() function.
Someone please let me know where I am going wrong in the code. This is the first time I am using tree tables so there could be something missing.
insertNodeInto is a method of DefaultTreeTableModel, but not one of AbstractTreeTableModel.
Let MyDataModel extend DefaultTreeTableModel rather than AbstractTreeTableModel to be able to use insertNodeInto.
When accessing the TreeTableModel through JXTreeTable#getTreeTableModel, remember to cast the returned object of type TreeTableModel to DefaultTreeTableModel before calling insertNodeInto.
I found where I had gone wrong. I extended MyDataModel class with DefaultTreeTableModel and made my node class(I am using a custom node class) extend DefaultMutableTreeTableNode this gave the solution to use insertNodeInto method to get the current selected node of the tree table.
Related
This is my implementation of EditTextCell:
public class MyEditTextCell extends EditTextCell {
#Override
protected void edit(Context context, Element parent, String value) {
if (value.equals("")) {
super.edit(context, parent, value);
} else {
clearViewData(context.getKey());
}
}
}
I would like to text input to be shown for only for empty cells. IT is why I've override the edit() method. The rest behaviour of oryginal EditTextCell is ok, so I've not changed it.
This unfortunatelly doesn't work. Please help.
The editor in EditTextCell is shown in onBrowserEvent method, so you just need:
public class MyEditTextCell extends EditTextCell {
#Override
public void onBrowserEvent(Context context, Element parent, String value, NativeEvent event, ValueUpdater<String> valueUpdater) {
if(value == null || value.isEmpty())
super.onBrowserEvent(context, parent, value, event, valueUpdater);
}
}
Remember, to add the FieldUpdater to the column to save the edited value.
Here you have a full working example with simple table type containing only one String:
public class MyTableType {
private String value;
public MyTableType(String value) {
super();
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
CellTable<MyTableType> table = new CellTable<MyTableType>();
MyEditTextCell cell = new MyEditTextCell();
Column<MyTableType, String> column = new Column<MyTableType, String>(cell) {
#Override
public String getValue(MyTableType object) {
if(object.getValue() == null)
return "";
else
return object.getValue();
}
};
column.setFieldUpdater(new FieldUpdater<MyTableType, String>() {
#Override
public void update(int index, MyTableType object, String value) {
object.setValue(value);
}
});
table.addColumn(column, "Value");
ArrayList<MyTableType> values = new ArrayList<MyTableType>();
values.add(new MyTableType("one"));
values.add(new MyTableType("two"));
values.add(new MyTableType("three"));
values.add(new MyTableType(null));
values.add(new MyTableType(""));
table.setRowData(values);
Please, notice that once you edit the cell value to be non-empty, the editor will not be shown after that.
I'm looking for a way to create a treeTable view in Nattable. I already have a NatTable implemented with filter, sorting, ...
But now I'm looking into a TreeTable like the TreeGridWithCheckBoxFieldsExample from the Nattable examples. The only requirement is that I do not change my datamodel for the tree.
I have two different objects Company and role. Every company does has all the roles. So in this situation I need a tree with all the companies as root object and all the roles beneath all the companies.
From the example it looks like I need to create a format class that implements the TreeList.Format but they are using the model to link the parent (I will not do this because it's a violation of the MVC principle.
Can someone get me on track to create a treetable view in NatTable?
After checking some example of Natable I got a working treeTable. But have only one problem left. The parent items are not correctly shown.
The treeFormat looks like:
public class TreeFormat implements TreeList.Format<PermissionViewModel> {
public TreeFormat() {
}
#Override
public Comparator getComparator(int depth) {
return new Comparator<PermissionViewModel>() {
#Override
public int compare(PermissionViewModel object1, PermissionViewModel object2) {
return object1.getModuleName().compareTo(object2.getModuleName());
}
};
}
#Override
public void getPath(List<PermissionViewModel> path, PermissionViewModel element) {
path.add(element);
PermissionViewModel parent = element.getParent();
while (parent != null) {
path.add(parent);
parent = parent.getParent();
}
Collections.reverse(path);
}
#Override
public boolean allowsChildren(PermissionViewModel element) {
return true;
}
The model that I use is a viewModel and is a one to one map to the normal model
public class PermissionViewModel implements Comparable {
private PermissionViewModel parent;
private ArrayList<PermissionViewModel> children = new ArrayList();
private Integer permissionId;
private String moduleName;
private String permissionName;
private boolean active;
private boolean on;
public PermissionViewModel(PermissionViewModel parent, Permission permission) {
this.parent = parent;
if (parent != null) {
parent.addChild(this);
}
if(parent == null && permission != null)
{
this.permissionId = 0;
this.moduleName = "";
this.permissionName = permission.getModuleName();
this.active = false;
}
else
{
this.permissionId = permission.getPermissionId();
this.moduleName = permission.getModuleName();
this.permissionName = permission.getPermissionName();
this.active = permission.isActive();
}
}
public PermissionViewModel getParent() {
return this.parent;
}
public void addChild(PermissionViewModel child) {
this.children.add(child);
}
public List getChildren() {
return this.children;
}
public PermissionViewModel getSelf() {
return this;
}
public boolean isOn() {
if (this.children.size() == 0) {
return this.on;
} else {
return getCheckBoxState() == CheckBoxStateEnum.CHECKED;
}
}
public void setOn(boolean on) {
if (this.children.size() == 0) {
this.on = on;
} else {
for (PermissionViewModel child : this.children) {
child.setOn(on);
}
}
}
public CheckBoxStateEnum getCheckBoxState() {
if (this.children.size() == 0) {
return this.on ? CheckBoxStateEnum.CHECKED
: CheckBoxStateEnum.UNCHECKED;
} else {
boolean atLeastOneChildChecked = false;
boolean atLeastOneChildUnchecked = false;
for (PermissionViewModel child : this.children) {
CheckBoxStateEnum childCheckBoxState = child.getCheckBoxState();
switch (childCheckBoxState) {
case CHECKED:
atLeastOneChildChecked = true;
break;
case SEMICHECKED:
return CheckBoxStateEnum.SEMICHECKED;
case UNCHECKED:
atLeastOneChildUnchecked = true;
break;
}
}
if (atLeastOneChildChecked) {
if (atLeastOneChildUnchecked) {
return CheckBoxStateEnum.SEMICHECKED;
} else {
return CheckBoxStateEnum.CHECKED;
}
} else {
return CheckBoxStateEnum.UNCHECKED;
}
}
}
#Override
public int compareTo(Object o) {
return 0;
}
public Integer getPermissionId() {
return permissionId;
}
public String getModuleName() {
return moduleName;
}
public String getPermissionName() {
return permissionName;
}
public boolean isActive() {
return active;
}
}
Putting data in the tree table will be done with following source:
ArrayList<PermissionViewModel> permissionViewModelsList = new ArrayList<>();
String previousModule = "";
PermissionViewModel currentParent = null;
for (Permission element : repo.getAllData()) {
if(!previousModule.equals(element.getModuleName()))
{
previousModule = element.getModuleName();
currentParent = new PermissionViewModel(null, element);
permissionViewModelsList.add(currentParent);
permissionViewModelsList.add(new PermissionViewModel(currentParent, element));
}
else
{
permissionViewModelsList.add(new PermissionViewModel(currentParent, element));
}
}
Collections.reverse(permissionViewModelsList);
permissionTable.setItems(permissionViewModelsList);
permissionTable.refresh(true);
But when I look at the table the root elements are viewed but the childeren of the root elements are wrong. I viewed the list of elements and there I can't find any issues. Can someone find the issue that I have?
I changed to order of the columns. I placed the "Module Name" before the "Name" column. And the parent module name has been placed in the module name column. This fixed my problem.
The issue in this case was that the comperator that I used is not working on mixed data in the same fields. Tnx to Dirk Fauth I found it.
I am implementing TreeModel interface and have implemented all methods except for the valueForPathChanged.
In my case, the values are not going to be programatically changed.
Why TreeModel interface contains this method? Is it used by JTree in any circumstances, or I am safe to leave it unimplemented?
Code for the reference, it works. I am just concerned whether valueForPathChanged is required by the JTree:
class ParamsTreeModel implements TreeModel {
private final TreeRoot root;
private final List<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
ParamsTreeModel(TreeRoot root) {
this.root = root;
}
#Override
public void addTreeModelListener(TreeModelListener l) {
listeners.add(l);
}
#Override
public Object getChild(Object parent, int index) {
if(parent instanceof Param) return null;
if(structuredMap.containsKey(parent)) {
return structuredMap.get(parent).get(index);
}
// Root
return partNames.get(index);
}
#Override
public int getChildCount(Object parent) {
if(parent instanceof Param) return 0;
if(parent instanceof TreeRoot) return partNames.size();
return structuredMap.get(parent).size();
}
#Override
public int getIndexOfChild(Object parent, Object child) {
if(parent instanceof TreeRoot) return partNames.indexOf(child);
return structuredMap.get(parent).indexOf(child);
}
#Override
public Object getRoot() {
return root;
}
#Override
public boolean isLeaf(Object node) {
return (node instanceof Param);
}
#Override
public void removeTreeModelListener(TreeModelListener l) {
listeners.remove(l);
}
#Override
public void valueForPathChanged(TreePath path, Object newValue) {
// TODO Auto-generated method stub
}
}
While you are required to implement the valueForPathChanged() method, as defined in the TreeModel interface, you are free to leave it empty. FileTreeModel, cited here, is an example. The valueForPathChanged() method is typically used to support cell editing. As a concrete example, the implementation in DefaultTreeModel, seen here, "sets the user object of the TreeNode identified by path and posts a node changed [event]."
Using GWT 2.6.1, UiBinder, DataGrid.
Also using SingleSelectionModel to select a single row:
final SingleSelectionModel<User> selectionModel = new SingleSelectionModel<>(keyProvider);
Checkboxes column:
// checkboxes
Column<User, Boolean> checkBoxColumn = new Column<User, Boolean>(
new CheckboxCell(false, false)) {
#Override
public Boolean getValue(User user) {
return user.isChecked();
}
};
checkBoxColumn.setFieldUpdater(new FieldUpdater<User, Boolean>() {
#Override
public void update(int index, User user, Boolean value) {
user.setChecked(value);
}
});
So i store "checked" user state as a boolean field in the User entity class, without
using a SelectionModel at all.
Now I need to implement custom header checkbox to select/deselect all checkboxes in the column.
public class CheckboxHeader extends Header<Boolean> {
public CheckboxHeader(CheckboxCell cell) {
super(cell);
}
#Override
public Boolean getValue() {
return null;
}
}
Have no ideas how to implement properly this header class to add column in the DataGrid:
dataGrid.addColumn(checkBoxColumn, new CheckboxHeader(new CheckboxCell(false, false)));
Another trouble is to enable/disable all those checkboxes by checking other checkbox that
isn't in the DataGrid.
How can i retrieve all checkboxes from the column/selectionmodel/etc and enable/disable them one by one?
Appreciate any suggestions.
Mixing the data model (User entity) and the state of user interface (isSelected) is never a good idea.
This is how you can do it (replace T with your object, or create a column object that you can re-use):
Column<T, Boolean> checkColumn = new Column<T, Boolean>(new CheckboxCell()) {
#Override
public Boolean getValue(T object) {
return getSelectionModel().isSelected(object);
}
};
checkColumn.setFieldUpdater(new FieldUpdater<T, Boolean>() {
#Override
public void update(int index, T object, Boolean value) {
getSelectionModel().setSelected(object, value);
dataProvider.refresh();
}
});
myDataGrid.setSelectionModel(getSelectionModel(), DefaultSelectionEventManager.<T> createCheckboxManager(0));
Header<Boolean> selectAllHeader = new Header<Boolean>(new HeaderCheckbox()) {
#Override
public Boolean getValue() {
for (T item : getVisibleItems()) {
if (!getSelectionModel().isSelected(item)) {
return false;
}
}
return getVisibleItems().size() > 0;
}
};
selectAllHeader.setUpdater(new ValueUpdater<Boolean>() {
#Override
public void update(Boolean value) {
for (T object : getVisibleItems()) {
getSelectionModel().setSelected(object, value);
}
}
});
myDataGrid.addColumn(checkColumn, selectAllHeader);
I try to use DataBinding on SWT Widgets.
I´m wondering if there is a way to connect a Combo Box to an underlying String in the model.
So I have a String in the Model and a Combo on my View?
As the standard way is not working:
//View
DataBindingContext ctx = new DataBindingContext();
IObservableValue target1 = WidgetProperties.singleSelectionIndex().observe(combo);
IObservableValue model1 = BeanProperties.value(OutputVariable.class, "type").observe(outputVariable);
ctx.bindValue(target1, model1);
//Model
public void setType(String type) {
//TYPES is a constant with the possible Combo values
if (contains(TYPES, type)) {
String oldType = this.type;
this.type = type;
firePropertyChange("type", oldType, this.type);
}else {
throw new IllegalArgumentException();
}
}
I tried to use the fireIndexedPropertyChangeMethod which didn't worked either.
Is there a way to connect those two together? Maybe I have to use another WidgetProperties or BeanProperties method?
As a workaround I could maybe use a new Property in the model, which defines the combo selection index, connect this to the Combo and transfer changes of this index to the type Property and vice versa. But that seems not as a great solution to me.
Edit:
The Solution with a selectionIndex Property is working. But a cleaner method would still be nice as now a type Property change in the model has to reset the selectionIndex too and vice versa.
I have a clean solution now, which is to use a Converter.
//View
IObservableValue comboObservable = WidgetProperties.singleSelectionIndex().observe(combo);
IObservableValue viewTypeObservable = BeanProperties.value(DebugModel.class, "type").observe(debugModel);
IConverter viewTypeToIntConverter = createViewTypeToIntConverter();
UpdateValueStrategy toTargetStrategy = new UpdateValueStrategy();
toTargetStrategy.setConverter(viewTypeToIntConverter);
IConverter intToViewTypeConverter = createIntToViewTypeConverter();
UpdateValueStrategy toModelStrategy = new UpdateValueStrategy();
toModelStrategy.setConverter(intToViewTypeConverter);
DataBindingContext context = new DataBindingContext();
context.bindValue(comboObservable, viewTypeObservable, toModelStrategy, toTargetStrategy);
//Converter
private IConverter createIntToViewTypeConverter() {
return new IConverter() {
#Override
public Object convert(Object value) {
if(value instanceof Integer) {
for(ViewType type : ViewType.values()) {
if(type.toString().equals(ViewType.getStringAtIndex((int)value))) {
return type;
}
}
}
throw new IllegalArgumentException("We need an Integer to convert it but got an " + value.getClass());
}
#Override
public Object getFromType() {
return Integer.class;
}
#Override
public Object getToType() {
return ViewType.class;
}
};
}
private IConverter createViewTypeToIntConverter() {
return new IConverter() {
#Override
public Object convert(Object value) {
if(value instanceof ViewType) {
String[] viewTypes = ViewType.getStringValues();
for(int i=0;i<viewTypes.length;i++) {
if(viewTypes[i].equals(((ViewType)value).toString())) {
return i;
}
}
}
throw new IllegalArgumentException("We need a View Type to be converted but got a " + value.getClass());
}
#Override
public Object getFromType() {
return ViewType.class;
}
#Override
public Object getToType() {
return Integer.class;
}
};
}
//Model
public class DebugModel extends ModelObject {
private ViewType type;
public ViewType getType() {
return type;
}
public void setType(ViewType type) {
firePropertyChange("type", this.type, this.type = type);
}
}
//Just to complete the example, be sure the Model class extends a ModelObject class like this
public class ModelObject {
private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
this);
public void addPropertyChangeListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
public void addPropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(propertyName, listener);
}
protected void firePropertyChange(String propertyName, Object oldValue,
Object newValue) {
changeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
protected void fireIndexedPropertyChange(String propertyName, int index, Object oldValue, Object newValue) {
changeSupport.fireIndexedPropertyChange(propertyName, index, oldValue, newValue);
}
}
Of course you can outsource the Converters to custom classes, I used it this way just to show a quick solution here.