com.jidesoft.swing.TreeSearchable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jide-oss Show documentation
Show all versions of jide-oss Show documentation
JIDE Common Layer (Professional Swing Components)
/*
* @(#)TreeSearchable.java
*
* Copyright 2002 - 2004 JIDE Software Inc. All rights reserved.
*/
package com.jidesoft.swing;
import javax.swing.*;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
/**
* TreeSearchable
is an concrete implementation of {@link Searchable} that enables the search function in
* JTree. It's very simple to use it. Assuming you have a JTree, all you need to do is to call
*
* JTree tree = ....;
* TreeSearchable searchable = new TreeSearchable(tree);
*
* Now the JTree will have the search function.
*
* There is very little customization you need to do to TreeSearchable. The only thing you might need is when the
* element in the JTree needs a special conversion to convert to string. If so, you can override
* convertElementToString() to provide you own algorithm to do the conversion.
*
* JTree tree = ....;
* TreeSearchable searchable = new TreeSearchable(tree) {
* protected String convertElementToString(Object object) {
* ...
* }
* };
*
*
* Additional customization can be done on the base Searchable class such as background and foreground color,
* keystrokes, case sensitivity. JTree actually has a simple searchable feature but has flaws. It will affect our
* searchable feature. To workaround it, you can override getNextMatch method and always return -1 when you create your
* JList.
*
* JTree tree = new JTree(...) {
* public TreePath getNextMatch(String prefix, int startingRow, Position.Bias bias) {
* return null;
* }
* };
*
*
*/
public class TreeSearchable extends Searchable implements TreeModelListener, PropertyChangeListener {
private boolean _recursive = false;
private transient List _treePathes;
public TreeSearchable(JTree tree) {
super(tree);
if (tree.getModel() != null) {
tree.getModel().addTreeModelListener(this);
}
tree.addPropertyChangeListener(JTree.TREE_MODEL_PROPERTY, this);
}
/**
* Checks if the searchable is recursive.
*
* @return true if searchable is recursive.
*/
public boolean isRecursive() {
return _recursive;
}
/**
* Sets the recursive attribute.
*
* If TreeSearchable is recursive, it will all tree nodes including those which are not visible to find the matching
* node. Obviously, if your tree has unlimited number of tree nodes or a potential huge number of tree nodes (such
* as a tree to represent file system), the recursive attribute should be false. To avoid this potential problem in
* this case, we default it to false.
*
* @param recursive the attribute
*/
public void setRecursive(boolean recursive) {
_recursive = recursive;
resetTreePathes();
}
@Override
public void uninstallListeners() {
super.uninstallListeners();
if (_component instanceof JTree) {
if (((JTree) _component).getModel() != null) {
((JTree) _component).getModel().removeTreeModelListener(this);
}
}
_component.removePropertyChangeListener(JTree.TREE_MODEL_PROPERTY, this);
}
@Override
protected void setSelectedIndex(int index, boolean incremental) {
if (!isRecursive()) {
if (incremental) {
((JTree) _component).addSelectionInterval(index, index);
}
else {
((JTree) _component).setSelectionRow(index);
}
((JTree) _component).scrollRowToVisible(index);
}
else {
Object elementAt = getElementAt(index);
if (elementAt instanceof TreePath) { // else case should never happen
TreePath path = (TreePath) elementAt;
if (incremental) {
((JTree) _component).addSelectionPath(path);
}
else {
((JTree) _component).setSelectionPath(path);
}
((JTree) _component).scrollPathToVisible(path);
}
}
}
@Override
protected int getSelectedIndex() {
if (!isRecursive()) {
int ai[] = ((JTree) _component).getSelectionRows();
return (ai != null && ai.length != 0) ? ai[0] : -1;
}
else {
TreePath[] treePaths = ((JTree) _component).getSelectionPaths();
if (treePaths != null && treePaths.length > 0) {
return getTreePathes().indexOf(treePaths[0]);
}
else
return -1;
}
}
@Override
protected Object getElementAt(int index) {
if (index == -1) {
return null;
}
if (!isRecursive()) {
return ((JTree) _component).getPathForRow(index);
}
else {
return getTreePathes().get(index);
}
}
@Override
protected int getElementCount() {
if (!isRecursive()) {
return ((JTree) _component).getRowCount();
}
else {
return getTreePathes().size();
}
}
/**
* Recursively go through the tree to populate the tree paths into a list and cache them.
*
* Tree paths list is only used when recursive attribute is true.
*/
protected void populateTreePaths() {
_treePathes = new ArrayList();
Object root = ((JTree) _component).getModel().getRoot();
populateTreePaths0(root, new TreePath(root), ((JTree) _component).getModel());
}
private void populateTreePaths0(Object node, TreePath path, TreeModel model) {
if (((JTree) _component).isRootVisible() || path.getLastPathComponent() != ((JTree) _component).getModel().getRoot()) {
// if root not visible, do not add root
_treePathes.add(path);
}
for (int i = 0; i < model.getChildCount(node); i++) {
Object childNode = model.getChild(node, i);
populateTreePaths0(childNode, path.pathByAddingChild(childNode), model);
}
}
/**
* Reset the cached tree paths list.
*
* Tree paths list is only used when recursive atattributes true.
*/
protected void resetTreePathes() {
_treePathes = null;
}
/**
* Gets the cached tree paths list. If it has never been cached before, this method will create the cache.
*
* Tree paths list is only used when recursive atattributes true.
*
* @return the tree paths list.
*/
protected List getTreePathes() {
if (_treePathes == null) {
populateTreePaths();
}
return _treePathes;
}
/**
* Converts the element in JTree to string. The element by default is TreePath. The returned value will be
* toString()
of the last path component in the TreePath.
*
* @param object the object to be converted
* @return the string representing the TreePath in the JTree.
*/
@Override
protected String convertElementToString(Object object) {
if (object instanceof TreePath) {
Object treeNode = ((TreePath) object).getLastPathComponent();
if (getComponent() instanceof JTree) {
JTree tree = (JTree) getComponent();
TreePath[] selectionPaths = tree.getSelectionPaths();
boolean selected = false;
if (selectionPaths != null) {
for (TreePath selectedPath : selectionPaths) {
if (selectedPath == object) {
selected = true;
break;
}
}
}
return tree.convertValueToText(treeNode, selected, tree.isExpanded((TreePath) object), tree.getModel().isLeaf(treeNode), tree.getRowForPath((TreePath) object), tree.hasFocus() && tree.getLeadSelectionPath() == object);
}
return treeNode.toString();
}
else if (object != null) {
return object.toString();
}
else {
return "";
}
}
public void treeNodesChanged(TreeModelEvent e) {
if (!isProcessModelChangeEvent()) {
return;
}
hidePopup();
resetTreePathes();
}
public void treeNodesInserted(TreeModelEvent e) {
if (!isProcessModelChangeEvent()) {
return;
}
hidePopup();
resetTreePathes();
}
public void treeNodesRemoved(TreeModelEvent e) {
if (!isProcessModelChangeEvent()) {
return;
}
hidePopup();
resetTreePathes();
}
public void treeStructureChanged(TreeModelEvent e) {
if (!isProcessModelChangeEvent()) {
return;
}
hidePopup();
resetTreePathes();
}
public void propertyChange(PropertyChangeEvent evt) {
if (JTree.TREE_MODEL_PROPERTY.equals(evt.getPropertyName())) {
hidePopup();
if (evt.getOldValue() instanceof TreeModel) {
((TreeModel) evt.getOldValue()).removeTreeModelListener(this);
}
if (evt.getNewValue() instanceof TreeModel) {
((TreeModel) evt.getNewValue()).addTreeModelListener(this);
}
resetTreePathes();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy