Reputation: 1052
I'm using a custom Dialog with a CheckboxTreeViewer inside my GMF Editor, which works fine so far, as you can see below:
After closing the Dialog, the selected element are saved so far. Now my Problem:
When I open the dialog again, all elements are unchecked. So I thought it would be easy to tell the treeViewer that specific elements should be initially checked.
But it turned out that it's not that easy, as the Tree initially consist of the root element. The other elements are not added until the tree expands. Elements are added by calling the getChildren(Object parentElement) of the ContentProvider.
So it seems that I can't check specific elements initially, but rather have to provide a dynamically approach. I'm looking for something like an element added listener, but there seems none to exist.
Here is the part, where I'm creating the CheckboxTreeViewer
Composite container = (Composite) super.createDialogArea(parent);
tv = new CheckboxTreeViewer(container, SWT.MULTI | SWT.H_SCROLL
| SWT.V_SCROLL);
tv.getTree().setLayoutData(new GridData(GridData.FILL_BOTH));
tv.setAutoExpandLevel(2);
tv.setContentProvider(new FeaturePropertyDialogContentProvider(this));
tv.setLabelProvider(new FeaturePropertyDialogLabelProvider());
tv.setInput(productLine);
tv.setExpandPreCheckFilters(true);
return container;
And here is the getChildren method of my ContentProvider:
@Override
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof PL) {
PL p = (PL) parentElement;
return new Object[] { p.getPropertyList() };
}
else if (parentElement instanceof PropertyList) {
PropertyList propertyList = (PropertyList) parentElement;
return propertyList.getGeneralPlatforms().toArray();
} else if (parentElement instanceof GeneralPlatform) {
GeneralPlatform platform = (GeneralPlatform) parentElement;
return platform.getHardwareElements().toArray();
}
} else {
return null;
}
}
Any ideas on this?
--------------------Solution---------------------
Found the following solution by myself, which works fine for me so far:
tv.expandAll();
tv.setCheckedElements(preSelectedProperties.toArray());
tv.collapseAll();
tv.expandToLevel(2);
Upvotes: 4
Views: 3576
Reputation: 922
You should add a listener on the tree viewer to get a notification when a node gets expanded (method addTreeListener() on TreeViewer). At that time, the nodes are already in the tree, and you can check the check-box.
Here is a snippet of a tree that sets the check state when the tree is expanded:
public class Snippet {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
final Map<String, Boolean> userChecks = new HashMap<>();
final CheckboxTreeViewer tv = new CheckboxTreeViewer(shell, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
GridData gridData = new GridData(GridData.FILL_BOTH);
tv.getTree().setLayoutData(gridData);
final FeaturePropertyDialogContentProvider provider = new FeaturePropertyDialogContentProvider(tv);
tv.setContentProvider(provider);
tv.setInput("root");
tv.addCheckStateListener(new ICheckStateListener() {
@Override
public void checkStateChanged(CheckStateChangedEvent event) {
userChecks.put((String) event.getElement(), event.getChecked());
}
});
tv.addTreeListener(new ITreeViewerListener() {
@Override
public void treeCollapsed(TreeExpansionEvent event) {
}
@Override
public void treeExpanded(TreeExpansionEvent event) {
final Object element = event.getElement();
final Object[] children = provider.getChildren(element);
for (Object child : children) {
if (userChecks.containsKey(child)) {
tv.setChecked(child, userChecks.get(child));
} else if (child.equals("b")) {
tv.setChecked(child, true);
}
}
}
});
shell.setSize(200, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
private static class FeaturePropertyDialogContentProvider implements ITreeContentProvider {
final CheckboxTreeViewer tv;
private FeaturePropertyDialogContentProvider(CheckboxTreeViewer tv) {
this.tv = tv;
}
@Override
public Object[] getElements(Object inputElement) {
return this.getChildren(inputElement);
}
@Override
public Object[] getChildren(Object parentElement) {
switch ((String) parentElement) {
case "root":
return new String[]{"1"};
case "1":
return new String[]{"a", "b", "c"};
default:
return new String[0];
}
}
@Override
public Object getParent(Object element) {
return null;
}
@Override
public boolean hasChildren(Object element) {
return this.getChildren(element).length > 0;
}
@Override
public void dispose() {
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
}
}
In this example, I only compare the node value with the string "b".
Edit: in this context, elements are immutable (for some reason they cannot be changed), and the solution with wrapping objects in tree nodes is not accepted (see comments).
In that case, you can save user check actions in a map, and when setting checked/unchecked state, search for any previous user check actions. I think it is reasonable to assume the map is simple and will not grow too much. User has to check a lot of nodes to get it big :)
I updated the snippet to keep track of user checks.
Upvotes: 1