Java 在JTree上过滤 问题
在Java 在JTree上过滤 问题,java,swing,jtree,swingx,Java,Swing,Jtree,Swingx,在JTree上应用过滤,以避免某些节点/叶显示在JTree的渲染版本中。理想情况下,我正在寻找一个解决方案,允许有一个动态过滤器,但我已经很高兴,如果我可以得到一个静态过滤器的工作 为了使它更简单,让我们假设JTree只支持渲染,而不支持编辑。应该可以移动、添加和删除节点 一个例子是JTree上方的搜索字段,键入JTree只会显示匹配的子树 有几个限制:它将用于可以访问JDK和SwingX的项目中。我希望避免包含其他第三方LIB 我已经想到了一些可能的解决办法,但这两个似乎都不理想 方法 基于模
JTree
上应用过滤,以避免某些节点/叶显示在JTree
的渲染版本中。理想情况下,我正在寻找一个解决方案,允许有一个动态过滤器,但我已经很高兴,如果我可以得到一个静态过滤器的工作
为了使它更简单,让我们假设JTree
只支持渲染,而不支持编辑。应该可以移动、添加和删除节点
一个例子是JTree
上方的搜索字段,键入JTree
只会显示匹配的子树
有几个限制:它将用于可以访问JDK和SwingX的项目中。我希望避免包含其他第三方LIB
我已经想到了一些可能的解决办法,但这两个似乎都不理想
方法
基于模型的筛选
- 装饰
,过滤掉一些值。一个快速而肮脏的版本很容易写。过滤掉节点,并且在每次更改过滤器或委托时,装饰器可以触发整个树发生更改的事件(TreeModel
,根节点作为节点)。将其与恢复treeStructureChanged
的选择状态和扩展状态的侦听器相结合,您会得到一个或多或少可以工作的版本,但是来自JTree
的事件会被搞乱。这或多或少是本书中使用的方法TreeModel
- 装饰
,但尝试触发正确的事件。我还没有想出一个可行的版本。似乎需要委托树模型
的副本,以便在从委托模型中删除节点时能够触发具有正确子索引的事件。我想再过一段时间我就可以让它工作了,但感觉不对(过滤感觉像是视图应该做的事情,而不是模型)TreeModel
- 装饰用于创建初始
的任何数据结构。但是,这是完全不可重用的,可能与为TreeModel
TreeModel
- 我看了一下教室。尽管javadoc似乎建议您可以将其与
结合使用: 当与JTree关联时,条目对应于节点 我找不到JTree
(or)和RowFilter
类之间的任何链接。JTree
和Swing教程的标准实现似乎表明RowFilter
只能直接与RowFilter
一起使用(请参阅)。JTable
JTree上没有类似的方法
- 我还查看了javadoc。它有一个可用的组件适配器,
的javadoc表示ComponentAdapter
可以与目标组件交互,但我看不到如何在RowFilter
和RowFilter
之间建立链接JTree
- 我还没有研究
如何使用JTable
s处理过滤,也许在RowFilter
的修改版本上也可以这样做JTree
注意:这个问题可能是重复的,但这个问题仍然没有回答,问题很短,答案似乎不完整,所以我想发布一个新问题。如果没有做到这一点(常见问题解答没有对此提供明确的答案),我将更新这个3年前的问题,基于视图的过滤肯定是一条可行之路。您可以使用我在下面编写的示例。过滤树时的另一个常见做法是在过滤树时切换到列表视图,因为列表不需要显示需要显示其后代的隐藏节点 这绝对是一个可怕的代码(我刚才试着尽一切可能快速编写它),但它应该足以让您开始。只需在搜索框中键入查询并按Enter键,它就会过滤JTree的默认模型。(仅供参考,前90行只是生成的样板和布局代码。)
package com.example.tree;
导入java.awt.BorderLayout;
公共类FilteredJTreeExample扩展JFrame{
私有JPanel内容窗格;
私有JTextField textField;
/**
*启动应用程序。
*/
公共静态void main(字符串[]args){
invokeLater(新的Runnable(){
公开募捐{
试一试{
FilteredJTreeExample frame=新的FilteredJTreeExample();
frame.setVisible(true);
}捕获(例外e){
e、 printStackTrace();
}
}
});
}
/**
*创建框架。
*/
公共过滤器dejtreeeexample(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
立根(100100450300);
contentPane=newjpanel();
setboorder(新的EmptyBorder(5,5,5,5));
setLayout(新的BorderLayout(0,0));
setContentPane(contentPane);
JPanel面板=新的JPanel();
添加(面板,BorderLayout.NORTH);
GridBagLayout gbl_面板=新建GridBagLayout();
gbl_panel.columnWidths=新int[]{34116,0};
gbl_panel.rowHeights=newint[]{22,0};
gbl_panel.columnWeights=新的double[]{0.0,1.0,double.MIN_值};
gbl_panel.rowWeights=新的double[]{0.0,double.MIN_值};
面板。设置布局(gbl_面板);
JLabel lblFilter=新的JLabel(“过滤器:”);
GridBagConstraints GBClblfilter=新的GridBagConstraints();
GBClblfilter.anchor=GridBagConstrai
package com.example.tree;
import java.awt.BorderLayout;
public class FilteredJTreeExample extends JFrame {
private JPanel contentPane;
private JTextField textField;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
FilteredJTreeExample frame = new FilteredJTreeExample();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public FilteredJTreeExample() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
JPanel panel = new JPanel();
contentPane.add(panel, BorderLayout.NORTH);
GridBagLayout gbl_panel = new GridBagLayout();
gbl_panel.columnWidths = new int[]{34, 116, 0};
gbl_panel.rowHeights = new int[]{22, 0};
gbl_panel.columnWeights = new double[]{0.0, 1.0, Double.MIN_VALUE};
gbl_panel.rowWeights = new double[]{0.0, Double.MIN_VALUE};
panel.setLayout(gbl_panel);
JLabel lblFilter = new JLabel("Filter:");
GridBagConstraints gbc_lblFilter = new GridBagConstraints();
gbc_lblFilter.anchor = GridBagConstraints.WEST;
gbc_lblFilter.insets = new Insets(0, 0, 0, 5);
gbc_lblFilter.gridx = 0;
gbc_lblFilter.gridy = 0;
panel.add(lblFilter, gbc_lblFilter);
JScrollPane scrollPane = new JScrollPane();
contentPane.add(scrollPane, BorderLayout.CENTER);
final JTree tree = new JTree();
scrollPane.setViewportView(tree);
textField = new JTextField();
GridBagConstraints gbc_textField = new GridBagConstraints();
gbc_textField.fill = GridBagConstraints.HORIZONTAL;
gbc_textField.anchor = GridBagConstraints.NORTH;
gbc_textField.gridx = 1;
gbc_textField.gridy = 0;
panel.add(textField, gbc_textField);
textField.setColumns(10);
textField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
TreeModel model = tree.getModel();
tree.setModel(null);
tree.setModel(model);
}
});
tree.setCellRenderer(new DefaultTreeCellRenderer() {
private JLabel lblNull = new JLabel();
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean arg2, boolean arg3, boolean arg4, int arg5, boolean arg6) {
Component c = super.getTreeCellRendererComponent(tree, value, arg2, arg3, arg4, arg5, arg6);
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
if (matchesFilter(node)) {
c.setForeground(Color.BLACK);
return c;
}
else if (containsMatchingChild(node)) {
c.setForeground(Color.GRAY);
return c;
}
else {
return lblNull;
}
}
private boolean matchesFilter(DefaultMutableTreeNode node) {
return node.toString().contains(textField.getText());
}
private boolean containsMatchingChild(DefaultMutableTreeNode node) {
Enumeration<DefaultMutableTreeNode> e = node.breadthFirstEnumeration();
while (e.hasMoreElements()) {
if (matchesFilter(e.nextElement())) {
return true;
}
}
return false;
}
});
}
}
public abstract class TellapicModelFilter extends DefaultTreeTableModel {
protected Map<AbstractMutableTreeTableNode,
AbstractMutableTreeTableNode> family;
protected Map<AbstractMutableTreeTableNode,
AbstractMutableTreeTableNode> filter;
protected MyTreeTable treeTable;
private boolean withChildren;
private boolean withParents;
/**
*
* @param model
*/
public TellapicModelFilter(MyTreeTable treeTable) {
this(treeTable, false, false);
}
/**
*
* @param treeTable
* @param wp
* @param wc
*/
public TellapicModelFilter(MyTreeTable treeTable, boolean wp, boolean wc) {
super(new DefaultMutableTreeTableNode("filteredRoot"));
this.treeTable = treeTable;
setIncludeChildren(wc);
setIncludeParents(wp);
}
/**
*
*/
public void filter() {
filter = new HashMap<AbstractMutableTreeTableNode, AbstractMutableTreeTableNode>();
family = new HashMap<AbstractMutableTreeTableNode, AbstractMutableTreeTableNode>();
AbstractMutableTreeTableNode filteredRoot = (AbstractMutableTreeTableNode) getRoot();
AbstractMutableTreeTableNode root = (AbstractMutableTreeTableNode) treeTable.getTreeTableModel().getRoot();
filterChildren(root, filteredRoot);
for(AbstractMutableTreeTableNode node : family.keySet())
node.setParent(null);
for(AbstractMutableTreeTableNode node : filter.keySet())
node.setParent(filter.get(node));
}
/**
*
* @param node
* @param filteredNode
*/
private void filterChildren(AbstractMutableTreeTableNode node, AbstractMutableTreeTableNode filteredNode) {
int count = node.getChildCount();
for(int i = 0; i < count; i++) {
AbstractMutableTreeTableNode child = (AbstractMutableTreeTableNode) node.getChildAt(i);
family.put(child, node);
if (shouldBeFiltered(child)) {
filter.put(child, filteredNode);
if (includeChildren())
filterChildren(child, child);
} else {
filterChildren(child, filteredNode);
}
}
}
/**
*
*/
public void restoreFamily() {
for(AbstractMutableTreeTableNode child : family.keySet()) {
AbstractMutableTreeTableNode parent = family.get(child);
child.setParent(parent);
}
}
/**
*
* @param node
* @return
*/
public abstract boolean shouldBeFiltered(AbstractMutableTreeTableNode node);
/**
* Determines if parents will be included in the filtered result. This DOES NOT means that parent will be filtered
* with the filter criteria. Instead, if a node {@code}shouldBeFiltered{@code} no matter what the parent node is,
* include it in the filter result. The use of this feature is to provide contextual data about the filtered node,
* in the terms of: "where was this node that belongs to?"
*
* @return True is parents should be included anyhow.
*/
public boolean includeParents() {
return withParents;
}
/**
* Determines if children should be filtered. When a node {@code}shouldBeFiltered{@code} you can stop the filtering
* process in that node by setting: {@code}setIncludeChildren(false){@code}. In other words, if you want to filter
* all the tree, {@code}includeChildren{@code} should return true.
*
* By letting this method return {@code}false{@code} all children of the node filtered will be automatically added
* to the resulting filter. That is, children aren't filtered with the filter criteria and they will be shown with
* their parent in the filter result.
*
* @return True if you want to filter all the tree.
*/
public boolean includeChildren() {
return withChildren;
}
/**
*
* @param include
*/
public void setIncludeParents(boolean include) {
withParents = include;
}
/**
*
* @param include
*/
public void setIncludeChildren(boolean include) {
withChildren = include;
}
@Override
public void setTreeTableModel(TreeTableModel treeModel) {
if (!(treeModel instanceof TellapicModelFilter))
model = treeModel;
super.setTreeTableModel(treeModel);
}
public void setModelFilter(TellapicModelFilter mf) {
if (modelFilter != null) {
modelFilter.restoreFamily();
setTreeTableModel(getUnfilteredModel());
}
// Is this necessary?
if (mf == null) {
setTreeTableModel(getUnfilteredModel());
} else {
modelFilter = mf;
modelFilter.filter();
setTreeTableModel(modelFilter);
}
}
public class FilteredSceneModel extends DefaultTreeModel {
public static boolean isSceneItem(Object child) {
return !(child instanceof DataItem);
}
public FilteredSceneModel(RootSceneNode root, SelectionModel sm) {
super(root, sm);
}
private boolean isSceneFolder(Object node) {
return node instanceof RootSceneNode || node instanceof Floor;
}
@Override
public AbstractSceneItem getChild(Object parent, int index) {
AbstractSceneItem asi = (AbstractSceneItem) parent;
if (isSceneItem(parent)) {
int dex = 0;
for (AbstractSceneItem child : asi.children) {
if (isSceneItem(child)) {
if (dex == index) {
return child;
}
dex++;
}
}
}
System.out.println("illegal state for: " + parent + " at index: " + index);
return asi.getChildAt(index);
}
@Override
public int getChildCount(Object parent) {
if (isSceneItem(parent)) {
AbstractSceneItem asi = (AbstractSceneItem) parent;
int count = 0;
for (AbstractSceneItem child : asi.children) {
if (isSceneItem(child)) {
count++;
}
}
return count;
}
return -1;
}
@Override
public int getIndexOfChild(Object parent, Object childItem) {
if (isSceneItem(parent)) {
AbstractSceneItem asi = (AbstractSceneItem) parent;
int count = 0;
for (AbstractSceneItem child : asi.children) {
if (isSceneItem(child)) {
if (child == childItem) {
return count;
}
count++;
}
}
}
return -1;
}
@Override
public boolean isLeaf(Object node) {
if (isSceneItem(node)) {
if (isSceneFolder(node)) {
return false;
}
}
return true;
}
@Override
public void activeFloorChanged(Floor floor) {
for (AbstractSceneItem asi : floor) {
if (isSceneItem(asi)) {
nodeChanged(asi);
}
}
}
@Override
protected void renamed(AbstractSceneItem asi) {
if (isSceneItem(asi)) {
nodeChanged(asi);
System.out.println("scene only model renamed: " + asi.fullPathToString());
}
}
@Override
public void nodeChanged(TreeNode tn) {
if (isSceneItem(tn)) {
filteredNodeChanged(tn);
}
}
@Override
public void nodeStructureChanged(TreeNode tn) {
if (isSceneItem(tn)) {
super.nodeStructureChanged(tn);
}
}
private void filteredNodeChanged(TreeNode node) {
if (listenerList != null && node != null) {
TreeNode parent = node.getParent();
if (parent != null) {
int anIndex = getIndexOfChild(parent, node);
if (anIndex != -1) {
int[] cIndexs = new int[1];
cIndexs[0] = anIndex;
nodesChanged(parent, cIndexs);
}
} else if (node == getRoot()) {
nodesChanged(node, null);
}
}
}
@Override
public void nodesChanged(TreeNode node, int[] childIndices) {
if (node != null) {
if (childIndices != null) {
int cCount = childIndices.length;
if (cCount > 0) {
Object[] cChildren = new Object[cCount];
for (int counter = 0; counter < cCount; counter++) {
cChildren[counter] = getChild(node, childIndices[counter]);
}
fireTreeNodesChanged(this, getPathToRoot(node),
childIndices, cChildren);
}
} else if (node == getRoot()) {
fireTreeNodesChanged(this, getPathToRoot(node), null, null);
}
}
}
}
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.tree.*;
public class FilterTreeDemo {
public static void main(String[] args) throws FileNotFoundException {
EventQueue.invokeLater(new ShowIt());
}
}
class FiltNode extends DefaultMutableTreeNode {
FiltNode( Object user_obj ){
super( user_obj );
}
FiltNode m_counterpart_node;
// public String toString(){
// // hash code demonstrates (as you toggle) that these are not the same nodes...
// return super.toString() + " (" + hashCode() + ")";
// }
}
class FilterPair {
TreeCoupling m_on_coupling, m_off_coupling;
boolean m_filter_on = true;
JFrame m_main_frame;
FiltNode m_on_root = new FiltNode( "root" );
FiltNode m_off_root = new FiltNode( "root" );
// needed to prevent infinite calling between models...
boolean m_is_propagated_call = false;
FilterPair( JFrame main_frame ){
m_on_root.m_counterpart_node = m_off_root;
m_off_root.m_counterpart_node = m_on_root;
m_on_coupling = new TreeCoupling( true );
m_off_coupling = new TreeCoupling( false );
m_main_frame = main_frame;
// starts by toggling to OFF (i.e. before display)
toggle_filter();
}
// this is the filter method for this particular FilterPair...
boolean is_filtered_out( MutableTreeNode node ){
return node.toString().contains( "nobble");
}
class TreeCoupling {
class FilterTreeModel extends DefaultTreeModel {
FilterTreeModel( TreeNode root ){
super( root );
}
public void insertNodeInto(MutableTreeNode new_child, MutableTreeNode parent, int index){
// aliases for convenience
FiltNode new_filt_node = (FiltNode)new_child;
FiltNode parent_filt_node = (FiltNode)parent;
FiltNode new_counterpart_filt_node = null;
FiltNode counterpart_parent_filt_node = null;
// here and below the propagation depth test is used to skip code which is leading to another call to
// insertNodeInto on the counterpart TreeModel...
if( ! m_is_propagated_call ){
// NB the counterpart new FiltNode is given exactly the same user object as its counterpart: no duplication
// of the user object...
new_counterpart_filt_node = new FiltNode( new_filt_node.getUserObject() );
counterpart_parent_filt_node = parent_filt_node.m_counterpart_node;
// set up the 2 counterpart relationships between the node in the ON tree and the node in the OFF tree
new_counterpart_filt_node.m_counterpart_node = new_filt_node;
new_filt_node.m_counterpart_node = new_counterpart_filt_node;
}
if( TreeCoupling.this == m_on_coupling ){
// ... we are in the ON coupling
// if first call and the parent has no counterpart (i.e. in the OFF coupling) sthg has gone wrong
if( ! m_is_propagated_call && counterpart_parent_filt_node == null ){
throw new NullPointerException();
}
if( ! is_filtered_out( new_filt_node ) ){
// only insert here (ON coupling) if the node is NOT filtered out...
super.insertNodeInto( new_filt_node, parent_filt_node, index);
}
else {
// enable the originally submitted new node (now rejected) to be unlinked and garbage-collected...
// (NB if you suspect the first line here is superfluous, try commenting out and see what happens)
new_filt_node.m_counterpart_node.m_counterpart_node = null;
new_filt_node.m_counterpart_node = null;
}
if( ! m_is_propagated_call ){
// as we are in the ON coupling we can't assume that the index value should be passed on unchanged to the
// OFF coupling: some siblings (of lower index) may be missing here... but we **do** know that the previous
// sibling (if there is one) of the new node has a counterpart in the OFF tree... so get the index of its
// OFF counterpart and add 1...
int off_index = 0;
if( index > 0 ){
FiltNode prev_sib = (FiltNode)parent_filt_node.getChildAt( index - 1 );
off_index = counterpart_parent_filt_node.getIndex( prev_sib.m_counterpart_node ) + 1;
}
m_is_propagated_call = true;
m_off_coupling.m_tree_model.insertNodeInto( new_counterpart_filt_node, counterpart_parent_filt_node, off_index);
}
}
else {
// ... we are in the OFF coupling
super.insertNodeInto( new_filt_node, parent_filt_node, index);
if( ! m_is_propagated_call ){
// we are in the OFF coupling: it is perfectly legitimate for the parent to have no counterpart (i.e. in the
// ON coupling: indicates that it, or an ancestor of it, has been filtered out)
if( counterpart_parent_filt_node != null ){
// OTOH, if the parent **is** available, we can't assume that the index value should be passed on unchanged:
// some siblings of the new incoming node (of lower index) may have been filtered out... to find the
// correct index value we track down the index value until we reach a node which has a counterpart in the
// ON coupling... or if not found the index must be 0
int on_index = 0;
if( index > 0 ){
for( int i = index - 1; i >= 0; i-- ){
FiltNode counterpart_sib = ((FiltNode)parent_filt_node.getChildAt( i )).m_counterpart_node;
if( counterpart_sib != null ){
on_index = counterpart_parent_filt_node.getIndex( counterpart_sib ) + 1;
break;
}
}
}
m_is_propagated_call = true;
m_on_coupling.m_tree_model.insertNodeInto( new_counterpart_filt_node, counterpart_parent_filt_node, on_index);
}
else {
// ... no ON-coupling parent node "counterpart": the new ON node must be discarded
new_filt_node.m_counterpart_node = null;
}
}
}
m_is_propagated_call = false;
}
}
JTree m_tree;
FilterTreeModel m_tree_model;
TreeCoupling( boolean on ){
m_tree = new JTree();
m_tree_model = on ? new FilterTreeModel( m_on_root ) : new FilterTreeModel( m_off_root );
m_tree.setModel( m_tree_model );
}
}
void toggle_filter(){
m_filter_on = ! m_filter_on;
m_main_frame.setTitle( m_filter_on? "FilterTree - ON (Ctrl-F6 to toggle)" : "FilterTree - OFF (Ctrl-F6 to toggle)" );
}
TreeCoupling getCurrCoupling(){
return m_filter_on? m_on_coupling : m_off_coupling;
}
}
class ShowIt implements Runnable {
@Override
public void run() {
JFrame frame = new JFrame("FilterTree");
final FilterPair pair = new FilterPair( frame );
final JScrollPane jsp = new JScrollPane( pair.getCurrCoupling().m_tree );
Action toggle_between_views = new AbstractAction( "toggle filter" ){
@Override
public void actionPerformed(ActionEvent e) {
pair.toggle_filter();
jsp.getViewport().setView( pair.getCurrCoupling().m_tree );
jsp.requestFocus();
}};
JPanel cpane = (JPanel)frame.getContentPane();
cpane.getActionMap().put("toggle between views", toggle_between_views );
InputMap new_im = new InputMap();
new_im.put(KeyStroke.getKeyStroke(KeyEvent.VK_F6, InputEvent.CTRL_DOWN_MASK), "toggle between views");
cpane.setInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, new_im);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(jsp);
frame.pack();
frame.setBounds(50, 50, 800, 500);
frame.setVisible(true);
// populate the tree(s) NB we are currently viewing the OFF tree
FilterPair.TreeCoupling curr_coupling = pair.getCurrCoupling();
curr_coupling.m_tree_model.insertNodeInto( new FiltNode( "scrags 1" ), (FiltNode)curr_coupling.m_tree_model.getRoot(), 0 );
FiltNode d2 = new FiltNode( "scrags 2" );
curr_coupling.m_tree_model.insertNodeInto( d2, (FiltNode)curr_coupling.m_tree_model.getRoot(), 1 );
curr_coupling.m_tree_model.insertNodeInto( new FiltNode( "scrags 3" ), (FiltNode)curr_coupling.m_tree_model.getRoot(), 2 );
curr_coupling.m_tree_model.insertNodeInto( new FiltNode( "scrags 2.1" ), d2, 0 );
// this will be filtered out of the ON tree
FiltNode nobble = new FiltNode( "nobble" );
curr_coupling.m_tree_model.insertNodeInto( nobble, d2, 1 );
// this will also be filtered out of the ON tree
FiltNode son_of_nobble = new FiltNode( "son of nobble");
curr_coupling.m_tree_model.insertNodeInto( son_of_nobble, nobble, 0 );
curr_coupling.m_tree_model.insertNodeInto( new FiltNode( "peewit (granddaughter of n****e)"), son_of_nobble, 0 );
// expand the OFF tree
curr_coupling.m_tree.expandPath( new TreePath( curr_coupling.m_tree_model.getRoot() ) );
curr_coupling.m_tree.expandPath( new TreePath( d2.getPath() ) );
curr_coupling.m_tree.expandPath( new TreePath( nobble.getPath() ) );
curr_coupling.m_tree.expandPath( new TreePath( son_of_nobble.getPath() ) );
// switch view (programmatically) to the ON tree
toggle_between_views.actionPerformed( null );
// expand the ON tree
curr_coupling = pair.getCurrCoupling();
curr_coupling.m_tree.expandPath( new TreePath( curr_coupling.m_tree_model.getRoot() ) );
curr_coupling.m_tree.expandPath( new TreePath( d2.m_counterpart_node.getPath() ) );
// try to expand the counterpart of "nobble"... there shouldn't be one...
FiltNode nobble_counterpart = nobble.m_counterpart_node;
if( nobble_counterpart != null ){
curr_coupling.m_tree.expandPath( new TreePath( nobble_counterpart.getPath() ) );
System.err.println( "oops..." );
}
else {
System.out.println( "As expected, node \"nobble\" has no counterpart in the ON coupling" );
}
// try inserting a node into the ON tree which will immediately be "rejected" by the ON tree (due to being
// filtered out before the superclass insertNodeInto is called), but will nonetheless appear in the
// OFF tree as it should...
curr_coupling.m_tree_model.insertNodeInto( new FiltNode( "yet another nobble"), d2.m_counterpart_node, 0 );
}
}
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
public class JTreeExample
{
public static void main( final String[] args ) throws Exception
{
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
// The only correct way to create a SWING Frame...
EventQueue.invokeAndWait( new Runnable()
{
@Override
public void run()
{
swingMain();
}
} );
}
protected static void swingMain()
{
final JFrame f = new JFrame( "JTree Test" );
f.setLocationByPlatform( true );
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
final int items = 5;
final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode( "JTree", true );
final DefaultTreeModel myModel = new DefaultTreeModel( rootNode );
final Box buttonBox = new Box( BoxLayout.X_AXIS );
for( int i = 0; i < items; i++ )
{
final String name = "Node " + i;
final DefaultMutableTreeNode newChild = new DefaultMutableTreeNode( name );
rootNode.add( newChild );
final JButton b = new JButton( "Show/Hide " + i );
buttonBox.add( b );
b.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( final ActionEvent e )
{
// If the node has a Text, set it to null, otherwise reset it
newChild.setUserObject( newChild.getUserObject() == null ? name : null );
myModel.nodeStructureChanged( newChild.getParent() );
}
} );
}
final JTree tree = new JTree( myModel );
tree.setRowHeight( 0 );
tree.setCellRenderer( new JTreeExample.TreeRenderer() );
f.add( tree, BorderLayout.CENTER );
f.add( buttonBox, BorderLayout.SOUTH );
f.setSize( 600, 500 );
f.setVisible( true );
}
public static class TreeRenderer extends DefaultTreeCellRenderer
{
@Override
public Component getTreeCellRendererComponent( final JTree tree, final Object value, final boolean selected,
final boolean expanded, final boolean leaf, final int row, final boolean hasFocus )
{
// Invoke default Implementation, setting all values of this
super.getTreeCellRendererComponent( tree, value, selected, expanded, leaf, row, hasFocus );
if( !isNodeVisible( (DefaultMutableTreeNode)value ) )
{
setPreferredSize( new Dimension( 0, 0 ) );
}
else
{
setPreferredSize( new Dimension( 200, 15 ) );
}
return this;
}
}
public static boolean isNodeVisible( final DefaultMutableTreeNode value )
{
// In this example all Nodes without a UserObject are invisible
return value.getUserObject() != null;
}
}