How to edit a JTree node with a single-click - java

I have a JTree, and would like for its getTreeCellEditorComponent() method to be invoked when I single click on a node. According to the documentation for the DefaultTreeCellEditor class (which I extended), "Editing is started on a triple mouse click, or after a click, pause, click and a delay of 1200 miliseconds." Is there some way to override this behavior, so that a single-click could start the editing process?

The JTree API recommends a MouseListener, but a key binding is also handy. This example invokes startEditingAtPath() and binds to the Enter key:
final JTree tree = new JTree();
tree.setEditable(true);
MouseListener ml = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
int row = tree.getRowForLocation(e.getX(), e.getY());
TreePath path = tree.getPathForLocation(e.getX(), e.getY());
if (row != -1) {
if (e.getClickCount() == 1) {
tree.startEditingAtPath(path);
}
}
}
};
tree.addMouseListener(ml);
tree.getInputMap().put(
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "startEditing");
Addendum: See also this answer regarding usability.

Technically, you can subclass DefaultTreeCellEditor and tweaks its logic to start editing on the first single click:
JTree tree = new JTree();
tree.setEditable(true);
TreeCellEditor editor =
new DefaultTreeCellEditor(tree, (DefaultTreeCellRenderer) tree.getCellRenderer()) {
#Override
protected boolean canEditImmediately(EventObject event) {
if((event instanceof MouseEvent) &&
SwingUtilities.isLeftMouseButton((MouseEvent)event)) {
MouseEvent me = (MouseEvent)event;
return ((me.getClickCount() >= 1) &&
inHitRegion(me.getX(), me.getY()));
}
return (event == null);
}
};
tree.setCellEditor(editor);
There's a usability quirk, though, as now you can't select without starting an edit - which may or may not be your intention.

Related

Jtree get object based on mouse click?

I use this code fragment to perform some action on a tree when the mouse is double-clicked: open a window and get the node which was double clicked by the mouse, but it doesn't return anything, it returns null:
MouseListener ml = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int selRow = contactTree.getRowForLocation(e.getX(), e.getY());
TreePath selPath = contactTree.getPathForLocation(e.getX(), e.getY());
System.out.println(contactTree.getEditingPath());
Account memberToChat;
if(selRow != -1) {
if(e.getClickCount() == 1) {
}
else if(e.getClickCount() == 2) {
new ChatWindow().setVisible(true);
memberToChat=(Account)node.getUserObject(); // node is declared somewhere in the class as DefaultMutableTreeNode node
System.out.println(memberToChat.getFirstName()+" "+memberToChat.getEmail());
}
}
}
};
for JTree to set proper setSelectionMode
add TreeSelectionListener
example with TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION

java JTree deselecting a node that is already selected

I am developing a java application that uses a JTree. What i would like to archive is that when i click on a node that is already selected it gets deselected.
My current solution is to add a mouse listener and a tree selection listener to the jtree. But the problem is that valueChanged gets called only if there is a change in selection (and not if you select the same node twice). To fix this i added a boolean which indicated if the node was clicked for the first time and then i handle the deselect in the mouseReleased function. This works but the problem now is if the node has children and you want to expand it the node gets deselected and reselected again (which i dont want).
How could i fix this problem? Is there any better way of deselecting a already selected node?
The code:
public void initComponents()
{
elementsTree.addTreeSelectionListener(this);
elementsTree.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent me) {
TreePath tp = elementsTree.getPathForLocation(me.getX(), me.getY());
if (tp != null)
{
if(!nodeSelected && elementsTree.getSelectionModel().isPathSelected(tp) )
{
elementsTree.getSelectionModel().removeSelectionPath(tp);
}
}
nodeSelected = false;
}
});
}
public void valueChanged(TreeSelectionEvent e)
{
nodeSelected = true;
}
Thanks!
Just use the method clearSelection() from JTree.

Add JPopup menu by right clicking on node in Swing in Java

In GUI,I am displaying one JTree at the left hand side of JPanel. Now for each Node(leaf), on Mouse right click I want to display JPopup menu asking for displaying the statistics about Node in right JPanel.
As i am new to swing,Could any one help with code.
Thanks in Advance.
Regards,
Tushar Dodia.
Use JTree's method
public TreePath getPathForLocation(int x, int y)
Then TreePath
public Object getLastPathComponent()
That returns you desired node from point where user right clicked.
Seem to have caused a bit of confusion (confusing myself ;-) - so here's a code snippet for doing target location related configuration of the componentPopup
JPopupMenu popup = new JPopupMenu();
final Action action = new AbstractAction("empty") {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
};
popup.add(action);
JTree tree = new JTree() {
/**
* #inherited <p>
*/
#Override
public Point getPopupLocation(MouseEvent e) {
if (e != null) {
TreePath path = getClosestPathForLocation(e.getX(), e.getY());
action.putValue(Action.NAME, String.valueOf(path.getLastPathComponent()));
return e.getPoint();
}
action.putValue(Action.NAME, "no mouse");
return null;
}
};
tree.setComponentPopupMenu(popup);
I took #kleopatra solution and changed it slightly.
Maybe it isn't the best way but works for me.
JTree tree = new JTree() {
private static final long serialVersionUID = 1L;
#Override public Point getPopupLocation(MouseEvent e) {
if (e == null) return new Point(0,0);
TreePath path = getClosestPathForLocation(e.getX(), e.getY());
Object selected = path != null ? path.getLastPathComponent() : null;
setComponentPopupMenu(getMenuForTreeNode(getComponentPopupMenu(), selected));
setSelectionPath(path);
return e.getPoint();
}
};
public JPopupMenu getMenuForTreeNode(JPopupMenu menu, Object treeNode) {
if (menu == null) menu = new JPopupMenu("Menu:");
menu.removeAll();
if (treeNode instanceof MyTreeItem) {
menu.add(new JMenuItem("This is my tree item: " + treeNode.toString()));
}
return menu;
}

Java Swing Shift+F10 Accessibility

Per accessibility requirements, Shift+F10 is supposed to open right-click context menus.
In Swing, one approach is to just add the key binding to every component you make. However, I've experimented with extending the EventQueue to handle all Shift+F10 presses. In particular, I've overridden dispatchEvent(AWTEvent) to convert Shift+F10 KeyEvents into right-click mousePresses:
protected void dispatchEvent(AWTEvent event) {
if (event instanceof KeyEvent) {
KeyEvent ev = (KeyEvent) event;
if ((ev.getKeyCode() == KeyEvent.VK_F10) &&
(ev.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) > 0) {
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
Component comp = kfm.getFocusOwner();
Point mouse = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(mouse, comp);
eventToDispatch = new MouseEvent(comp,
MouseEvent.MOUSE_RELEASED, ev.getWhen(), 0, mouse.x, mouse.y,
1, true);
}
}
}
However, this prevents Shift+F10 from being able to close any JPopupMenus that get launched. Any idea if this solution is workable, or are there better ways to accomplish meeting this requirement?
ActionListener actionListener = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
try {
int dotPosition = textField.getCaretPosition();
Rectangle popupLocation = textField
.modelToView(dotPosition);
popup.show(textField, popupLocation.x, popupLocation.y);
} catch (BadLocationException badLocationException) {
System.out.println("Oops");
}
}
};
KeyStroke keystroke = KeyStroke.getKeyStroke(KeyEvent.VK_F10,
InputEvent.SHIFT_MASK);
textField.registerKeyboardAction(actionListener, keystroke,
JComponent.WHEN_FOCUSED);

Right-click context menu for Java JTree?

I'm trying to implement pop-up menus in Java JTree. I've sub-classed DefaultTreeCellRenderer (to change node appearance) and DefaultTreeCellEditor (to create Components to attach event listeners to, since apparently the Components that DefaultTreeCellRenderer.getTreeCellRendererComponent() returns can't do it?). I don't really want to "edit" the nodes, just be able to pop-up a menu when a node gets right-clicked, but this is the only way I can think of doing it right now...
Below is the code that I have so far-- I'm just trying to figure out how to capture MouseEvents. It sort of works, but badly. What's a better way to accomplish what I'm trying to do here?
private class My_TreeCellRenderer extends DefaultTreeCellRenderer {
My_TreeCellRenderer() {
super ();
}
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
// set label text and tool tips
setText(((My_Object)value).getTreeLabel());
setToolTipText(((My_Object)value).getTreeToolTip());
return this;
}
}
private class My_TreeCellEditor extends DefaultTreeCellEditor {
private MouseAdapter ma;
My_TreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
super (tree, renderer);
ma = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
System.out.println("My Popup");
}
}
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
System.out.println("My Popup");
}
}
};
}
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) {
String src_filename = null;
// return non-editing component
Component c = renderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, true);
// add mouse listener if it's not listening already
MouseListener mouseListeners[] = c.getMouseListeners();
int i;
for (i=0; i < mouseListeners.length && mouseListeners[i] != ma; i++);
if (i >= mouseListeners.length)
c.addMouseListener(ma);
return c;
}
protected boolean canEditImmediately(EventObject event) {
if (event instanceof MouseEvent && ((MouseEvent)event).getClickCount() == 1)
return true;
else
return false;
}
}
This task is simple to accomplish, the following is all you need:
//create a class which implements the MouseListener interface and
//implement the following in your overridden mouseClicked method
#Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
int row = tree.getClosestRowForLocation(e.getX(), e.getY());
tree.setSelectionRow(row);
popupMenu.show(e.getComponent(), e.getX(), e.getY());
}
}
You can then add this custom listener to your desired tree(s).
Taken right out of the JTree API
// If you are interested in detecting either double-click events or when a user clicks on a node, regardless of whether or not it was selected, we recommend you do the following:
final JTree tree = ...;
MouseListener ml = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int selRow = tree.getRowForLocation(e.getX(), e.getY());
TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
if(selRow != -1) {
if(e.getClickCount() == 1) {
mySingleClick(selRow, selPath);
}
else if(e.getClickCount() == 2) {
myDoubleClick(selRow, selPath);
}
}
}
};
tree.addMouseListener(ml);
Of course you need to modify it a bit for right click instead of left click
Thanks everyone. I knew something was wrong when I was spending that much effort on implementing a simple popup.
I dismissed this line of thought at first because it felt weird to resort to x- and y- coordinates to find the node I'm looking for, but I guess this is the way to do it.
// add MouseListener to tree
MouseAdapter ma = new MouseAdapter() {
private void myPopupEvent(MouseEvent e) {
int x = e.getX();
int y = e.getY();
JTree tree = (JTree)e.getSource();
TreePath path = tree.getPathForLocation(x, y);
if (path == null)
return;
tree.setSelectionPath(path);
My_Obj obj = (My_Obj)path.getLastPathComponent();
String label = "popup: " + obj.getTreeLabel();
JPopupMenu popup = new JPopupMenu();
popup.add(new JMenuItem(label));
popup.show(tree, x, y);
}
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) myPopupEvent(e);
}
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) myPopupEvent(e);
}
};
(...)
JTree tree = new JTree();
tree.addMouseListener(ma);
I think you're making things way harder than they need to be.
JTree has several "add_foo_Listener" methods on it. Implement one of those (TreeSelectionListener looks about right), and then you've got the currently selected node.
Implement a MouseListener so that you can detect the right-click event (and add it to the JTree, since JTree's a Component), and then you should have everything you need to post a context-sensitive menu.
Check out this tutorial for more details.
The Renderer is only a transient "rubber stamp", so adding an input listener on that wont be particularly helpful. The Editor, as you point out, is only there once you have gestured to edit. So you want to add a listener to the JTree (assuming it isn't implemented as a composite component).
Call addRightClickListener() to add the right-click context menu listener to your JTree. Both overrides are for proper cross-platform functionality (Windows and Linux differ here).
private void addRightClickListener()
{
MouseListener mouseListener = new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent mouseEvent)
{
handleContextMenu(mouseEvent);
}
#Override
public void mouseReleased(MouseEvent mouseEvent)
{
handleContextMenu(mouseEvent);
}
};
tree.addMouseListener(mouseListener);
}
private void handleContextMenu(MouseEvent mouseEvent)
{
if (mouseEvent.isPopupTrigger())
{
MyContextMenu contextMenu = new MyContextMenu();
contextMenu.show(mouseEvent.getComponent(),
mouseEvent.getX(),
mouseEvent.getY());
}
}

Categories

Resources