package com.griddynamics.jagger.webclient.client.components; import com.google.gwt.resources.client.ImageResource; import com.sencha.gxt.core.client.ValueProvider; import com.sencha.gxt.data.shared.TreeStore; import com.sencha.gxt.widget.core.client.event.BeforeCheckChangeEvent; import com.sencha.gxt.widget.core.client.event.BeforeCollapseItemEvent; import com.sencha.gxt.widget.core.client.event.BeforeExpandItemEvent; import com.sencha.gxt.widget.core.client.event.CheckChangeEvent; import com.sencha.gxt.widget.core.client.tree.Tree; /** * Abstract tree that allows check all children, check parent with no additional events. * @param <M> the model type * @param <C> the cell data type */ public abstract class AbstractTree<M, C> extends Tree<M, C> { /** * boolean disabled tree or not * uses for canceling events */ protected boolean disabled; /** * Constructor matches super class */ public AbstractTree(TreeStore<M> store, ValueProvider<? super M, C> valueProvider) { super(store, valueProvider); } public boolean isDisabled() { return disabled; } public void setDisabled(boolean disabled) { this.disabled = disabled; } { // runs after any constructor this.setCheckable(true); this.setCheckStyle(Tree.CheckCascade.NONE); this.setCheckNodes(Tree.CheckNodes.BOTH); this.addBeforeExpandHandler(new BeforeExpandItemEvent.BeforeExpandItemHandler<M>() { @Override public void onBeforeExpand(BeforeExpandItemEvent<M> event) { if (disabled) event.setCancelled(true); } }); this.addBeforeCollapseHandler(new BeforeCollapseItemEvent.BeforeCollapseItemHandler<M>() { @Override public void onBeforeCollapse(BeforeCollapseItemEvent<M> event) { if (disabled) event.setCancelled(true); } }); this.addBeforeCheckChangeHandler(new BeforeCheckChangeEvent.BeforeCheckChangeHandler<M>() { @Override public void onBeforeCheckChange(BeforeCheckChangeEvent<M> event) { if (disabled) event.setCancelled(true); } }); this.addCheckChangeHandler(new CheckChangeEvent.CheckChangeHandler<M>() { @Override public void onCheckChange(CheckChangeEvent<M> event) { AbstractTree.this.disableEvents(); CheckState state = event.getChecked(); M item = event.getItem(); AbstractTree.this.setStateToSubTree(item, state); if (state.equals(CheckState.CHECKED)) { AbstractTree.this.checkParent(item); AbstractTree.this.setExpanded(item, true, false); } else { unCheckParent(item); } AbstractTree.this.enableEvents(); check(item, state); } }); } /** * SetCheckState without firing any events * @param item model * @param state check state */ public void setCheckedNoEvents(M item, CheckState state) { disableEvents(); setChecked(item, state); enableEvents(); } /** * Check subtree * @param item root ir=tem for subtree * @param state state to be set to subtree */ protected void setStateToSubTree(M item, CheckState state) { if (store.hasChildren(item)) for (M child : store.getChildren(item)) { setChecked(child, state); setStateToSubTree(child, state); } } /** * Check parent. * Check state of parent = PARTIAL if parent has unchecked or partial checked children, and CHECKED otherwise. * @param item item witch parent should be checked */ protected void checkParent(M item) { M parent = store.getParent(item); if (parent == null) return; boolean hasUnchecked = false; for (M ch : store.getChildren(parent)) { if (!isChecked(ch) || CheckState.PARTIAL.equals(getChecked(ch))) { setChecked(parent, CheckState.PARTIAL); hasUnchecked = true; break; } } if (!hasUnchecked) setChecked(parent, CheckState.CHECKED); checkParent(parent); } /** * Uncheck parent. * Check state of parent = PARTIAL if parent has checked or partial checked children, and UNCHECKED otherwise. * @param item item witch parent should be checked */ protected void unCheckParent(M item) { M parent = store.getParent(item); if (parent == null) return; boolean hasChecked = false; for (M ch : store.getChildren(parent)) { if (AbstractTree.this.getChecked(ch).equals(CheckState.CHECKED) || this.getChecked(ch).equals(CheckState.PARTIAL)) { this.setChecked(parent, CheckState.PARTIAL); hasChecked = true; break; } } if (!hasChecked) this.setChecked(parent, CheckState.UNCHECKED); unCheckParent(parent); } /** * No images in tree */ @Override protected ImageResource calculateIconStyle(M model) { return null; } /** * disable ability to check/unCheck, collapse/expand actions */ @Override public void disable() { super.disable(); setDisabled(true); } /** * disable ability to check/unCheck, collapse/expand actions */ @Override public void enable() { super.enable(); setDisabled(false); } public void enableTree() { this.enable(); this.enableEvents(); } public void clearStore() { store.clear(); } /** * return false if CheckState = Tree.CheckState.UNCHECKED * true in other cases * @param model tree model * @return bool */ public boolean isChosen(M model) { return !CheckState.UNCHECKED.equals(getChecked(model)); } /** * Set check state for item with specified id * @param elementId id of item to set check state * @param checkState state to set */ public void setCheckState(String elementId, CheckState checkState) { M model = getStore().findModelWithKey(elementId); setChecked(model, checkState); } /** * Check event can goes here * @param item item that has been checked * @param state new check state of item in the tree */ protected abstract void check(M item, CheckState state); }