2

What I want to happen is while editing JTree, I want to commit the edit if I click outside of the edit box. tree.setInvokesStopCellEditing(true); helped, where if I click somewhere in the JTree, the edit gets committed. However if I click outside of the JTree, like say a JTextPane the edit won't commit. Thus the Question: Question: How do I make JTree stop cell editing, when Focus Lost? Or When Left Click outside JTree Location? Also is there a prefered way to do this?

Note: In the code below I tried to solve this using a Focus Listener Annoymous inner class, and tried pseudocode WhenTreeLosesFocus() { if( tree.isEditing() ) tree.stopEditing(); }

That doesn't work because On Edit the Focus changes to a CellEditor component within the Tree, and I see the edit box flash on and then flash off quickly.

The solution doesn't have to be Focus based, it can be left click outside JTree area.

SSCE follows (BTW if you run the code for Convienence both Spacebar and F2 will rename nodes.)

import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextPane;
import javax.swing.JTree;
import javax.swing.KeyStroke;

public class StackExchangeQuestion3 {

     public static void main(String[] args){     
     JFrame window = new JFrame();
     window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     window.setTitle("Stack Exchange Question");
     window.setSize(400,500);//variable parameters would be best
     window.setVisible(true);           

     JSplitPane split = new JSplitPane();
     window.getRootPane().setContentPane(split);
     JScrollPane left = new JScrollPane();
     JScrollPane right = new JScrollPane();
     JTree tree = new JTree();//loads sample tree data
     tree.setEditable(true);//select a node then press F2 to edit (built in keybinding)
     tree.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "startEditing");//can edit with space now
     tree.setInvokesStopCellEditing(true);//this helps stop editing within focus of tree

     JTextPane text = new JTextPane();
     split.setLeftComponent(left);
     split.setRightComponent(right);
     left.setViewportView(tree);
     right.setViewportView(text);
     split.setDividerLocation(200);

     tree.addFocusListener(new FocusListener(){
         public void focusGained(FocusEvent e) {         }
         public void focusLost(FocusEvent e) {
         JTree tree = (JTree)e.getSource();
         if( tree.isEditing() ) tree.stopEditing();
         }
     });//end addFocusListener
     }//end main
}

Commentary with reasoning on if 1 way is preferred over the other is welcome. As well as Tips on how to Detect if Mouse Click outside of JTree/tips in the right direction.

neoakris
  • 4,217
  • 1
  • 30
  • 32
  • I added `System.out.println(e.getOppositeComponent().getClass().getName());` to focusLost(FocusEvent e) it gives me something to compare to with an if statement, however I'm starting to think It'd be best to add a focus listener to "javax.swing.tree.DefaultTreeCellEditor$DefaultTextField" to handle this instead. – neoakris Jul 25 '13 at 17:27
  • I wish I could do `tree.getCellEditor().addFocusListener()` , but .addFocusListener() doesn't exist for TreeCellEditor, .getCellEditor() has a .addCellEditorListener but it's methods don't seem helpful. A possible yet ugly solution would be to add a Focus Listener to Every Other Click-able Component. (This doesn't work in the case of JSplitPane) – neoakris Jul 25 '13 at 19:46
  • I'm working on this idea, of adding a glasspane, setting it visible, adding a MouseListener, only problem is the glasspane consumes MouseClicks. Based on this [question](http://stackoverflow.com/questions/1408080/mouselistener-on-jframe) **it seems I can prevent the MouseClicks from being consumed, only problem is I don't know how** (researching atm), if I can figure out how, It'd be easy to check if MouseClick is outside of JTree – neoakris Jul 25 '13 at 20:48
  • Any help [here](http://stackoverflow.com/a/11113648/230513)? – trashgod Jul 25 '13 at 23:37
  • Reading it now, earlier I was looking into [create mouseListener on GlassPane, that doesn't consume mouse Event, via redispatching](http://codeidol.com/java/swing/Rendering/Create-a-Global-Right-Click/) I tried that code in my SSCE, but havn't been able to get it to work (It may become a separate question, for curiosity sake). For now I'll try attacking the problem from a different angle using your link. – neoakris Jul 26 '13 at 00:10
  • Your link wasn't directly helpful, however [it did contain an alternative approach by kleopatra](http://stackoverflow.com/questions/12642792/easy-and-fast-jtree-cell-editor/12651990#12651990) That looks promising, basically `DefaultTreeCellEditor` has a method `getTreeCellEditorComponent(ton of parameters)` I might be able to use that to get a Component which I can add a Focus Listener to, and override Focus Lost to do my bidding. – neoakris Jul 26 '13 at 00:50

1 Answers1

0
    import java.awt.Component;
    import java.awt.event.FocusEvent;
    import java.awt.event.FocusListener;
    import javax.swing.JFrame;
    import javax.swing.JScrollPane;
    import javax.swing.JSplitPane;
    import javax.swing.JTextPane;
    import javax.swing.JTree;
    import javax.swing.KeyStroke;
    import javax.swing.tree.DefaultTreeCellEditor;
    import javax.swing.tree.DefaultTreeCellRenderer;
    import javax.swing.tree.TreeCellRenderer;


    public class StackExchangeQuestion3AnsA {

         public static void main(String[] args){  
         new StackExchangeQuestion3AnsA();           
         }        


   StackExchangeQuestion3AnsA(){
         JFrame window;    
         window = new JFrame();
         window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
         window.setTitle("Stack Exchange Answer");
         window.setSize(400,500);//variable parameters would be best
         window.setVisible(true);           


         JSplitPane split = new JSplitPane();
       window.setContentPane(split);
         JScrollPane left = new JScrollPane();
         JScrollPane right = new JScrollPane();
         JTree tree;
         tree = new JTree();//loads sample tree data
         tree.setEditable(true);//select a node then press F2 to edit (built in keybinding)
         tree.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "startEditing");//can edit with space now
         tree.setInvokesStopCellEditing(true);//this helps stop editing within focus of tree
         //even with my fix the above line is still needed

         JTextPane text = new JTextPane();
         split.setLeftComponent(left);
         split.setRightComponent(right);
         left.setViewportView(tree);
         right.setViewportView(text);
         split.setDividerLocation(200);   

         tree.setCellEditor( new MyTreeCellEditor(tree, tree.getCellRenderer()) );

   }//end constructor


private class MyTreeCellEditor extends DefaultTreeCellEditor {
     public MyTreeCellEditor(JTree tree, TreeCellRenderer renderer) {
                    super(tree, (DefaultTreeCellRenderer)renderer);
//Note: http://stackoverflow.com/questions/5031101/why-i-lose-the-focus-for-a-short-time-from-jtree-after-editing-a-node?rq=1 
//André mentions "The tree adds a DefaultCellEditor to the JTree hierarchy when editing starts. This textfield gains the focus."                    
//it's not as simple as just this.addFocusListener()

//2 variables: editingComponent and editingContainer, are inherited from DefaultTreeCellEditor
//the constructor doesn't initialize the editingComponent
//it's null atm, so we can't add focusListener to it, (yet at a later time it will gain focus)    
//FocusOwner (on edit): javax.swing.tree.DefaultTreeCellEditor$DefaultTextField
//editingComponent is by default a DefaultTextField                    
        }        

    @Override
    public Component getTreeCellEditorComponent(final JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) {
       Component container = super.getTreeCellEditorComponent(tree, value, isSelected, expanded, leaf, row);
//Note: System.out.println("Components Type: "+containeractually.getClass().getName());
//was used to show it was container, javax.swing.tree.DefaultTreeCellEditor$EditorContainer

//getTreeCellEditorComponent(parameters) is called as soon as editing begins
//also at this time editingComponent != null (aka initialized)
//so it's a good place to add in a Focus Listener

editingComponent.addFocusListener( new FocusListener(){
                @Override  public void focusGained(FocusEvent e) { }
                @Override  public void focusLost(FocusEvent e) {
                tree.stopEditing();}                  } );

//EditorContainer is responsible for displaying the editingComponent
//so we added focusListener, after editingComponent initialized, and before it's used
//(I think the return statement means it's about to be used)
        return container;
        }
}//end MyTreeCellEditor

}//end class         
neoakris
  • 4,217
  • 1
  • 30
  • 32
  • I don't remember where but I read the glass pane solution might not be useable on applets/all cases, so I'd say that makes the focus listener based solution potentially better. (but I also heard that different operating systems treat focus differently, so I guess there's still pros and cons, feel free to add a glass pane based soln, I'm happy with this one) – neoakris Jul 27 '13 at 00:44