TreeCellEditor: must select cell to edit even if ShouldSelectCell return false

穿精又带淫゛_ 提交于 2020-01-21 05:52:06

问题


I need to use custom cell renderer for my JTree to add some JLabel on each cell. And then allow the user to click on these label without needing to select the cell first.

So, i've created a Renderer which return a JPanel that contains a DefaultTreeCellRenderer and 2 JLabel.

    public class TreeNodeRenderer extends DefaultTreeCellRenderer implements TreeCellRenderer
    {
        private JPanel panel1 = new JPanel();
        private JLabel delete = new JLabel("");
        private JLabel upload = new JLabel("");

        public Component getTreeCellRendererComponent(JTree tree, 
                            Object value,
                boolean selected, boolean expanded, boolean leaf, int row,
                boolean hasFocus) 
        {   
            //
            // DELETE label
            //
            delete.setName("delete");
            delete.setIcon(new ImageIcon("Data/trash.png"));

            //
            // UPLOAD label
            //
            upload.setName("upload");
            upload.setIcon(new ImageIcon("Data/app_up.png"));


            DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer();
            Color backgroundSelectionColor = defaultRenderer.getBackgroundSelectionColor();
            Color backgroundNonSelectionColor = defaultRenderer.getBackgroundNonSelectionColor();

            if(selected)
                panel1.setBackground(backgroundSelectionColor);
            else
                panel1.setBackground(backgroundNonSelectionColor);


            component = (DefaultTreeCellRenderer) super.getTreeCellRendererComponent(tree,
                    value, selected, expanded, leaf, row, hasFocus);

            panel1.add(component);
            panel1.add(delete);
            panel1.add(upload);

            return panel1;
        }
    }

Then i've created the editor to allow user to click on these labels thanks to a MouseListener. Everything works well except that the user must select the cell before click on a label. I tried to return "false" with the method "ShouldSelectCell" but it doesn't work.

Does someone know why ?

Here the editor:

public class TreeNodeEditor extends AbstractCellEditor implements TreeCellEditor
{
    private TreeNodeRenderer renderer;

    public TreeNodeEditor(TreeNodeRenderer treeRenderer)
    {
        this.renderer = treeRenderer;

    //change the cursor when it's over a label  renderer.getDeleteButton().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    renderer.getUploadButton().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));       renderer.getDownloadButton().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));

                 //add labels' mouse listeners 
        addLabelMouseListener(renderer.getDeleteButton());
        addLabelMouseListener(renderer.getUploadButton());
    }

    public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row)
    {
        ...

        return renderer.getTreeCellRendererComponent(
                tree, value, isSelected,
                expanded, leaf, row, true);
    }

    public boolean isCellEditable(EventObject anEvent) 
    {
        return true;
    }

    public boolean shouldSelectCell(EventObject anEvent) 
    {
        return false;
    }


    public boolean stopCellEditing() 
    {
        return super.stopCellEditing();
    }

    public void cancelCellEditing() 
    {
        super.cancelCellEditing();
    }

    public void addCellEditorListener(CellEditorListener l)
    {
        super.addCellEditorListener(l);
    }

    public void removeCellEditorListener(CellEditorListener l) 
    {
        super.removeCellEditorListener(l);
    }

    public Object getCellEditorValue() 
    {
        return null;
    }
}

EDIT - Here a SSCCE:

import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.EventObject;

import javax.swing.AbstractCellEditor;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.CellEditorListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;



public class EditJTreeCell extends JFrame
{
    /**
     * 
     */
    private static final long serialVersionUID = 4745146614430249610L;

    private JTree tree;
    private DefaultTreeModel treeModel;
    private DefaultMutableTreeNode root;

    public EditJTreeCell()
    {
        super("Sample");
        root = new DefaultMutableTreeNode("Root folder");
        treeModel = new DefaultTreeModel(root);
        tree = new JTree(treeModel);

        TreeNodeRenderer renderer = new TreeNodeRenderer();
        tree.setCellRenderer(renderer);
        tree.setCellEditor(new TreeNodeEditor());
        tree.setEditable(true);

        //tree creation
        DefaultMutableTreeNode folder = new DefaultMutableTreeNode("folder1");
        DefaultMutableTreeNode file = new DefaultMutableTreeNode("file1");
        folder.add(file);
        file = new DefaultMutableTreeNode("file2");
        folder.add(file);
        root.add(folder);
        folder = new DefaultMutableTreeNode("folder2");
        file = new DefaultMutableTreeNode("file1");
        folder.add(file);
        file = new DefaultMutableTreeNode("file2");
        folder.add(file);
        file = new DefaultMutableTreeNode("file3");
        folder.add(file);
        root.add(folder);

        this.setSize(400, 800);
        this.add(tree);
        this.setVisible(true);
    }

    public static void main(String[] args) 
    {
        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (UnsupportedLookAndFeelException e) {
            e.printStackTrace();
        }
        new EditJTreeCell();
    }
}

class TreeNodeRenderer extends DefaultTreeCellRenderer implements TreeCellRenderer
{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private JPanel panel1 = new JPanel();
    private JLabel delete = new JLabel("DELETE");
    private JLabel upload = new JLabel("UPLOAD");

    public Component getTreeCellRendererComponent(JTree tree, Object value,
            boolean selected, boolean expanded, boolean leaf, int row,
            boolean hasFocus) 
    {   
        //
        // DELETE label
        //
        delete.setName("delete");
        delete.setIcon(new ImageIcon("trash.png"));
        //addLabelMouseListener(delete);
        //
        // UPLOAD label
        //
        upload.setName("upload");
        upload.setIcon(new ImageIcon("app_up.png"));
        //addLabelMouseListener(upload);


        DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer();
        Color backgroundSelectionColor = defaultRenderer.getBackgroundSelectionColor();
        Color backgroundNonSelectionColor = defaultRenderer.getBackgroundNonSelectionColor();

        DefaultTreeCellRenderer component = (DefaultTreeCellRenderer) super.getTreeCellRendererComponent(tree,
                value, selected, expanded, leaf, row, hasFocus);

        if(selected)
        {   
            panel1.setBackground(backgroundSelectionColor);
        }
        else
        {
            panel1.setBackground(backgroundNonSelectionColor);
        }

        panel1.add(component);
        panel1.add(delete);
        panel1.add(upload);

        return panel1;
    }
}

class TreeNodeEditor extends AbstractCellEditor implements TreeCellEditor
{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private JLabel button1;
    private JLabel button2;
    private JPanel panel1;
    private DefaultMutableTreeNode node = null;
    private DefaultTreeCellRenderer defaultRenderer;


    public TreeNodeEditor()
    {
        super();
        panel1 = new JPanel();
        defaultRenderer = new DefaultTreeCellRenderer();
        button1 = new JLabel("DELETE");
        button1.setOpaque(true);
        button1.setIcon(new ImageIcon("trash.png"));
        button1.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        button1.addMouseListener(new MouseListener() 
        {
            public void mouseClicked(MouseEvent arg0) {
                System.out.println("Delete clicked");
            }
            public void mouseEntered(MouseEvent arg0) {}
            public void mouseExited(MouseEvent arg0) {}
            public void mousePressed(MouseEvent arg0) {}
            public void mouseReleased(MouseEvent arg0) {}

        });
        button2 = new JLabel("UPLOAD");
        button2.setOpaque(true);
        button2.setIcon(new ImageIcon("app_up.png"));
        button2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        button2.addMouseListener(new MouseListener() 
        {
            public void mouseClicked(MouseEvent arg0) {
                System.out.println("Upload clicked");
            }
            public void mouseEntered(MouseEvent arg0) {}
            public void mouseExited(MouseEvent arg0) {}
            public void mousePressed(MouseEvent arg0) {}
            public void mouseReleased(MouseEvent arg0) {}

        });
    }


    public Component getTreeCellEditorComponent(JTree tree, Object value, 
            boolean isSelected, boolean expanded, boolean leaf, int row)
    {
        //in order to do some actions on a node
        if(value instanceof DefaultMutableTreeNode)
        {
            node = (DefaultMutableTreeNode) value;
        }


         defaultRenderer.getTreeCellRendererComponent(tree,
                    value, isSelected, expanded, leaf, row, true);

        panel1.add(defaultRenderer);
        panel1.add(button1);
        panel1.add(button2);
        return panel1;
    }

    public boolean isCellEditable(EventObject anEvent) 
    {
        return true;
    }

    public boolean shouldSelectCell(EventObject anEvent) 
    {
        return false;
    }


    public boolean stopCellEditing() 
    {
        return super.stopCellEditing();
    }

    public void cancelCellEditing() 
    {
        super.cancelCellEditing();
    }

    public void addCellEditorListener(CellEditorListener l)
    {
        super.addCellEditorListener(l);
    }

    public void removeCellEditorListener(CellEditorListener l) 
    {
        super.removeCellEditorListener(l);
    }

    public Object getCellEditorValue() 
    {
        return null;
    }
}

回答1:


starting an edit on mouseEnter is a valid solution :-)

Your editor, on the other hand is not a valid implementation: it fails on not notifying its listener if the edit is terminated due to internal events (as f.i. clicking on any of the buttons) Below is an example of how-to achieve both your goal and have a valid implementation

a couple of comments:

  • if you want something like a button .. use a button: otherwise users might be confused
  • in your editor, set an action to the buttons as needed
  • do all basic panel config (like adding its children) in the constructor)
  • to start editing/detect which button is clicked, re-dispatch the event received in shouldSelect. Do it in a SwingUtilities.invokeLater to make sure any internally pending events (in the tree) are ready
  • do not change the tree node inside the editor: a) those changes will fail to notify the model b) will be overruled by the tree's default editing termination behaviour. DefaultTreeTable will reset the userObject of the tree with editorValue, that's done in valueForPathChanged: to implement custom behaviour, override that method in the model

in code:

static class TreeNodeEditor extends AbstractCellEditor implements TreeCellEditor {
    private static final long serialVersionUID = 1L;
    private JButton button1;
    private JButton button2;
    private JPanel panel1;
    // JW: do not modify the node inside the editor 
    //        private DefaultMutableTreeNode node = null;
    private DefaultTreeCellRenderer defaultRenderer;

    private Object editorValue;

    public TreeNodeEditor() {
        super();
        panel1 = new JPanel();
        defaultRenderer = new DefaultTreeCellRenderer();
        button1 = new JButton("DELETE");
        button1.setOpaque(true);
        button1.setIcon(new ImageIcon("trash.png"));
        button1.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        button2 = new JButton("UPLOAD");
        button2.setOpaque(true);
        button2.setIcon(new ImageIcon("app_up.png"));
        button2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        button2.setAction(createAction("upload", "UPLOAD"));
        button1.setAction(createAction("delete", "DELETE"));
        panel1.add(defaultRenderer);
        panel1.add(button1);
        panel1.add(button2);
    }

    private Action createAction(final String actionCommand, String display) {
        Action action = new AbstractAction(display) {

            @Override
            public void actionPerformed(ActionEvent e) {
                stopEditing(actionCommand);
            }

        };
        return action;

    }
    /**
     * @param actionCommand
     */
    protected void stopEditing(String actionCommand) {
        editorValue = actionCommand;
        stopCellEditing();
    }

    @Override
    public Component getTreeCellEditorComponent(JTree tree, Object value,
            boolean isSelected, boolean expanded, boolean leaf, int row) {
        // in order to do some actions on a node
        //            if (value instanceof DefaultMutableTreeNode) {
        //                node = (DefaultMutableTreeNode) value;
        //            }

        defaultRenderer.getTreeCellRendererComponent(tree, value,
                isSelected, expanded, leaf, row, true);

        return panel1;
    }

    /**
     * 
     */
    private void reset() {
        editorValue = null;
    }

    /**
     * At this point in time the component is added to the tree (not documented!) but
     * tree's internal cleanup might not yet be ready
     */ 
    @Override
    public boolean shouldSelectCell(EventObject anEvent) {
        reset();
        if (anEvent instanceof MouseEvent) {
            redirect((MouseEvent) anEvent);
        }
        return false;
    }

    private void redirect(final MouseEvent anEvent) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                MouseEvent ev = SwingUtilities.convertMouseEvent(anEvent.getComponent(), anEvent, panel1);
                panel1.dispatchEvent(ev);

            }
        });
    }

    @Override
    public Object getCellEditorValue() {
        return editorValue;
    }
}



回答2:


Finally, i solved my problem with a MouseMotionListener and the method : myTree.startEditingAtPath(path). A node is now in editing mode when the cursor is over it.

tree.addMouseMotionListener(new MouseMotionListener() {
            public void mouseMoved(MouseEvent e) 
            {
                if (tree.getRowForLocation(e.getX(), e.getY()) != -1)
                {
                    tree.startEditingAtPath(tree.getPathForLocation(e.getX(), e.getY()));
                }               
            }
            public void mouseDragged(MouseEvent e) {}
        });

However, if someone has a better idea, please let me know.




回答3:


I think you need to add the mouse listeners inside the treeNodeRenderer itself. It is likely that the mouselistener is only getting added after you enter 'edit mode' and editor is put into the cell.



来源:https://stackoverflow.com/questions/8119718/treecelleditor-must-select-cell-to-edit-even-if-shouldselectcell-return-false

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!