4 Replies Latest reply on Jan 14, 2011 9:19 PM by 802349

    JTree - highlight entire row on selection

    802349
      currently when i select a node in a JTree, only the label of that node is selected. is there a convenient way to select the entire row, from left to right?
        • 1. Re: JTree - highlight entire row on selection
          kleopatra-JavaNet
          no, there isn't. Some LAFs (like Nimbus f.i.) do - but the way it's done there disrespects render's responsibility

          Cheers
          Jeanette
          1 person found this helpful
          • 2. Re: JTree - highlight entire row on selection
            802349
            thanks for the info! i checked the tree highlighter code of the great Santhosh Kumar from http://jroller.com/santhosh/entry/highlight_a_node_s_descendants
            and made my own solution. in case anyone needs it as well, see below. the only annoyance remaining is that i always have to click on the text of a node in order to select it. of course i'd like to click anywhere in a node's row, even on whitespace, in order to select it. does anyone know how to do this properly?

            import java.awt.BorderLayout;
            import java.awt.Color;
            import java.awt.Container;
            import java.awt.Graphics;
            import java.awt.Rectangle;
            
            import javax.swing.JFrame;
            import javax.swing.JScrollPane;
            import javax.swing.JTree;
            import javax.swing.event.TreeSelectionEvent;
            import javax.swing.event.TreeSelectionListener;
            import javax.swing.tree.DefaultTreeCellRenderer;
            
            public class TreeExample {
            
                 public static Color selectedColor = new Color(115,164,209);
                 public static Color selectedBorderColor = new Color(57,105,138);
                 
                 public static void main(String args[]) {
                      
                      JFrame f = new JFrame("JTree Sample");
                      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                      Container content = f.getContentPane();
            
                      
                      // custom tree handling
                      final JTree tree = new MyTree();
                      MyTreeCellRenderer renderer = new MyTreeCellRenderer();
                      renderer.setBorderSelectionColor(null); // remove selection border
                      renderer.setBackgroundSelectionColor( null); // remove selection background since we paint the selected row ourselves
                      tree.setCellRenderer( renderer);
            
                   // send repaint event when node selection changes; otherwise sometimes the border remained visible although the selected node changed 
                      TreeSelectionListener treeSelListener = new TreeSelectionListener() {
                           public void valueChanged(TreeSelectionEvent evt) {
                                tree.treeDidChange();
                           }
                      };
                      tree.addTreeSelectionListener(treeSelListener);
                      
                      
                      // add tree to frame and show frame
                      JScrollPane scrollPane = new JScrollPane(tree);
                      content.add(scrollPane, BorderLayout.CENTER);
                      f.setSize(400, 600);
                      f.setVisible(true);
                 }
            
                 public static class MyTree extends JTree {
            
                      protected void paintComponent(Graphics g) {
            
                           // paint background
                           g.setColor(getBackground());
                           g.fillRect(0, 0, getWidth(), getHeight());
            
                           // paint selected node's background and border
                           int fromRow = getRowForPath( getSelectionPath());
                           if (fromRow != -1) {
                                int toRow = fromRow + 1;
                                Rectangle fromBounds = getRowBounds(fromRow);
                                Rectangle toBounds = getRowBounds(toRow - 1);
                                if (fromBounds != null && toBounds != null) {
                                     g.setColor(selectedColor);
                                     g.fillRect(0, fromBounds.y, getWidth(), toBounds.y - fromBounds.y + toBounds.height);
                                     g.setColor(selectedBorderColor);
                                     g.drawRect(0, fromBounds.y, getWidth() - 1, toBounds.y - fromBounds.y + toBounds.height);
                                }
                           }
            
                           // perform operation of superclass
                           setOpaque(false); // trick not to paint background
                           super.paintComponent(g);
                           setOpaque(false);
                      }
                 }
            
                 // trick to make renderer transparent for the unselected nodes 
                 public static class MyTreeCellRenderer extends DefaultTreeCellRenderer {
                      public MyTreeCellRenderer() {
                           setBackgroundNonSelectionColor(null);
                      }
            
                      public Color getBackground() {
                           return null;
                      }
                 }
            
            }
            • 3. Re: JTree - highlight entire row on selection
              terai
              import java.awt.*;
              import javax.swing.*;
              import javax.swing.tree.*;
              public class TreeExample2 extends JPanel {
                public static Color selectedColor = new Color(115,164,209);
                public static Color selectedBorderColor = new Color(57,105,138);
                private final JTree tree = new JTree() {
                  @Override public void paintComponent(Graphics g) {
                    g.setColor(getBackground());
                    g.fillRect(0,0,getWidth(),getHeight());
                    if (tree.getSelectionCount()>0) {
                      for(int i: getSelectionRows()) {
                        Rectangle r = getRowBounds(i);
                        g.setColor(selectedColor);
                        g.fillRect(0, r.y, getWidth(), r.height);
                      }
                    }
                    super.paintComponent(g);
                    if(getSelectionPath()!=null) {
                      Rectangle r = tree.getRowBounds(getRowForPath(getSelectionPath()));
                      g.setColor(selectedBorderColor);
                      g.drawRect(0, r.y, getWidth()-1, r.height-1);
                    }
                  }
                };
                public JComponent makeUI() {
                  tree.setUI(new javax.swing.plaf.metal.MetalTreeUI() {
                    @Override public Rectangle getPathBounds(JTree tree, TreePath path) {
                      if(tree != null && treeState != null) {
                        return getPathBounds(path, tree.getInsets(), new Rectangle());
                      }
                      return null;
                    }
                    private Rectangle getPathBounds( //copied from BasicTreeUI
                        TreePath path, Insets insets,  Rectangle bounds) {
                      bounds = treeState.getBounds(path, bounds);
                      if (bounds != null) {
                        bounds.width = tree.getWidth();
                        bounds.y += insets.top;
                      }
                      return bounds;
                    }
                  });
                  tree.setOpaque(false);
                  final TreeCellRenderer renderer = tree.getCellRenderer();
                  ((JComponent)renderer).setOpaque(true);
                  tree.setCellRenderer(new TreeCellRenderer() {
                    @Override public Component getTreeCellRendererComponent(
                      JTree tree, Object value, boolean selected, boolean expanded,
                    boolean leaf, int row, boolean hasFocus) {
                      JLabel l = (JLabel)renderer.getTreeCellRendererComponent(
                          tree, value, selected, expanded, leaf, row, hasFocus);
                      l.setBackground(selected?selectedColor:tree.getBackground());
                      return l;
                    }
                  });
                  JPanel p = new JPanel(new BorderLayout());
                  p.add(new JScrollPane(tree));
                  return p;
                }
                public static void main(String[] args) {
                  EventQueue.invokeLater(new Runnable() {
                    @Override public void run() { createAndShowGUI(); }
                  });
                }
                public static void createAndShowGUI() {
                  JFrame f = new JFrame();
                  f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
                  f.getContentPane().add(new TreeExample2().makeUI());
                  f.setSize(320,240);
                  f.setLocationRelativeTo(null);
                  f.setVisible(true);
                }
              }
              • 4. Re: JTree - highlight entire row on selection
                802349
                thank you very much, aterai! your help is very appreciated :) my application has different look and feel, so i can't bind myself to metal look and feel. your solution however is great, i'll see if i can adapt it to be LAF-independent.

                in the meantime i also came up with a solution as well. basically i add a mouselistener and use the tree's getClosestRowForLocation method:
                import java.awt.BorderLayout;
                import java.awt.Color;
                import java.awt.Container;
                import java.awt.Graphics;
                import java.awt.Rectangle;
                import java.awt.event.MouseAdapter;
                import java.awt.event.MouseEvent;
                import java.awt.event.MouseListener;
                
                import javax.swing.JFrame;
                import javax.swing.JScrollPane;
                import javax.swing.JTree;
                import javax.swing.event.TreeSelectionEvent;
                import javax.swing.event.TreeSelectionListener;
                import javax.swing.tree.DefaultTreeCellRenderer;
                import javax.swing.tree.TreePath;
                
                public class TreeExample2 {
                
                     public static Color selectedColor = new Color(115,164,209);
                     public static Color selectedBorderColor = new Color(57,105,138);
                     
                     public static void main(String args[]) {
                          
                          JFrame f = new JFrame("JTree Sample");
                          f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                          Container content = f.getContentPane();
                
                          
                          // custom tree handling
                          final JTree tree = new MyTree();
                          MyTreeCellRenderer renderer = new MyTreeCellRenderer();
                          renderer.setBorderSelectionColor(null); // remove selection border
                          renderer.setBackgroundSelectionColor( null); // remove selection background since we paint the selected row ourselves
                          tree.setCellRenderer( renderer);
                
                       // send repaint event when node selection changes; otherwise sometimes the border remained visible although the selected node changed 
                          TreeSelectionListener treeSelListener = new TreeSelectionListener() {
                               public void valueChanged(TreeSelectionEvent evt) {
                                    tree.treeDidChange();
                               }
                          };
                          tree.addTreeSelectionListener(treeSelListener);
                          
                          // handle mouse clicks outside of the node's label
                          // mouse begin ----------------------------------------------------------------
                          MouseListener ml = new MouseAdapter() {
                               public void mousePressed(MouseEvent e) {
                                    int selRow = tree.getClosestRowForLocation( e.getX(), e.getY());
                                    if( selRow != -1) {
                                         Rectangle bounds = tree.getRowBounds( selRow);
                                         boolean outside = e.getX() < bounds.x || e.getX() > bounds.x + bounds.width || e.getY() < bounds.y || e.getY() >= bounds.y + bounds.height;
                                         if( outside) {
                                              
                                              tree.setSelectionRow(selRow);
                                              System.out.println( "manual selection: " + selRow);
                                              
                                              // handle doubleclick
                                              if( e.getClickCount() == 2) {
                                                   if( tree.isCollapsed(selRow))
                                                        tree.expandRow( selRow);
                                                   else if( tree.isExpanded( selRow))
                                                        tree.collapseRow( selRow);
                                              }
                                              
                                         } else {
                                              System.out.println( "auto selection: " + selRow);
                                         }
                                    }
                               }
                          };
                          tree.addMouseListener(ml);          
                          // mouse end ----------------------------------------------------------------
                          
                          // add tree to frame and show frame
                          JScrollPane scrollPane = new JScrollPane(tree);
                          content.add(scrollPane, BorderLayout.CENTER);
                          f.setSize(400, 600);
                          f.setVisible(true);
                     }
                
                     public static class MyTree extends JTree {
                
                          protected void paintComponent(Graphics g) {
                
                               // paint background
                               g.setColor(getBackground());
                               g.fillRect(0, 0, getWidth(), getHeight());
                
                               // paint selected node's background and border
                               int fromRow = getRowForPath( getSelectionPath());
                               if (fromRow != -1) {
                                    int toRow = fromRow + 1;
                                    Rectangle fromBounds = getRowBounds(fromRow);
                                    Rectangle toBounds = getRowBounds(toRow - 1);
                                    if (fromBounds != null && toBounds != null) {
                                         g.setColor(selectedColor);
                                         g.fillRect(0, fromBounds.y, getWidth(), toBounds.y - fromBounds.y + toBounds.height);
                                         g.setColor(selectedBorderColor);
                                         g.drawRect(0, fromBounds.y, getWidth() - 1, toBounds.y - fromBounds.y + toBounds.height);
                                    }
                               }
                
                               // perform operation of superclass
                               setOpaque(false); // trick not to paint background
                               super.paintComponent(g);
                               setOpaque(false);
                          }
                     }
                
                     // trick to make renderer transparent for the unselected nodes 
                     public static class MyTreeCellRenderer extends DefaultTreeCellRenderer {
                          public MyTreeCellRenderer() {
                               setBackgroundNonSelectionColor(null);
                          }
                
                          public Color getBackground() {
                               return null;
                          }
                     }
                
                }