I was facing the same issue and came up with a client side only solution that is fast and accurately throws the proper selection events.
To enable it, in your tree item renderer, add this widget listener:
treeitem.setWidgetListener(Events.ON_CLICK, "selectSubTreesOnItemClick(this, event);");
In your case (template), do it like this:
<tree xmlns:w="client">
...
<treeitem w:onClick="selectSubTreesOnItemClick(this, event)">
Then, import the following js file (in your zul, add <script src="/js/treeselection.js" />
somewhere):
function selectSubTreesOnItemClick(item, event) {
// if clicked outside of check box, reset selection
if (!jq(event.target).is('.z-treerow')) {
item.getTree().clearSelection();
// re-select clicked node
item.setSelected(true);
}
privateSelectDescendants(item);
privateSelectParentIfAllDescendantsSelected(item);
// update selection
item.getTree().fireOnSelect(item);
// prevent interference of the tree's default click behavior (like selection of the clicked node ;) ).
event.stop();
}
/**
* @param parent if selected, selects all children (and grand-children and so on), otherwise
* removes them from selection.
*/
function privateSelectDescendants(parent) {
var items = parent.getTree().itemIterator();
// find all descendants of parent
while (items.hasNext() && items.next() !== parent) {}
var stopAt = privateGetFirstNonDescendant(parent);
// check descendants
while (items.hasNext()) {
var descendant = items.next();
if (descendant === stopAt) {
break;
} else {
descendant.setSelected(parent.isSelected());
}
}
}
/**
* @param item parent will be selected if item and all its siblings are selected, otherwise
* unselected. Repeated for grandparents, great-grandparents, and so on.
*/
function privateSelectParentIfAllDescendantsSelected(item) {
if (item.getParentItem() != null) {
var parent = item.getParentItem();
// find all descendants of parent
var items = parent.getTree().itemIterator();
while (items.hasNext()) {
if (items.next() === parent){
break;
}
}
var stopAt = privateGetFirstNonDescendant(parent);
// check descendants
var allDescendantsSelected = true;
while (items.hasNext()) {
var child = items.next();
if (child === stopAt) {
break;
} else if (!child.isSelected()) {
allDescendantsSelected = false;
break;
}
}
parent.setSelected(allDescendantsSelected);
// continue with grandparents
privateSelectParentIfAllDescendantsSelected(parent);
}
}
/**
* @param item
* @returns the next item that is on the same or a higher level as item.
* Undefined if item is the last node or only followed by children.
*/
function privateGetFirstNonDescendant(item) {
var result = item.nextSibling;
while (!result && item.getParentItem() != null) {
item = item.getParentItem();
result = item.nextSibling;
}
return result;
}
(Un)Selecting a tree node will also (un)select its descendants. Furthermore, if all the node's siblings are selected, the parent node will be selected, otherwise unselected (this goes all the way up to the root node).