Strikethrough a node in a JTree - java

In my project i have a Jtree with custom node (which extends DefaultMutableTreeNode). Each node is associated with a boolean value. When the boolean is False i woul like to strike the texte of my node. So for example :
node 1
node1.1
node1.2
node 2
node2.1
...
I tried to create a new Font but i don't find any properties to strike the text and i only managed to add my custom font to the whole tree and not node by node.
I think that i should use the TreeRenderer but i can't find any method to help me strike the node.
Does someone have an idea on i can do it ?

Check out the example below. For keeping it simple, I am just striking through the selected node. You need to, of course, use your own check on the value.
package snippet;
import java.awt.Component;
import java.awt.Font;
import java.awt.font.TextAttribute;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultTreeCellRenderer;
public class JTreeTest extends JFrame {
private final class StrikeoutCellRenderer extends DefaultTreeCellRenderer {
private static final long serialVersionUID = 1L;
#Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
boolean leaf, int row, boolean hasFocus) {
Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
Font font = c.getFont();
Map attributes = font.getAttributes();
if(sel)
attributes.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
else
attributes.remove(TextAttribute.STRIKETHROUGH);
Font newFont = new Font(attributes);
c.setFont(newFont);
return c;
}
}
private static final long serialVersionUID = 1L;
public JTreeTest() {
super(JTreeTest.class.getName());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents();
}
private void initComponents() {
JTree tree = new JTree();
tree.setCellRenderer(new StrikeoutCellRenderer());
add(tree);
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override public void run() {
JTreeTest t = new JTreeTest();
t.setVisible(true);
}
});
}
}
Note that even if the node doesn't need a strike through, you need to reset the attribute, since a single component is used for painting all the nodes.

The simplest way would be to define a renderer (extending DefaultTreeCellRenderer) and call setText() passing html string like this "<html><u>node1.2</u></html>" as value for the strikethrough.

Related

Add content to cell when user clicks it

I have a functionality in a JTable, that when user clicks the cell, it removes certain character in it (like when there's content - Hello, when user clicks it, it shows Hello). When it's no longer edited, it shows - Hello again.
My problem is that when some cell is selected (but not being edited yet) and I start typing Hi, it doesn't remove the character, so the editable cell looks like - Hello Hi.
Same problem is when some cell is selected and user presses space key.
I want to add the functionality to the JTable, so that when the content of the cell starts to be edited (by any way - clicking/typing when selected/space key/and maybe there are more options I don't know about), I want to programatically change the content first. Another option would be removing it when the cell is selected (but then I have to remember position of the last selected cell, so that the character could be readded to it).
I've tried in propertyChange in class TableChangeListener:
table.setValueAt(removeCharacter(table.getValueAt(row,column)), row, column);
but it doesn't work as the cell is already being edited and I cant change it.
You will have to use your own implementation of Cell Editor to meet your own set of requirements.
So create a custom CellEditor implementing FocusLisetener and ActionListener and implement the FocusGained and FocusLost function
Implement the actionPerformed function too to update value on enter click.
Handling the Focus event is a little bit tricky. As it tends to update the cell wrongly. That is why i had to pass the reference table to the CellEditor as a constructor parameter and read the cell row, col on Focus gain.
To reflect the - xxxx: placing - before the cell value, try using a custom CellRenderer. Check out the official tutorial page for details with example. And the part of the credit goes to #mKobel.
An implemented custom cell editor for direction: assign it to your target table column and test.
Giff of my test result:
Code:
class CustomRenderer extends DefaultTableCellRenderer {
public void setValue(Object value)
{
setText("- "+value);
}
}
class MyCellEditor extends AbstractCellEditor
implements TableCellEditor,
FocusListener,
ActionListener
{
JTextField textFeild;
String currentValue;
JTable table;
int row, col;
public MyCellEditor(JTable table) {
this.table = table;
textFeild = new JTextField();
textFeild.addActionListener(this);
textFeild.addFocusListener(this);
}
#Override
public Object getCellEditorValue() {
return currentValue;
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
currentValue = (String)value;
return textFeild;
}
#Override
public void focusGained(FocusEvent e) {
textFeild.setText("");
row = table.getSelectedRow();
col = table.getSelectedColumn();
}
#Override
public void focusLost(FocusEvent e) {
if(!textFeild.getText().equals(""))
//currentValue = textFeild.getText();
table.setValueAt(textFeild.getText(), row, col);
fireEditingStopped();
}
#Override
public void actionPerformed(ActionEvent e) {
if(!textFeild.getText().trim().equals(""))
currentValue = textFeild.getText();
fireEditingStopped();
}
}
I think you should not change the content of the cell at all.
What you need is to set on the table a TableCellRenderer that renders the cell values. Implement the cell renderer so that it shows the value "- Hello" (although your actual data could contain just "Hello"). The renderer just shows any component you want in the table. When user starts editing the cell, the renderer component is not shown. Actually you could also manipulate the editing component using a TableCellEditor.
Extracted from this comment:
The table shows prices like "$5" "20€" and when user clicks the cell
to change the price, I'd like the sign to disappear. When user
finishes editing (clicks enter or by other way), I want the symbol to
appear again.
Although #Sage post is a really great and general solution (+1 for you :), in this particular case I'd implement a TableCellRenderer and TableCellEditor using JFormattedTextField which can manage the currency format matter, as follows:
Set a generic number format to the renderer component: NumberFormat.getNumberInstance()
Set a currency number format to the editor component: NumberFormat.getCurrencyInstance()
This way when the cell is displayed the currency sign will be shown but when the cell is being edited the currency sign will "disappear".
Take a look to this example of implementation:
import java.awt.Color;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.text.NumberFormat;
import java.util.EventObject;
import java.util.Locale;
import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.JFormattedTextField;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.NumberFormatter;
public class CurrencyEditor extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {
JFormattedTextField editor;
JFormattedTextField renderer;
Integer clickCountToStart = 2;
public CurrencyEditor(Locale locale) {
initEditor(locale);
initRenderer(locale);
}
private void initRenderer(Locale locale) {
NumberFormat format = locale != null ?
NumberFormat.getCurrencyInstance(locale) : NumberFormat.getCurrencyInstance();
NumberFormatter formatter = new NumberFormatter(format);
formatter.setMinimum(Double.MIN_VALUE);
formatter.setMaximum(Double.MAX_VALUE);
formatter.setAllowsInvalid(false);
renderer = new JFormattedTextField(formatter);
}
private void initEditor(Locale locale) {
NumberFormat format = locale != null ?
NumberFormat.getNumberInstance(locale) : NumberFormat.getNumberInstance();
NumberFormatter formatter = new NumberFormatter(format);
formatter.setMinimum(Double.MIN_VALUE);
formatter.setMaximum(Double.MAX_VALUE);
formatter.setAllowsInvalid(false);
editor = new JFormattedTextField(formatter);
editor.setBorder(UIManager.getBorder("Tree.editorBorder"));
}
#Override
public Object getCellEditorValue() {
return editor.getValue();
}
#Override
public boolean isCellEditable(EventObject anEvent) {
if (anEvent instanceof MouseEvent) {
return ((MouseEvent)anEvent).getClickCount() >= clickCountToStart;
}
return true;
}
#Override
public boolean shouldSelectCell(EventObject anEvent) {
return true;
}
#Override
public boolean stopCellEditing() {
fireEditingStopped();
return true;
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
if(value instanceof Double){
editor.setValue(value);
}
return editor;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if(value instanceof Double) {
Color background = isSelected ? UIManager.getColor("Table.selectionBackground") : UIManager.getColor("Table.background");
Color foreground = isSelected ? UIManager.getColor("Table.selectionForeground") : UIManager.getColor("Table.foreground");
Border border = hasFocus ? UIManager.getBorder("Table.focusCellHighlightBorder") : BorderFactory.createEmptyBorder();
renderer.setBackground(background);
renderer.setForeground(foreground);
renderer.setBorder(border);
renderer.setValue(value);
return renderer;
} else {
String message = String.format("Not supported for %1$1s class!", value.getClass());
throw new IllegalArgumentException(message);
}
}
}
Disclaimer: it may not work properly with Nimbus look and feel as UIManager properties are named different. I've tested it using Metal, Windows, Windows Classic and Motif.
Here is the code I've used to test it:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.Locale;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
public class Demo {
private void initGUI(){
DefaultTableModel model = new DefaultTableModel(new Object[]{"Item", "Price USD", "Price EUR"}, 0);
model.addRow(new Object[]{"Fender stratocaster", 1599.99d, 1176.46d});
model.addRow(new Object[]{"Gibson Les Paul", 1299.99d, 955.87d});
model.addRow(new Object[]{"Pual Reed Smith Standard 24", 1999.99d, 1470.58d});
JTable table = new JTable(model);
table.setPreferredScrollableViewportSize(new Dimension(500, 300));
TableColumn priceUSD = table.getColumn("Price USD");
priceUSD.setCellRenderer(new CurrencyEditor(Locale.US));
priceUSD.setCellEditor(new CurrencyEditor(Locale.US));
TableColumn priceEUR = table.getColumn("Price EUR");
priceEUR.setCellRenderer(new CurrencyEditor(Locale.GERMANY));
priceEUR.setCellEditor(new CurrencyEditor(Locale.GERMANY));
JPanel content = new JPanel(new BorderLayout());
content.add(new JScrollPane(table));
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(content);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Demo().initGUI();
}
});
}
}
Screenshot

jtable in jtree with different row count

I'm trying to create a JTree that consists of JTables. So far, i succeeded in creating a Jtree with Jtables.But, I cant change the row count of a table of a specific tree node. Whenever i try to adjust the row count, all of the tree's node's row count changes.
I used the code at the following link:
Jtable as a Jtree Node
I wrote the following code by the recommendation of Trashgod; but it didnt work; could you please give some working code..
package helperPack;
import java.awt.BorderLayout;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.tree.DefaultMutableTreeNode;
public class JTreeTrial extends JFrame {
/**
* #param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
DefaultMutableTreeNode parentNode = new DefaultMutableTreeNode("node");
JTree tree = new JTree(parentNode);
JTable table = new JTable();
table.setModel(new DefaultTableModel() {
private static final long serialVersionUID = 1L;
#Override
public int getRowCount() {
return 2;
}
#Override
public int getColumnCount() {
return 2;
}
#Override
public Object getValueAt(int row, int column) {
return ":" + "row" + ":" + column;
}
});
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getModel().getRoot();
node.setUserObject(table);
JTreeTrial trial=new JTreeTrial();
trial.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane jsp = new JScrollPane(tree);
trial.add(jsp, BorderLayout.CENTER);
trial.pack();
trial.setVisible(true);
trial.setLocationRelativeTo(null);
}
});
}
}
Example :
|node1|
|a|b|
|c|d|
|node2|
|e|f|
|node3|
|g|h|
|i|j|
|k|m|
Instead of rendering the tables in the tree, add a TreeSelectionListener and update a single JTable in an adjacent component. Let each TreeNode contain a Tablemodel, and use setModel() to update the JTable. Several related examples are cited here.

JTree is badly blurred when scrolling [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
I have a JTree inside a JScrollPane, and when I use the scrollbar the tree gets all blurred up, as you can see in the image below.
It gets back to normal if I do something to make it repaint, like minimize and restore the window, or click in the tree to make a node expand or collapse (however the blurring doesn't go away if I drag the window off the screen and back, or drag another window in front of it).
The JTree has a custom TreeModel and cell renderer. The recent change was for the TreeModel; the cell renderer has been there for a long time and was working fine. The cell renderer is a subclass of DefaultTreeCellRenderer, with only the getTreeCellRendererComponent method overridden (to display custom icons).
I used to populate DefaultMutableTreeNodes from a data structure which contained the data to be displayed, but that was giving performance problems when the number of nodes was large (like over 10,000). Since the data I had was already in a tree structure, I realized it would be fairly simple to create a custom TreeModel around it without using any DefaultMutableTreeNodes. That made the JTree populate more quickly, but now I'm left with this blurred scrolling problem.
The code below isn't from the application, but it compiles as is and will demonstrate the problem. Removing the tree.setBackground line stops the blurry behavior.
package stackoverflow;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.*;
import java.awt.Color;
public class NumberTreeModel implements TreeModel {
public static final int ROOT_NUMBER = 100;
public Object getChild(Object parent, int index) {
return index;
}
public int getChildCount(Object node) {
return isLeaf(node) ? 0 : ROOT_NUMBER;
}
#Override
public int getIndexOfChild(Object parent, Object child) {
int parentValue = ((Integer) parent).intValue();
int childValue = ((Integer) child).intValue();
return parentValue == ROOT_NUMBER ? childValue : -1 ;
}
public Object getRoot() {
return ROOT_NUMBER;
}
public boolean isLeaf(Object node) {
return ((Integer) node).intValue() < ROOT_NUMBER;
}
public void addTreeModelListener(TreeModelListener listener) { }
public void removeTreeModelListener(TreeModelListener listener) { }
public void valueForPathChanged(TreePath path, Object obj) { }
public static void display() {
JFrame frame = new JFrame("Number JTree");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
NumberTree tree = new NumberTree();
tree.setModel(new NumberTreeModel());
tree.setBackground(new Color(0,0,0,0));
JScrollPane scroll = new JScrollPane(tree);
frame.add(scroll);
scroll.getViewport().setScrollMode(JViewport.BLIT_SCROLL_MODE);
tree.expandRow(0);
frame.pack();
frame.setSize(300, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static class NumberTree extends JTree {
static final long serialVersionUID = 1;
#Override
public String convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf,
int row, boolean hasFocus) {
if (value instanceof Integer) {
int n = ((Integer) value).intValue();
return n + "=========".substring(0, n % 10);
} else {
System.out.println("value class=" + value.getClass());
return value.toString();
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
display();
}
});
}
}
I suspect you have overridden paintComponent() but neglected super.paintComponent(g), as shown here.
Less likely, you might experiment with setScrollMode() in the scroll pane's viewport.
Editing your question to include an sscce might clarify the problem.
Addendum: For reference, here's an example that does not exhibit the rendering artifact seen in the question.
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
/**
* #see https://stackoverflow.com/a/15696825/230513
*/
public class Sscce {
private void display() {
JFrame f = new JFrame("Sscce");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTree tree = new JTree();
for (int i = 0; i < tree.getRowCount(); i++) {
tree.expandRow(i);
}
f.add(new JScrollPane(tree));
f.pack();
f.setSize(200, 200);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Sscce().display();
}
});
}
}
Solution:
Don't use setColor to change the background of the JTree with a non-opaque color.
For visual transparency of the nodes, implement a custom TreeCellRenderer to return null for getBackgroundNonSelectionColor and getBackground, as described in JTree set background of node to non-opaque
setting the viewport mode of the JScrollPane to JViewport.SIMPLE_SCROLL_MODE helps the scroll blur problem, but not necessarily repaint problems with expanding and collapsing ndoes.

JTree set background of node to non-opaque

Please have a look at the SSCCE. How can I make the non-selected tree nodes' background transparent. At the moment the background of non-selected nodes is white. My cell renderer, however, should paint it non-opaque if it is not selected (and green when selected...what it does). In the end I want non-selected nodes to be just text without background, since the area which is red in the SSCCE has a gradient fill in my application.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
public class SimpleTree extends JFrame
{
public static void main(final String[] args)
{
new SimpleTree();
}
public SimpleTree()
{
super("Creating a Simple JTree");
final Container content = this.getContentPane();
content.setBackground(Color.RED);
final Object[] hierarchy = { "javax.swing", "javax.swing.border", "javax.swing.colorchooser", "javax.swing.event", "javax.swing.filechooser", new Object[] { "javax.swing.plaf", "javax.swing.plaf.basic", "javax.swing.plaf.metal", "javax.swing.plaf.multi" }, "javax.swing.table",
new Object[] { "javax.swing.text", new Object[] { "javax.swing.text.html", "javax.swing.text.html.parser" }, "javax.swing.text.rtf" }, "javax.swing.tree", "javax.swing.undo" };
final DefaultMutableTreeNode root = this.processHierarchy(hierarchy);
final JTree tree = new JTree(root);
tree.setOpaque(false);
tree.setCellRenderer(new MyCellRenderer());
final JScrollPane scroller = new JScrollPane(tree);
scroller.getViewport().setOpaque(false);
scroller.setOpaque(false);
content.add(scroller, BorderLayout.CENTER);
this.setSize(275, 300);
this.setVisible(true);
}
/**
* Small routine that will make node out of the first entry in the array,
* then make nodes out of subsequent entries and make them child nodes of
* the first one. The process is repeated recursively for entries that are
* arrays.
*/
private DefaultMutableTreeNode processHierarchy(final Object[] hierarchy)
{
final DefaultMutableTreeNode node = new DefaultMutableTreeNode(hierarchy[0]);
DefaultMutableTreeNode child;
for (int i = 1; i < hierarchy.length; i++)
{
final Object nodeSpecifier = hierarchy[i];
if (nodeSpecifier instanceof Object[]) // Ie node with children
child = this.processHierarchy((Object[]) nodeSpecifier);
else
child = new DefaultMutableTreeNode(nodeSpecifier); // Ie Leaf
node.add(child);
}
return (node);
}
public class MyCellRenderer extends DefaultTreeCellRenderer
{
#Override
public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean sel, final boolean expanded, final boolean leaf, final int row, final boolean hasFocus)
{
final Component ret = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
final DefaultMutableTreeNode node = ((DefaultMutableTreeNode) (value));
this.setText(value.toString());
if (sel)
{
this.setOpaque(true);
this.setBackground(Color.GREEN);
}
else
{
this.setOpaque(false);
this.setBackground(null);
}
return ret;
}
}
}
You should override getBackgroundNonSelectionColor,getBackgroundSelectionColor and getBackground of DefaultTreeCellRenderer and return appropriate values like so:
public class MyCellRenderer extends DefaultTreeCellRenderer {
#Override
public Color getBackgroundNonSelectionColor() {
return (null);
}
#Override
public Color getBackgroundSelectionColor() {
return Color.GREEN;
}
#Override
public Color getBackground() {
return (null);
}
#Override
public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean sel, final boolean expanded, final boolean leaf, final int row, final boolean hasFocus) {
final Component ret = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
final DefaultMutableTreeNode node = ((DefaultMutableTreeNode) (value));
this.setText(value.toString());
return ret;
}
}
which will produce:
Other suggestions:
Create and manipulate Swing components on Event Dispatch Thread.
Dont extend JFrame unnecessarily rather create an instance and use that.
Dont call setSize on JFrame rather use a correct LayoutManager and/or override getPreferredSize() and call pack() on JFrame before setting it visible but after adding all components.
Remember to call JFrame#setDefaultCloseOperation with either DISPOSE_ON_CLOSE or EXIT_ON_CLOSE (DISPOSE_XXX is usually preferred unless using Timers as this will allow main(String[] args) to continue its execution after Gui has been closed).
To avoid background refilling, just put UIManager.put("Tree.rendererFillBackground", false); before new SimpleTree(); or after super("Creating a Simple JTree");.

Java TreePath.equals() trouble

I'm having trouble getting an Outline control (alternative TreeTable) to work with a tree of data objects, due to the method that TreePath uses to identify data nodes.
The key problem is that TreePath's equals() method uses the data nodes' equals() method to identify that two node objects are the same ones in the data tree. TreeModel.java even comments on this problem:
"Some implementations may assume that if two TreePaths are equal [as determined by equals()], they identify the same node. If this condition is not met, painting problems and other oddities may result." Example data:
A
B
C
D
B
E
F
H
K
Here, the two "B" nodes might, as individual nodes, be considered to have equal values (hence equals() returns true), but they certainly do not represent the same nodes in the tree.
OK, so if the source data objects have implemented equals() to indicate equal value considering just the node itself, this breaks TreePath if more than one node of the same value appears under a particular parent. In that case, Outline is unable to expand/collapse the correct one of the same-value nodes.
This problem would be solved if TreePath.equals() used "==" instead of data objects' equals() methods. However, since the stock TreePath is closely wired into TreeModel etc etc, it's not obvious how to go about repairing this behavior without a lot of disruption.
Is there some graceful way to get the right effect?
Thanks!
Actually, I think that the problem comes from the way you are implementing equals() in your TreeNode's. Two TreeNode's, in your case, should be considered equals if they represent the same visual nodes. Two TreeNode's can represent the same model object (for example Model Object B) but remain different nodes..
Here is a simple demo based on DefaultMutableTreeNode (equals() uses the Object.equals(Object) default implementation ==). Every 2 seconds it toggles selection from node B1 to B2:
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.ToolTipManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
public class TestTreeNodes {
public static class SomeModelNode {
private String value;
public SomeModelNode(String value) {
super();
this.value = value;
}
public String getValue() {
return value;
}
}
public class MyTreeCellRenderer extends DefaultTreeCellRenderer {
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
Component cell = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
if (value instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
if (treeNode.getUserObject() instanceof SomeModelNode) {
setText(((SomeModelNode) treeNode.getUserObject()).getValue());
}
}
return cell;
}
}
private JFrame f;
private JTree tree;
private DefaultMutableTreeNode nodeA;
private DefaultMutableTreeNode nodeB1;
private DefaultMutableTreeNode nodeB2;
private DefaultMutableTreeNode nodeC;
private DefaultMutableTreeNode nodeD;
private DefaultMutableTreeNode nodeE;
private DefaultMutableTreeNode nodeF;
private DefaultMutableTreeNode nodeH;
private DefaultMutableTreeNode nodeK;
private boolean showingB1 = false;
protected void initUI() {
tree = new JTree(getModel());
for (int i = 0; i < tree.getRowCount(); i++) {
tree.expandRow(i);
}
ToolTipManager.sharedInstance().registerComponent(tree);
tree.setCellRenderer(new MyTreeCellRenderer());
f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.add(new JScrollPane(tree));
f.pack();
f.setVisible(true);
Timer t = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (!showingB1) {
tree.getSelectionModel().setSelectionPath(getPathForNode(nodeB1));
} else {
tree.getSelectionModel().setSelectionPath(getPathForNode(nodeB2));
}
showingB1 = !showingB1;
}
});
t.start();
}
private TreePath getPathForNode(TreeNode node) {
List<TreeNode> nodes = new ArrayList<TreeNode>();
TreeNode current = node;
while (current != null) {
nodes.add(current);
current = current.getParent();
}
Collections.reverse(nodes);
return new TreePath(nodes.toArray(new Object[nodes.size()]));
}
private TreeModel getModel() {
SomeModelNode a = new SomeModelNode("A");
SomeModelNode b = new SomeModelNode("B");
SomeModelNode c = new SomeModelNode("C");
SomeModelNode d = new SomeModelNode("D");
SomeModelNode e = new SomeModelNode("E");
SomeModelNode f = new SomeModelNode("F");
SomeModelNode h = new SomeModelNode("H");
SomeModelNode k = new SomeModelNode("K");
nodeA = new DefaultMutableTreeNode(a);
nodeB1 = new DefaultMutableTreeNode(b);
nodeB2 = new DefaultMutableTreeNode(b);
nodeC = new DefaultMutableTreeNode(c);
nodeD = new DefaultMutableTreeNode(d);
nodeE = new DefaultMutableTreeNode(e);
nodeF = new DefaultMutableTreeNode(f);
nodeH = new DefaultMutableTreeNode(h);
nodeK = new DefaultMutableTreeNode(k);
// Children of A
nodeA.add(nodeB1);
nodeA.add(nodeB2);
nodeA.add(nodeH);
nodeA.add(nodeK);
// Children of B1
nodeB1.add(nodeC);
nodeB1.add(nodeD);
// Children of B2
nodeB2.add(nodeE);
nodeB2.add(nodeF);
DefaultTreeModel model = new DefaultTreeModel(nodeA);
return model;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestTreeNodes().initUI();
}
});
}
}

Categories