/*
* Copyright 2004-2014 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*//**
* MySwing: Advanced Swing Utilites
* Copyright (C) 2005 Santhosh Kumar T
* <p/>
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* <p/>
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package skt.swing.tree.check;
import skt.swing.tree.PreorderEnumeration;
import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.util.*;
/**
* @author Santhosh Kumar T
* @email santhosh@in.fiorano.com
*/
public class CheckTreeSelectionModel extends DefaultTreeSelectionModel {
private TreeModel model;
private boolean dig = true;
public CheckTreeSelectionModel(TreeModel model, boolean dig) {
this.model = model;
this.dig = dig;
setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
}
public boolean isDigged() {
return dig;
}
// tests whether there is any unselected node in the subtree of given path
public boolean isPartiallySelected(TreePath path) {
if (isPathSelected(path, true)) {
return false;
}
TreePath[] selectionPaths = getSelectionPaths();
if (selectionPaths == null) {
return false;
}
for (int j = 0; j < selectionPaths.length; j++) {
if (isDescendant(selectionPaths[j], path)) {
return true;
}
}
return false;
}
// tells whether given path is selected.
// if dig is true, then a path is assumed to be selected, if
// one of its ancestor is selected.
public boolean isPathSelected(TreePath path, boolean dig) {
if (!dig) {
return super.isPathSelected(path);
}
while (path != null && !super.isPathSelected(path)) {
path = path.getParentPath();
}
return path != null;
}
// is path1 descendant of path2
private boolean isDescendant(TreePath path1, TreePath path2) {
Object obj1[] = path1.getPath();
Object obj2[] = path2.getPath();
for (int i = 0; i < obj2.length; i++) {
if (obj1[i] != obj2[i]) {
return false;
}
}
return true;
}
public void setSelectionPaths(TreePath[] paths) {
if (dig) {
throw new UnsupportedOperationException("not implemented yet!!!");
} else {
super.setSelectionPaths(paths);
}
}
public void addSelectionPaths(TreePath[] paths) {
if (!dig) {
super.addSelectionPaths(paths);
return;
}
// unselect all descendants of paths[]
for (int i = 0; i < paths.length; i++) {
TreePath path = paths[i];
TreePath[] selectionPaths = getSelectionPaths();
if (selectionPaths == null) {
break;
}
ArrayList toBeRemoved = new ArrayList();
for (int j = 0; j < selectionPaths.length; j++) {
if (isDescendant(selectionPaths[j], path)) {
toBeRemoved.add(selectionPaths[j]);
}
}
super.removeSelectionPaths((TreePath[]) toBeRemoved.toArray(new TreePath[0]));
}
// if all siblings are selected then unselect them and select parent recursively
// otherwize just select that path.
for (int i = 0; i < paths.length; i++) {
TreePath path = paths[i];
TreePath temp = null;
while (areSiblingsSelected(path)) {
temp = path;
if (path.getParentPath() == null) {
break;
}
path = path.getParentPath();
}
if (temp != null) {
if (temp.getParentPath() != null) {
addSelectionPath(temp.getParentPath());
} else {
if (!isSelectionEmpty()) {
removeSelectionPaths(getSelectionPaths());
}
super.addSelectionPaths(new TreePath[]{temp});
}
} else {
super.addSelectionPaths(new TreePath[]{path});
}
}
}
// tells whether all siblings of given path are selected.
private boolean areSiblingsSelected(TreePath path) {
TreePath parent = path.getParentPath();
if (parent == null) {
return true;
}
Object node = path.getLastPathComponent();
Object parentNode = parent.getLastPathComponent();
int childCount = model.getChildCount(parentNode);
for (int i = 0; i < childCount; i++) {
Object childNode = model.getChild(parentNode, i);
if (childNode == node) {
continue;
}
if (!isPathSelected(parent.pathByAddingChild(childNode))) {
return false;
}
}
return true;
}
public void removeSelectionPaths(TreePath[] paths) {
if (!dig) {
super.removeSelectionPaths(paths);
return;
}
for (int i = 0; i < paths.length; i++) {
TreePath path = paths[i];
if (path.getPathCount() == 1) {
super.removeSelectionPaths(new TreePath[]{path});
} else {
toggleRemoveSelection(path);
}
}
}
// if any ancestor node of given path is selected then unselect it
// and selection all its descendants except given path and descendants.
// otherwise just unselect the given path
private void toggleRemoveSelection(TreePath path) {
Stack stack = new Stack();
TreePath parent = path.getParentPath();
while (parent != null && !isPathSelected(parent)) {
stack.push(parent);
parent = parent.getParentPath();
}
if (parent != null) {
stack.push(parent);
} else {
super.removeSelectionPaths(new TreePath[]{path});
return;
}
while (!stack.isEmpty()) {
TreePath temp = (TreePath) stack.pop();
TreePath peekPath = stack.isEmpty() ? path : (TreePath) stack.peek();
Object node = temp.getLastPathComponent();
Object peekNode = peekPath.getLastPathComponent();
int childCount = model.getChildCount(node);
for (int i = 0; i < childCount; i++) {
Object childNode = model.getChild(node, i);
if (childNode != peekNode) {
super.addSelectionPaths(new TreePath[]{temp.pathByAddingChild(childNode)});
}
}
}
super.removeSelectionPaths(new TreePath[]{parent});
}
public Enumeration getAllSelectedPaths() {
TreePath[] treePaths = getSelectionPaths();
if (treePaths == null) {
return Collections.enumeration(Collections.EMPTY_LIST);
}
Enumeration enumer = Collections.enumeration(Arrays.asList(treePaths));
if (dig) {
enumer = new PreorderEnumeration(enumer, model);
}
return enumer;
}
}