Change mouse cursor on hover over node in JTree - java

I'm creating a JTree and adding some nodes doing something like this:
DefaultMutableTreeNode top = new DefaultMutableTreeNode("The Java Series");
tree = new JTree(top);
...
DefaultMutableTreeNode node = new DefaultMutableTreeNode("<html>"+code+" "+description+"</html>");
top.add(node);
...
Now I added a "addMouseMotionListener" to the node which is ok. My problem is that the mouse cursor changes whenever I hover over any part of the node. What I really want is to only change mouse cursor when hovering over the HTML hyperlink text part of the node
""+code+""
and NOT the description.
So is there a way to make the mouse cursor change only when hovering in certain parts of the node?
Thanks in advance.

OK, it took me a while, but what I described in my comment seems to work. There may be other/better ways and I would love to read about it, but so far this is all I have found.
The idea is that:
I use a JEditorPane as a TreeCellRenderer
I listen for mouse move (and also, as a bonus for mouse click)
For each event I recreate the renderer for the hovered cell
I translate the event to the component coordinates
I use viewToModel to find if I am hovering an anchor element
I change the cursor accordingly.
Here is the code and as a bonus, you also get working hyperlinks!
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.text.AttributeSet;
import javax.swing.text.Element;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLDocument;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
public class TestTreeHyperlinks {
private final class HyperlinkMouseListener extends MouseAdapter {
private final JTree tree;
private HyperlinkMouseListener(JTree tree) {
this.tree = tree;
}
#Override
public void mouseExited(MouseEvent e) {
tree.setCursor(Cursor.getDefaultCursor());
}
#Override
public void mouseClicked(MouseEvent e) {
Element h = getHyperlinkElement(e);
if (h != null) {
Object attribute = h.getAttributes().getAttribute(HTML.Tag.A);
if (attribute instanceof AttributeSet) {
AttributeSet set = (AttributeSet) attribute;
String href = (String) set.getAttribute(HTML.Attribute.HREF);
if (href != null) {
try {
Desktop.getDesktop().browse(new URI(href));
} catch (IOException e1) {
e1.printStackTrace();
} catch (URISyntaxException e1) {
e1.printStackTrace();
}
}
}
}
}
#Override
public void mouseMoved(MouseEvent event) {
boolean isHyperlink = isHyperlink(event);
if (isHyperlink) {
tree.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
} else {
tree.setCursor(Cursor.getDefaultCursor());
}
}
private boolean isHyperlink(MouseEvent event) {
return getHyperlinkElement(event) != null;
}
private Element getHyperlinkElement(MouseEvent event) {
Point p = event.getPoint();
int selRow = tree.getRowForLocation(p.x, p.y);
TreeCellRenderer r = tree.getCellRenderer();
if (selRow != -1 && r != null) {
TreePath path = tree.getPathForRow(selRow);
Object lastPath = path.getLastPathComponent();
Component rComponent = r.getTreeCellRendererComponent(tree, lastPath, tree.isRowSelected(selRow), tree.isExpanded(selRow),
tree.getModel().isLeaf(lastPath), selRow, true);
if (rComponent instanceof JEditorPane) {
Rectangle pathBounds = tree.getPathBounds(path);
JEditorPane editor = (JEditorPane) rComponent;
editor.setBounds(tree.getRowBounds(selRow));
p.translate(-pathBounds.x, -pathBounds.y);
int pos = editor.getUI().viewToModel(editor, p);
if (pos >= 0 && editor.getDocument() instanceof HTMLDocument) {
HTMLDocument hdoc = (HTMLDocument) editor.getDocument();
Element elem = hdoc.getCharacterElement(pos);
if (elem.getAttributes().getAttribute(HTML.Tag.A) != null) {
return elem;
}
}
}
}
return null;
}
}
#SuppressWarnings("serial")
public class MyTreeCellRenderer extends DefaultTreeCellRenderer implements TreeCellRenderer {
private JEditorPane editorPane;
public MyTreeCellRenderer() {
editorPane = new JEditorPane("text/html", null);
}
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
if (c instanceof JLabel) {
JLabel label = (JLabel) c;
editorPane.setText(label.getText());
editorPane.setToolTipText(label.getToolTipText());
editorPane.setOpaque(label.isOpaque());
editorPane.setBackground(label.getBackground());
editorPane.setBorder(label.getBorder());
}
return editorPane;
}
}
protected void initUI() {
final JTree tree = new JTree(getTreeModel());
tree.setCellRenderer(new MyTreeCellRenderer());
HyperlinkMouseListener listener = new HyperlinkMouseListener(tree);
tree.addMouseListener(listener);
tree.addMouseMotionListener(listener);
JFrame f = new JFrame(TestTreeHyperlinks.class.getSimpleName());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(tree), BorderLayout.CENTER);
f.pack();
f.setSize(f.getWidth() + 100, f.getHeight() + 100);
f.setVisible(true);
}
private TreeModel getTreeModel() {
return new DefaultTreeModel(
getNodes(new DefaultMutableTreeNode("<html>Root Google</html>"), 5));
}
private TreeNode getNodes(DefaultMutableTreeNode parent, int i) {
if (i > 0) {
for (int j = 0; j < 5; j++) {
DefaultMutableTreeNode newChild = new DefaultMutableTreeNode(
"<html>Node "
+ (j + 1)
+ " a link to stackoverflow</html> and some more text not in an hyperlink");
getNodes(newChild, i - 1);
parent.add(newChild);
}
}
return parent;
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
UnsupportedLookAndFeelException {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestTreeHyperlinks().initUI();
}
});
}
}

This code works:
tree_object.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
TreePath tp = ((JTree)e.getSource()).getPathForLocation(e.getX(), e.getY());
if(tp != null)
{
((JTree)e.getSource()).setCursor(new Cursor(Cursor.HAND_CURSOR));
}
else
{
((JTree)e.getSource()).setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
}
});

Related

Add rollover to JTree handles

I am trying to make a custom rollover effect on the Collapsed Icon for a JTree. However, I am unsure how to target an individual handle instead of all the handles.
If you run the code below, you will see that when you hover over any handle, node, or leaf of the JTree all the collapsed handles will change to the rollover. This is not desired. So how can I change just a single handle when I am hovering over that handle, and preferably not when hovering over the node next to it?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.plaf.basic.*;
#SuppressWarnings("serial")
public class DirectoryExplorer extends JFrame {
private DirectoryExplorer() {
super("Directory Explorer");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridLayout(1, 1));
createPanel();
setSize(800,600);
setVisible(true);
}
private void createPanel() {
JPanel panel = new JPanel(new GridLayout(1, 1));
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Hello");
root.add(new DefaultMutableTreeNode("1"));
root.add(new DefaultMutableTreeNode("2"));
root.add(new DefaultMutableTreeNode("3"));
JTree tree = new JTree();
BasicTreeUI tUI = (BasicTreeUI) tree.getUI();
tUI.setCollapsedIcon(new ImageIcon("resources/closed.png"));
tUI.setExpandedIcon(new ImageIcon("resources/open.png"));
tree.setShowsRootHandles(true);
tree.addMouseMotionListener(new MouseHandler(tree));
panel.add(new JScrollPane(tree));
getContentPane().add(panel);
}
public static void main(String[] args) {
new DirectoryExplorer();
}
private class MouseHandler implements MouseMotionListener {
JTree t = null;
BasicTreeUI tUI = null;
public MouseHandler(JTree tree) {
t = tree;
tUI = (BasicTreeUI) tree.getUI();
}
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
TreePath selPath = t.getPathForLocation(e.getX(), e.getY());
if(selPath != null)
tUI.setCollapsedIcon(new ImageIcon("resources/rollover.png"));
else
tUI.setCollapsedIcon(new ImageIcon("resources/closed.png"));
t.repaint();
}
}
}
In order to achieve the desired result you need to override BasicTreeUI.paintExpandControl() and BasicTreeUI.MouseHandler.mouseMoved(). You will also need to create a few methods such as setRolloverIcon().
A working example of this might look like this
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.plaf.basic.BasicTreeUI;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
#SuppressWarnings("serial")
public class DirectoryExplorer extends JFrame {
private DirectoryExplorer() {
super("Directory Explorer");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridLayout(1, 1));
createPanel();
setSize(800,600);
setVisible(true);
}
private void createPanel() {
JPanel panel = new JPanel(new GridLayout(1, 1));
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Hello");
root.add(new DefaultMutableTreeNode("1"));
root.add(new DefaultMutableTreeNode("2"));
root.add(new DefaultMutableTreeNode("3"));
JTree tree = new JTree();
//UI Stuff//
TreeHandleUI tUI = new TreeHandleUI(tree);
tree.setUI(tUI);
try {
tUI.setCollapsedIcon(new ImageIcon(new URL("https://i.stack.imgur.com/nKJFv.png")));
tUI.setExpandedIcon(new ImageIcon(new URL("https://i.stack.imgur.com/NJvcp.png")));
tUI.setRolloverIcon(new ImageIcon(new URL("https://i.stack.imgur.com/jN6uX.png")));
} catch(MalformedURLException e) {
System.out.println("Bad URL / URLs");
}
////////////
tree.setShowsRootHandles(true);
panel.add(new JScrollPane(tree));
getContentPane().add(panel);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> new DirectoryExplorer());
}
private class TreeHandleUI extends BasicTreeUI {
///Variables
private JTree t = null;
private Icon rolloverIcon = null;
private boolean rolloverEnabled = false;
private UpdateHandler uH = null;
private boolean isLeftToRight( Component c ) {
return c.getComponentOrientation().isLeftToRight();
}
public TreeHandleUI(JTree tree) {
t = tree;
uH = new UpdateHandler(t);
t.addMouseMotionListener(uH);
}
public void setRolloverIcon(Icon rolloverG) {
Icon oldValue = rolloverIcon;
rolloverIcon = rolloverG;
setRolloverEnabled(true);
if (rolloverIcon != oldValue) {
t.repaint();
}
}
private void setRolloverEnabled(boolean handleRolloverEnabled) {
boolean oldValue = rolloverEnabled;
rolloverEnabled = handleRolloverEnabled;
if (handleRolloverEnabled != oldValue) {
t.repaint();
}
}
#Override
protected void paintExpandControl(Graphics g,
Rectangle clipBounds, Insets insets,
Rectangle bounds, TreePath path,
int row, boolean isExpanded,
boolean hasBeenExpanded,
boolean isLeaf) {
Object value = path.getLastPathComponent();
if (!isLeaf && (!hasBeenExpanded || treeModel.getChildCount(value) > 0)) {
int middleXOfKnob;
if (isLeftToRight(t)) {
middleXOfKnob = bounds.x - getRightChildIndent() + 1;
} else {
middleXOfKnob = bounds.x + bounds.width + getRightChildIndent() - 1;
}
int middleYOfKnob = bounds.y + (bounds.height / 2);
if (isExpanded) {
Icon expandedIcon = getExpandedIcon();
if(expandedIcon != null)
drawCentered(tree, g, expandedIcon, middleXOfKnob, middleYOfKnob );
} else if(isLocationInExpandControl(path, uH.getXPos(), uH.getYPos()) && !isExpanded && rolloverEnabled) {
if(row == uH.getRow()) {
if(rolloverIcon != null)
drawCentered(tree, g, rolloverIcon, middleXOfKnob, middleYOfKnob);
} else {
Icon collapsedIcon = getCollapsedIcon();
if(collapsedIcon != null)
drawCentered(tree, g, collapsedIcon, middleXOfKnob, middleYOfKnob);
}
} else {
Icon collapsedIcon = getCollapsedIcon();
if(collapsedIcon != null)
drawCentered(tree, g, collapsedIcon, middleXOfKnob, middleYOfKnob);
}
}
}
private class UpdateHandler extends BasicTreeUI.MouseHandler {
private JTree t = null;
private int xPos = 0;
private int yPos = 0;
private boolean leftToRight(Component c) {
return c.getComponentOrientation().isLeftToRight();
}
public UpdateHandler(JTree tree) {
t = tree;
}
#Override
public void mouseMoved(MouseEvent e) {
xPos = e.getX();
yPos = e.getY();
t.repaint();
}
public int getXPos() {
return xPos;
}
public int getYPos() {
return yPos;
}
public int getRow() {
return getRowForPath(t, getClosestPathForLocation(t, xPos, yPos));
}
}
}
}
Code will run without downloading images however they are available below
closed.png
open.png
rollover.png

JTable right-click copy/paste menu to copy cell data on one click

I created my JPopupMenu. It appears on my JTable when I right click on a cell. However, I cannot copy the data in the cell unless I first double click and then highlight the data, and then right click anywhere but this current cell to show my popup menu and copy option.
I would like to copy the data in a cell without having to double click on a cell and enter into cell edit mode where I then need to select the data.
How can I do this?
popup = new JPopupMenu();
popup.setName("popupMenu");
menuItemCopy = new JMenuItem(new DefaultEditorKit.CopyAction());
menuItemCopy.setText("Copy");
menuItemCopy.setName("copy");
popup.add(menuItemCopy);
popup.addSeparator();
menuItemPaste = new JMenuItem(new DefaultEditorKit.PasteAction());
menuItemPaste.setText("Paste");
menuItemPaste.setName("paste");
popup.add(menuItemPaste);
Here's the code that I have in my MouseListener for my JTable, in mouseReleased() and mousePressed().
if(e.isPopupTrigger())
{
JTable source = (JTable)e.getSource();
int row = source.rowAtPoint( e.getPoint() );
int column = source.columnAtPoint( e.getPoint() );
gridView.popup.show(e.getComponent(), e.getX(), e.getY());
}
Two things...
I'm not sure how you expect a DefaultEditorKit.CopyAction and DefaultEditorKit.PasteAction to work with a JTable, these are suppose to be used with JTextComponents...
The JTable will only highlight the row on a left button press (or key board navigation change), a right mouse button click doesn't do this by default.
Now, I wanted to use the JTable's component popup support, but this seems to consume all mouse events once it detects a popup trigger, which makes it (near to) impossible to highlight the row/column on a right mouse click.
Instead, I ended up adding a highlight method into my MouseListener which highlights the row/column in question and then triggers the popup.
The reason I did it this way, is the Action's associated with copying and pasting have no concept of anything other than the table, so they don't know what row/column was clicked on.
This allows these actions to focus on worrying about the selection alone.
Content is copied directly to the system clipboard via a custom transferable, which maintains the cell's original type, which means you don't need to reconstruct the object when it's pasted.
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.FlavorEvent;
import java.awt.datatransfer.FlavorListener;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import static javax.swing.Action.NAME;
import javax.swing.JFrame;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
public class TestTable100 {
public static void main(String[] args) {
new TestTable100();
}
public TestTable100() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
DefaultTableModel model = new DefaultTableModel();
model.addColumn("Type");
model.addColumn("Column");
for (File file : new File(System.getProperty("user.home")).listFiles()) {
model.addRow(new Object[]{file, file});
}
JTable table = new JTable(model);
table.getColumnModel().getColumn(0).setCellRenderer(new FirstCellRenderer());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
final JPopupMenu pm = new JPopupMenu();
pm.add(new CopyAction(table));
pm.add(new PasteAction(table));
table.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.isPopupTrigger()) {
highlightRow(e);
doPopup(e);
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
highlightRow(e);
doPopup(e);
}
}
protected void doPopup(MouseEvent e) {
pm.show(e.getComponent(), e.getX(), e.getY());
}
protected void highlightRow(MouseEvent e) {
JTable table = (JTable) e.getSource();
Point point = e.getPoint();
int row = table.rowAtPoint(point);
int col = table.columnAtPoint(point);
table.setRowSelectionInterval(row, row);
table.setColumnSelectionInterval(col, col);
}
});
}
});
}
public class FirstCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
File f = (File) value;
super.getTableCellRendererComponent(table,
value, isSelected, hasFocus, row, column);
String prefix = f.isDirectory() ? "DIR" : "FILE";
setText(prefix);
return this;
}
}
public class CopyAction extends AbstractAction {
private JTable table;
public CopyAction(JTable table) {
this.table = table;
putValue(NAME, "Copy");
}
#Override
public void actionPerformed(ActionEvent e) {
int row = table.getSelectedRow();
int col = table.getSelectedColumn();
Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
cb.setContents(new CellTransferable(table.getValueAt(row, col)), null);
}
}
public class PasteAction extends AbstractAction {
private JTable table;
public PasteAction(JTable table) {
this.table = table;
putValue(NAME, "Paste");
final Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
cb.addFlavorListener(new FlavorListener() {
#Override
public void flavorsChanged(FlavorEvent e) {
setEnabled(cb.isDataFlavorAvailable(CellTransferable.CELL_DATA_FLAVOR));
}
});
setEnabled(cb.isDataFlavorAvailable(CellTransferable.CELL_DATA_FLAVOR));
}
#Override
public void actionPerformed(ActionEvent e) {
int row = table.getSelectedRow();
int col = table.getSelectedColumn();
Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
if (cb.isDataFlavorAvailable(CellTransferable.CELL_DATA_FLAVOR)) {
try {
Object value = cb.getData(CellTransferable.CELL_DATA_FLAVOR);
table.setValueAt(value, row, col);
} catch (UnsupportedFlavorException | IOException ex) {
ex.printStackTrace();
}
}
}
}
public static class CellTransferable implements Transferable {
public static final DataFlavor CELL_DATA_FLAVOR = new DataFlavor(Object.class, "application/x-cell-value");
private Object cellValue;
public CellTransferable(Object cellValue) {
this.cellValue = cellValue;
}
#Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{CELL_DATA_FLAVOR};
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return CELL_DATA_FLAVOR.equals(flavor);
}
#Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if (!isDataFlavorSupported(flavor)) {
throw new UnsupportedFlavorException(flavor);
}
return cellValue;
}
}
}
Here is code by using Clipboard
Steps to follow:
Declare some variable to store the current selected row and column index
private static int rowIndex;
private static int columnIndex;
Add ActionListener on MenuItem
menuItemCopy.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
StringSelection stringSelection = new StringSelection(String.valueOf(table1
.getModel().getValueAt(rowIndex, columnIndex)));
Clipboard clpbrd = Toolkit.getDefaultToolkit().getSystemClipboard();
clpbrd.setContents(stringSelection, null);
}
});
menuItemPaste.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Clipboard clpbrd = Toolkit.getDefaultToolkit().getSystemClipboard();
try {
table1.getModel().setValueAt(clpbrd.getData(DataFlavor.stringFlavor), rowIndex,
columnIndex);
} catch (UnsupportedFlavorException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
Add MouseListener on JTable to update the value of rowIndex and columnIndex and finally show the JPopupMenu
table1.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == 3) {
rowIndex = table1.rowAtPoint(e.getPoint());
columnIndex = table1.columnAtPoint(e.getPoint());
popup.show(e.getComponent(), e.getX(), e.getY());
}
}
});
int row = source.rowAtPoint(e.getPoint());
int column = source.columnAtPoint(e.getPoint());
Object valueInCell = table.getValueAt(row, column);
Simple!

Double clicking on a file in FileTreeModel

I am comfortable with Java but to swing API. After googling for sometime, I have got below program working fine so far. Now what I want is, on double clicking on any file in the tree, below function to be executed.
Can someone suggest me how can I add a listener to understand double click event? Example would really help.
public boolean getSomeData(String fileName){
//I will make JDBC call here
}
My working program is as below,
/*http://www.chka.de/swing/tree/FileTreeModel.html
*/
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.EventListenerList;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.io.File;
import java.io.Serializable;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
public class FileTreeModel
implements TreeModel, Serializable, Cloneable
{
protected EventListenerList listeners;
private static final Object LEAF = new Serializable() { };
private Map map;
private File root;
public FileTreeModel(File root)
{
this.root = root;
if (!root.isDirectory())
map.put(root, LEAF);
this.listeners = new EventListenerList();
this.map = new HashMap();
}
public Object getRoot()
{
return root;
}
public boolean isLeaf(Object node)
{
return map.get(node) == LEAF;
}
public int getChildCount(Object node)
{
List children = children(node);
if (children == null)
return 0;
return children.size();
}
public Object getChild(Object parent, int index)
{
return children(parent).get(index);
}
public int getIndexOfChild(Object parent, Object child)
{
return children(parent).indexOf(child);
}
protected List children(Object node)
{
File f = (File)node;
Object value = map.get(f);
if (value == LEAF)
return null;
List children = (List)value;
if (children == null)
{
File[] c = f.listFiles();
if (c != null)
{
children = new ArrayList(c.length);
for (int len = c.length, i = 0; i < len; i++)
{
children.add(c[i]);
if (!c[i].isDirectory())
map.put(c[i], LEAF);
}
}
else
children = new ArrayList(0);
map.put(f, children);
}
return children;
}
public void valueForPathChanged(TreePath path, Object value)
{
}
public void addTreeModelListener(TreeModelListener l)
{
listeners.add(TreeModelListener.class, l);
}
public void removeTreeModelListener(TreeModelListener l)
{
listeners.remove(TreeModelListener.class, l);
}
public Object clone()
{
try
{
FileTreeModel clone = (FileTreeModel)super.clone();
clone.listeners = new EventListenerList();
clone.map = new HashMap(map);
return clone;
}
catch (CloneNotSupportedException e)
{
throw new InternalError();
}
}
public static void main(String[] args)
{
if (args.length != 1)
{
System.err.println("Usage: java FileTreeModel path");
System.exit(1);
}
File root = new File(args[0]);
if (!root.exists())
{
System.err.println(root+ ": No such file or directory");
System.exit(2);
}
JTree tree = new JTree(new FileTreeModel(root));
JFrame f = new JFrame(root.toString());
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
f.getContentPane().add(new JScrollPane(tree));
f.pack();
f.setVisible(true);
}
}
I think you should add MouseListener and implement the mousePressed method
MouseListener ml = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if(e.getClickCount() == 2) {
getSomeData(...);
}
}
};
tree.addMouseListener(ml);

want to change the cursor on mouse move on JTree listed elements

I want to change the cursor to hand cursor on mouse move over JTree component when the cursor is on listed elements only, not for the whole component.
The below code is for Jlist Component. I want same for the JTree, but JTree does not have getCellBound(). Is there any other way?
final JList list = new JList(new String[] {"a","b","c"});
list.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseMoved(MouseEvent e) {
final int x = e.getX();
final int y = e.getY();
// only display a hand if the cursor is over the items
final Rectangle cellBounds = list.getCellBounds(0, list.getModel().getSize() - 1);
if (cellBounds != null && cellBounds.contains(x, y)) {
list.setCursor(new Cursor(Cursor.HAND_CURSOR));
} else {
list.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
}
#Override
public void mouseDragged(MouseEvent e) {
}
});
You are looking for something like this, I guess:
import java.awt.Cursor;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
public class TestTreeSelection {
protected void initUI() {
final DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
fillTree(root, 5, "Some tree label");
final DefaultTreeModel model = new DefaultTreeModel(root);
final JTree tree = new JTree(model);
tree.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
boolean inside = false;
TreePath path = tree.getPathForLocation(e.getX(), e.getY());
if (path != null) {
Rectangle pathBounds = tree.getPathBounds(path);
inside = pathBounds.contains(e.getPoint());
}
if (inside) {
tree.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
} else {
tree.setCursor(Cursor.getDefaultCursor());
}
}
});
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(tree));
f.setSize(400, 600);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private void fillTree(DefaultMutableTreeNode parent, int level, String label) {
for (int i = 0; i < 5; i++) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode(label + " " + i);
parent.add(node);
if (level > 0) {
fillTree(node, level - 1, label);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestTreeSelection().initUI();
}
});
}
}
This code works:
tree_object.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
TreePath tp = ((JTree)e.getSource()).getPathForLocation(e.getX(), e.getY());
if(tp != null)
{
((JTree)e.getSource()).setCursor(new Cursor(Cursor.HAND_CURSOR));
}
else
{
((JTree)e.getSource()).setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
}
});

Using MouseListener with JTree

I am using a MouseListener to detect double clicks on JTree items. That JTree is located in a JScrollPane. When I detect a double click (a MouseEvent), I get the path for the click location from the JTree. Most of the time, this works fine.
Now if I double click on a (collapsed) node with children, the node expands and therefor the scroll pane scrolls down. When I try to get the tree path from the click location, the JTree looks at the current (scrolled) view and returns the wrong item as the click location refers to the previous view (not scrolled).
Does anyone have an idea how to fix this? Below, I will attach an example demonstrating the problem.
package test;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JDialog;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
public class TestMain extends JDialog implements MouseListener {
protected final JTree tree;
public TestMain() {
tree = new JTree(getRootNode());
tree.addMouseListener(this);
JScrollPane pane = new JScrollPane(tree);
pane.setPreferredSize(new Dimension(250, 300));
getContentPane().add(pane, BorderLayout.CENTER);
pack();
}
private TreeNode getRootNode() {
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
for (int i = 0; i < 10; i++) {
root.add(new DefaultMutableTreeNode("Node " + i));
}
DefaultMutableTreeNode sub = new DefaultMutableTreeNode("Sub");
root.add(sub);
for (int i = 0; i < 10; i++) {
sub.add(new DefaultMutableTreeNode("Sub " + i));
}
return root;
}
#Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
TreePath path = tree.getPathForLocation(e.getX(), e.getY());
if (path != null) {
System.out.println(path.getLastPathComponent().toString());
}
}
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
public static void main(String[] args) {
JDialog dialog = new TestMain();
dialog.setVisible(true);
}
}
I would recommend you to use JTree.getSelectionPath().getLastPathComponent() since it won't change on scroll.

Categories