/*
* Based on CheckTreeSelectionModel (rev 120, 2007-07-20)
* By Santhosh Kumar T
* https://java.net/projects/myswing
*
* https://java.net/projects/myswing/sources/svn/content/trunk/src/skt/swing/tree/check/CheckTreeSelectionModel.java?rev=120
*/
/**
* 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 net.vhati.modmanager.ui.tree;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Stack;
import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import net.vhati.modmanager.ui.tree.PreorderEnumeration;
public class ChecklistTreeSelectionModel extends DefaultTreeSelectionModel {
private TreeModel model;
private boolean dig = true;
public ChecklistTreeSelectionModel( TreeModel model, boolean dig ) {
this.model = model;
this.dig = dig;
this.setSelectionMode( TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION );
}
public boolean isDigged() {
return dig;
}
/**
* Returns true if path1 is a 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;
}
/**
* Returns true a selected node exists in the subtree of a given unselected path.
* Returns false if the given path is itself selected.
*/
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;
}
/**
* Returns true if a given path is selected.
*
* If dig is true, then the path is assumed to be selected, if
* one of its ancestors 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 );
}
@Override
public void setSelectionPaths( TreePath[] paths ) {
if ( dig ) {
throw new UnsupportedOperationException();
} else {
super.setSelectionPaths( paths );
}
}
@Override
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;
List<TreePath> toBeRemoved = new ArrayList<TreePath>();
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} );
}
}
}
@Override
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);
}
}
}
/**
* Returns true if 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;
}
/**
* Unselects a given path, toggling ancestors if they were entirely selected.
*
* If any ancestor node of the given path is selected, it will be unselected,
* and all its descendants - except any within the given path - will be selected.
* The ancestor will have gone from fully selected to partially selected.
*
* Otherwise, the given path will be unselected, and nothing else will change.
*/
private void toggleRemoveSelection( TreePath path ) {
Stack<TreePath> stack = new Stack<TreePath>();
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 = stack.pop();
TreePath peekPath = ( stack.isEmpty() ? path : 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<TreePath> getAllSelectedPaths() {
Enumeration<TreePath> result = null;
TreePath[] treePaths = getSelectionPaths();
if ( treePaths == null ) {
List<TreePath> pathsList = Collections.emptyList();
result = Collections.enumeration( pathsList );
}
else {
List<TreePath> pathsList = Arrays.asList( treePaths );
result = Collections.enumeration( pathsList );
if ( dig ) {
result = new PreorderEnumeration( result, model );
}
}
return result;
}
}