org.jdesktop.swingx.demos.tree.XTreeDemo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of swingset3-demos Show documentation
Show all versions of swingset3-demos Show documentation
Demonstrating the abilities of the Swing UI Toolkit swingset2 and swingx aka swingset3
The newest version!
/* Copyright 2007-2009 Sun Microsystems, Inc. All Rights Reserved.
Copyright notice, list of conditions and disclaimer see LICENSE file
*/
package org.jdesktop.swingx.demos.tree;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Window;
import java.io.IOException;
import java.net.URI;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.SingleSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.border.BevelBorder;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
import org.jdesktop.swingx.JXButton;
import org.jdesktop.swingx.JXFrame;
import org.jdesktop.swingx.JXFrame.StartPosition;
import org.jdesktop.swingx.JXPanel;
import org.jdesktop.swingx.JXTree;
import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.ComponentAdapter;
import org.jdesktop.swingx.decorator.HighlightPredicate;
import org.jdesktop.swingx.decorator.Highlighter;
import org.jdesktop.swingx.decorator.IconHighlighter;
import org.jdesktop.swingx.demos.highlighter.RolloverIconHighlighter;
import org.jdesktop.swingx.demos.svg.FeatheRdisc;
import org.jdesktop.swingx.demos.svg.FeatheRmusic;
import org.jdesktop.swingx.demos.svg.FeatheRuser;
import org.jdesktop.swingx.demos.tree.TreeDemoIconValues.LazyLoadingIconValue;
import org.jdesktop.swingx.demos.treetable.TreeTableDemo;
import org.jdesktop.swingx.icon.JXIcon;
import org.jdesktop.swingx.icon.SizingConstants;
import org.jdesktop.swingx.icon.TrafficLightRedIcon;
import org.jdesktop.swingx.icon.TrafficLightYellowIcon;
import org.jdesktop.swingx.renderer.DefaultTableRenderer;
import org.jdesktop.swingx.renderer.IconValue;
import org.jdesktop.swingx.renderer.StringValue;
import org.jdesktop.swingx.renderer.StringValues;
import org.jdesktop.swingx.rollover.RolloverProducer;
import org.jdesktop.swingx.treetable.TreeTableModel;
import swingset.AbstractDemo;
/**
* JXTree Demo
*
* PENDING JW: make editable to demonstrate terminate enhancement.
*
* @author Jeanette Winzenburg, Berlin, Created on 18.04.2008
* @author EUG https://github.com/homebeaver (reorg, add music tree from SwingSet2)
*/
//@DemoProperties(
// value = "JXTree Demo",
// category = "Data",
// description = "Demonstrates JXTree, an enhanced tree component",
// sourceFiles = {
// "org/jdesktop/swingx/demos/tree/XTreeDemo.java",
// "org/jdesktop/swingx/demos/tree/TreeDemoIconValues.java"
// }
//)
public class XTreeDemo extends AbstractDemo {
public static final String ICON_PATH = "toolbar/JTree.gif";
private static final long serialVersionUID = 7070451442278673301L;
private static final Logger LOG = Logger.getLogger(XTreeDemo.class.getName());
private static final String DESCRIPTION = "Demonstrates JXTree, an enhanced tree component";
/**
* main method allows us to run as a standalone demo.
* @param args params
*/
public static void main(String[] args) {
// invokeLater method can be invoked from any thread
SwingUtilities.invokeLater( () -> {
// ...create UI here...
JXFrame controller = new JXFrame("controller", exitOnClose);
AbstractDemo demo = new XTreeDemo(controller);
JXFrame frame = new JXFrame(DESCRIPTION, exitOnClose);
frame.setStartPosition(StartPosition.CenterInScreen);
//frame.setLocationRelativeTo(controller);
frame.getContentPane().add(demo);
frame.pack();
frame.setVisible(true);
controller.getContentPane().add(demo.getControlPane());
controller.pack();
controller.setVisible(true);
});
}
private JTabbedPane tabbedpane; // contains music tree, index 0 and component tree at tabbedpane.getTabCount()
private JXTree componentTree;
/*
* intentionally not defined music tree here.
* You can get it from scroll pane with getTree(tabbedpane.getComponentAt(0))
*/
private Component getTreeComp(Component c) {
JScrollPane sp = (JScrollPane)c;
LOG.fine(" ---> getViewport:"+sp.getViewport());
return sp.getViewport().getView();
}
/**
* XTreeDemo Constructor
*
* @param frame controller Frame
*/
public XTreeDemo(Frame frame) {
super(new BorderLayout());
frame.setTitle(getBundleString("frame.title", DESCRIPTION));
super.setPreferredSize(PREFERRED_SIZE);
super.setBorder(new BevelBorder(BevelBorder.LOWERED));
// create tabs
tabbedpane = new JTabbedPane();
add(tabbedpane, BorderLayout.CENTER);
tabbedpane.add(getBundleString("music")
, createMusicTree(new MusicTreeModel(getBundleString("music"), getClass().getResource("resources/tree.txt"))));
tabbedpane.add(getBundleString("default JTree"), createDefaultTree());
tabbedpane.add(getBundleString("componentTree"), createComponentTree());
tabbedpane.setTabPlacement(JTabbedPane.TOP);
// the default model of tabbedpane implements SingleSelectionModel
// javax.swing.DefaultSingleSelectionModel
tabbedpane.getModel().addChangeListener( changeEvent -> {
Object source = changeEvent.getSource();
if(source instanceof SingleSelectionModel ssmodel) {
Component scrollPane = tabbedpane.getComponentAt(ssmodel.getSelectedIndex());
// componentTree ist in JScrollPane eingepackt
if(getTreeComp(scrollPane)==componentTree) {
LOG.info("componentTree selected "+changeEvent);
componentTree.setModel(createTreeModel()); // lazy createTreeModel
componentTree.expandAll();
} else {
LOG.info("selected "+getTreeComp(scrollPane));
}
} else {
LOG.warning(""+changeEvent);
}
});
}
@SuppressWarnings("serial")
class MusicTree extends JXTree /*implements TableCellRenderer*/ {
static final String LINE_STYLE = "JTree.lineStyle";
static final String LEG_LINE_STYLE_STRING = "Angled";
MusicTree(MusicTreeModel model) {
super(model);
// javax.swing.plaf.metal.MetalTreeUI.LINE_STYLE : the property name is private
Object lineStyle = getClientProperty(LINE_STYLE);
if(lineStyle==null) {
// warum ist es null? es sollte "Angled" sein
LOG.warning("JTree.lineStyle="+lineStyle + " will be set to 'Horizontal'");
// null oder LEG_LINE_STYLE_STRING ist gleichwertig!!
// siehe MetalTreeUI#144: lineStyle = LEG_LINE_STYLE; // default case
putClientProperty(LINE_STYLE, "Horizontal"); // macht es einen Unterschied? JA
LOG.fine("JTree.lineStyle=" + getClientProperty(LINE_STYLE) + "\n");
}
setRolloverEnabled(true); // to show a "live" rollover behaviour
setCellRenderer(musicCellRenderer());
}
public Insets getInsets() {
return new Insets(5,5,5,5);
}
public void setEditable(boolean editable) {
LOG.config("setEditable to "+editable+(isLargeModel()?", tree isLargeModel":"")+", cellEditor:"+cellEditor);
// cellEditor:org.jdesktop.swingx.tree.DefaultXTreeCellEditor
super.setEditable(editable);
}
private TreeCellRenderer musicCellRenderer() {
StringValue sv = (Object value) -> {
if(value instanceof MusicTreeModel.MusicEntry
|| value instanceof MusicTreeModel.Album
|| value instanceof MusicTreeModel.Song
) {
return StringValues.TO_STRING.getString(value);
}
String simpleName = value.getClass().getSimpleName();
return simpleName + "(" + value + ")";
};
return new JXTree.DelegatingRenderer(sv);
}
}
private JComponent createMusicTree(MusicTreeModel model) {
JXTree tree = new MusicTree(model);
/*
* use small person icon for Composer (use And Predicate)
*/
Highlighter personIcon = new IconHighlighter(
new HighlightPredicate.AndHighlightPredicate(HighlightPredicate.IS_LEAF, new HighlightPredicate.DepthHighlightPredicate(2)),
FeatheRuser.of(SizingConstants.SMALL_ICON, SizingConstants.SMALL_ICON));
tree.addHighlighter(personIcon);
/*
* use small disc icon for records/Albums
*/
Highlighter discIcon = new IconHighlighter(new HighlightPredicate.DepthHighlightPredicate(3),
FeatheRdisc.of(SizingConstants.SMALL_ICON, SizingConstants.SMALL_ICON));
tree.addHighlighter(discIcon);
/*
* use very small XS music icon instead the default Tree.leafIcon (file/sheet/fileview)
* HighlightPredicate.IS_LEAF is not good, because there is a composer (Chopin) entry,
* with no records ==> hence Chopin is a leaf
* ==> use Depth predicate
*/
Highlighter musicIcon = new IconHighlighter(new HighlightPredicate.DepthHighlightPredicate(4),
FeatheRmusic.of(SizingConstants.XS, SizingConstants.XS));
tree.addHighlighter(musicIcon);
Highlighter redText = new ColorHighlighter(HighlightPredicate.ROLLOVER_CELL, null, Color.RED);
tree.addHighlighter(redText);
tree.addHighlighter(new RolloverIconHighlighter(HighlightPredicate.ROLLOVER_ROW, null));
/*
* define a ToolTip for Cursor-Rollover event.
* - for Albums : show the Album cover from wikipedia
*
* addPropertyChangeListener is defined and implemented in java.awt.Component
* PropertyChangeListener is a SAM-interface (Single Abstract Method)
* with method void propertyChange(PropertyChangeEvent evt). So I can define this method
* as Lambda-Expression.
*
* @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
*/
tree.addPropertyChangeListener(RolloverProducer.ROLLOVER_KEY, propertyChangeEvent -> {
JXTree source = (JXTree)propertyChangeEvent.getSource(); // source where event occurred
source.setToolTipText(null);
Point newPoint = (Point)propertyChangeEvent.getNewValue();
if(newPoint!=null && newPoint.y>-1) {
TreePath treePath = source.getPathForRow(newPoint.y);
if(treePath.getPathCount()==4) { // Album / Record / Style
Object o = treePath.getLastPathComponent();
// show https://en.wikipedia.org/wiki/File:My_Name_Is_Albert_Ayler.jpg
if(o instanceof MusicTreeModel.Album album) {
source.setToolTipText(album.getHtmlSrc());
}
} else if (treePath.getPathCount() == 3) { // Artist / Composer
Object o = treePath.getLastPathComponent();
if (o instanceof MusicTreeModel.Artist artist) {
URI uri = artist.getURI();
if(uri!=null) {
source.setToolTipText("click to browse wikipedia");
}
}
} else if(treePath.getPathCount()==5) { // Song / Composition
Object o = treePath.getLastPathComponent();
if(o instanceof MusicTreeModel.Song song) {
URI uri = song.getURI();
if(uri!=null) {
source.setToolTipText("click to open a browser player");
}
}
}
}
});
tree.addPropertyChangeListener(RolloverProducer.CLICKED_KEY, propertyChangeEvent -> {
JXTree source = (JXTree)propertyChangeEvent.getSource();
source.setToolTipText(null);
Point newPoint = (Point)propertyChangeEvent.getNewValue();
if(newPoint!=null && newPoint.y>-1) {
TreePath treePath = source.getPathForRow(newPoint.y);
if(treePath.getPathCount()==3) { // Artist / Composer
Object o = treePath.getLastPathComponent();
if (o instanceof MusicTreeModel.Artist artist) {
URI uri = artist.getURI();
if(uri!=null) try {
Desktop.getDesktop().browse(uri);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} else if(treePath.getPathCount()==5) { // Song / Composition
Object o = treePath.getLastPathComponent();
if(o instanceof MusicTreeModel.Song song) {
URI uri = song.getURI();
if(uri!=null) try {
Desktop.getDesktop().browse(uri);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
});
tree.setEditable(true);
return new JScrollPane(tree);
}
@SuppressWarnings("serial")
private JComponent createDefaultTree() {
/*
* no param: use the DefaultMutableTreeNode JTree with colors, sports, food
* defined in protected static TreeModel JTree.getDefaultTreeModel()
*/
JXTree dtree = new JXTree() {
@Override
public TreeCellRenderer getCellRenderer() {
StringValue sv = (Object value) -> {
if (value instanceof String) {
return StringValues.TO_STRING.getString(value);
}
// UserObject is wrapped in DefaultMutableTreeNode instance
// auto-unwrap in WrappingProvider is switched off
if (value instanceof javax.swing.tree.DefaultMutableTreeNode dmtn) {
return StringValues.TO_STRING.getString(dmtn.getUserObject());
}
String simpleName = value.getClass().getSimpleName();
return simpleName + "(" + value + ")";
};
IconValue iv = (Object value) -> {
if (value instanceof String s) {
if(s.equals("red")) {
// return UIManager.getIcon("Tree.leafIcon");
return TrafficLightRedIcon.of(JXIcon.SMALL_ICON, JXIcon.SMALL_ICON);
}
if(s.equals("yellow")) {
return TrafficLightYellowIcon.of(JXIcon.SMALL_ICON, JXIcon.SMALL_ICON);
}
}
if (value instanceof javax.swing.tree.DefaultMutableTreeNode dmtn) {
if("red".equals(dmtn.getUserObject())) {
return TrafficLightRedIcon.of(JXIcon.SMALL_ICON, JXIcon.SMALL_ICON);
}
if("yellow".equals(dmtn.getUserObject())) {
return TrafficLightYellowIcon.of(JXIcon.SMALL_ICON, JXIcon.SMALL_ICON);
}
}
return null;
};
return new JXTree.DelegatingRenderer(iv, sv);
}
};
dtree.setCellRenderer(dtree.getCellRenderer());
dtree.setRolloverEnabled(true);
dtree.addHighlighter(new RolloverIconHighlighter(HighlightPredicate.ROLLOVER_ROW, null));
dtree.setRowHeight(-1);
dtree.setName("dtree");
dtree.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
JScrollPane scrollpane = new JScrollPane(dtree);
dtree.expandAll();
dtree.updateUI();
dtree.setEditable(true);
return scrollpane;
}
@SuppressWarnings("serial")
private JComponent createComponentTree() {
/*
* no param: in JTree there is a dafault DefaultMutableTreeNode JTree with colors, sports, food.
* the TreeModel is set later when componentTree is selected
*/
componentTree = new JXTree() {
@Override
public TreeCellRenderer getCellRenderer() {
StringValue sv = (Object value) -> {
if (value instanceof Component component) {
String simpleName = component.getClass().getSimpleName();
if (simpleName.length() == 0){
// anonymous class
simpleName = component.getClass().getSuperclass().getSimpleName();
}
return simpleName + "(" + component.getName() + ")";
}
return StringValues.TO_STRING.getString(value);
};
// StringValue for lazy icon loading interface org.jdesktop.swingx.renderer.StringValue
StringValue keyValue = (Object value) -> {
if (value == null) return "";
String simpleClassName = value.getClass().getSimpleName();
if (simpleClassName.length() == 0){
// anonymous class
simpleClassName = value.getClass().getSuperclass().getSimpleName();
}
return simpleClassName + ".png";
};
IconValue iv = new LazyLoadingIconValue(getClass(), keyValue, "fallback.png");
return new JXTree.DelegatingRenderer(iv, sv);
}
};
componentTree.setCellRenderer(componentTree.getCellRenderer());
// JXTree rollover
// enable and register a highlighter
componentTree.setRolloverEnabled(true);
componentTree.addHighlighter(new RolloverIconHighlighter(HighlightPredicate.ROLLOVER_ROW, null));
//
// the current cell renderer is queried for each row's height:
componentTree.setRowHeight(-1);
componentTree.setName("componentTree");
componentTree.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
JScrollPane scrollpane = new JScrollPane(componentTree);
componentTree.expandAll();
// LOG.info("done init ComponentTree \n tree Background:"+componentTree.getBackground()
// + "\n tabbedpane.getAccessibleContext():"+tabbedpane.getAccessibleContext()
// + "\n tabbedpane.getUI():"+tabbedpane.getUI()
// + "\n tree:"+componentTree);
return scrollpane;
}
@SuppressWarnings("serial")
class ComponentTreeTable extends JXTreeTable implements TableCellRenderer {
ComponentTreeTable(JXTreeTable.TreeTableCellRenderer renderer) {
super(renderer);
assert ((JXTreeTable.TreeTableModelAdapter) getModel()).getTree() == renderer;
StringValue locSize = (Object value) -> {
int x;
int y;
if (value instanceof Dimension dim) {
x = dim.width;
y = dim.height;
} else if (value instanceof Point point) {
x = point.x;
y = point.y;
} else {
return StringValues.TO_STRING.getString(value); // also for value==null
}
return "(" + x + ", " + y + ")";
};
setDefaultRenderer(Point.class, new DefaultTableRenderer(locSize, JLabel.CENTER));
setDefaultRenderer(Dimension.class, this.getDefaultRenderer(Point.class));
}
@Override // code in super: return (TreeTableModel) renderer.getModel();
public TreeTableModel getTreeTableModel() {
TableModel tm = this.getModel();
if(tm instanceof TreeTableModelAdapter mttma) {
return mttma.getTreeTableModel();
}
return super.getTreeTableModel();
}
@Override
public int getHierarchicalColumn() {
int superret = super.getHierarchicalColumn();
assert superret==0;
return super.getHierarchicalColumn();
}
@Override
public TableCellRenderer getCellRenderer(int row, int column) {
ComponentAdapter ca = getComponentAdapter(row, column);
if(ca.column == getHierarchicalColumn()) {
JXTree.DelegatingRenderer renderer = (JXTree.DelegatingRenderer)getTreeCellRenderer();
LOG.info("hierarchical column "+column + " isHierarchicalColumn!!! renderer:"+renderer);
JTree tree = ((JXTreeTable.TreeTableModelAdapter) getModel()).getTree();
JXTree xtree = (JXTree)tree;
return (JXTreeTable.TreeTableCellRenderer)xtree;
}
return super.getCellRenderer(row, column);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
LOG.warning("NICHT IMPLEMENTIERT row="+row + " column="+column + " value:"+value);
// super.getCellRenderer(row, column)
return null;
}
}
// Controller:
private JXButton expandButton;
private JXButton collapseButton;
@Override
public JXPanel getControlPane() {
JXPanel buttons = new JXPanel();
// JXTree convenience api
expandButton = new JXButton(getBundleString("expandAll.Action.text"));
expandButton.setName("expandButton");
expandButton.addActionListener(actionEvent -> {
if(tabbedpane.getSelectedIndex()==tabbedpane.getTabCount()) {
componentTree.expandAll();
} else if(tabbedpane.getSelectedIndex()>=0) {
// 0:JScrollPane with music , 1:JScrollPane with default JTree
// Component c = tabbedpane.getComponentAt(tabbedpane.getSelectedIndex());
Component comp = getTreeComp(tabbedpane.getComponentAt(tabbedpane.getSelectedIndex()));
if(comp instanceof JXTree tree) tree.expandAll();
if(comp instanceof JXTreeTable ttable) ttable.expandAll();
}
});
buttons.add(expandButton);
collapseButton = new JXButton(getBundleString("collapseAll.Action.text"));
collapseButton.setName("collapseButton");
collapseButton.addActionListener(actionEvent -> {
if(tabbedpane.getSelectedIndex()==tabbedpane.getTabCount()) {
componentTree.collapseAll();
} else if(tabbedpane.getSelectedIndex()>=0) {
Component comp = getTreeComp(tabbedpane.getComponentAt(tabbedpane.getSelectedIndex()));
if(comp instanceof JXTree tree) tree.collapseAll();
if(comp instanceof JXTreeTable ttable) ttable.collapseAll();
}
});
buttons.add(collapseButton);
//
return buttons;
}
private TreeTableModel createTreeModel() {
Window window = SwingUtilities.getWindowAncestor(this);
// use model from TreeTableDemo
return TreeTableDemo.getTreeTableModel(window != null ? window : this);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy