Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * @(#)JTreeTable.java  1.2 98/10/27
   *
   * Copyright 1997, 1998 Sun Microsystems, Inc.  All Rights Reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
  *   - Redistributions of source code must retain the above copyright
  *     notice, this list of conditions and the following disclaimer.
  *
  *   - Redistributions in binary form must reproduce the above copyright
  *     notice, this list of conditions and the following disclaimer in the
  *     documentation and/or other materials provided with the distribution.
  *
  *   - Neither the name of Sun Microsystems nor the names of its
  *     contributors may be used to endorse or promote products derived
  *     from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 package org.mozilla.javascript.tools.debugger.treetable;
 
 import javax.swing.*;
 
 
 
This example shows how to create a simple JTreeTable component, by using a JTree as a renderer (and editor) for the cells in a particular column in the JTable.

Author(s):
Philip Milne
Scott Violet
Version:
1.2 10/27/98
 
 public class JTreeTable extends JTable {
    
 
     private static final long serialVersionUID = -2103973006456695515L;
    
A subclass of JTree.
 
     protected TreeTableCellRenderer tree;
 
     public JTreeTable(TreeTableModel treeTableModel) {
         super();
 
         // Create the tree. It will be used as a renderer and editor.
          = new TreeTableCellRenderer(treeTableModel);
 
         // Install a tableModel representing the visible rows in the tree.
         super.setModel(new TreeTableModelAdapter(treeTableModel, tree));
 
         // Force the JTable and JTree to share their row selection models.
         ListToTreeSelectionModelWrapper selectionWrapper = new
                                 ListToTreeSelectionModelWrapper();
         .setSelectionModel(selectionWrapper);
         setSelectionModel(selectionWrapper.getListSelectionModel());
 
         // Install the tree editor renderer and editor.
         setDefaultRenderer(TreeTableModel.class);
         setDefaultEditor(TreeTableModel.classnew TreeTableCellEditor());
 
         // No grid.
         setShowGrid(false);
 
         // No intercell spacing
         setIntercellSpacing(new Dimension(0, 0));
 
         // And update the height of the trees row to match that of
         // the table.
         if (.getRowHeight() < 1) {
             // Metal looks better like this.
             setRowHeight(18);
         }
     }

    
Overridden to message super and forward the method to the tree. Since the tree is not actually in the component hierarchy it will never receive this unless we forward it in this manner.
    @Override
    public void updateUI() {
        super.updateUI();
        if( != null) {
            .updateUI();
        }
        // Use the tree's default foreground and background colors in the
        // table.
        LookAndFeel.installColorsAndFont(this"Tree.background",
                                         "Tree.foreground""Tree.font");
    }
    /* Workaround for BasicTableUI anomaly. Make sure the UI never tries to
     * paint the editor. The UI currently uses different techniques to
     * paint the renderers and editors and overriding setBounds() below
     * is not the right thing to do for an editor. Returning -1 for the
     * editing row in this case, ensures the editor is never painted.
     */
    @Override
    public int getEditingRow() {
        return (getColumnClass() == TreeTableModel.class) ? -1 :
                ;
    }

    
Overridden to pass the new rowHeight to the tree.
    @Override
    public void setRowHeight(int rowHeight) {
        super.setRowHeight(rowHeight);
        if ( != null && .getRowHeight() != rowHeight) {
            .setRowHeight(getRowHeight());
        }
    }

    
Returns the tree that is being shared between the model.
    public JTree getTree() {
        return ;
    }

    
A TreeCellRenderer that displays a JTree.
    public class TreeTableCellRenderer extends JTree implements TableCellRenderer {
        private static final long serialVersionUID = -193867880014600717L;
        
Last table/tree row asked to renderer.
        protected int visibleRow;
        public TreeTableCellRenderer(TreeModel model) {
            super(model);
        }

        
updateUI is overridden to set the colors of the Tree's renderer to match that of the table.
        @Override
        public void updateUI() {
            super.updateUI();
            // Make the tree's cell renderer use the table's cell selection
            // colors.
            TreeCellRenderer tcr = getCellRenderer();
            if (tcr instanceof DefaultTreeCellRenderer) {
                DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer)tcr);
                // For 1.1 uncomment this, 1.2 has a bug that will cause an
                // exception to be thrown if the border selection color is
                // null.
                // dtcr.setBorderSelectionColor(null);
                dtcr.setTextSelectionColor(UIManager.getColor
                                           ("Table.selectionForeground"));
                dtcr.setBackgroundSelectionColor(UIManager.getColor
                                                ("Table.selectionBackground"));
            }
        }

        
Sets the row height of the tree, and forwards the row height to the table.
        @Override
        public void setRowHeight(int rowHeight) {
            if (rowHeight > 0) {
                super.setRowHeight(rowHeight);
                if (JTreeTable.this != null &&
                    JTreeTable.this.getRowHeight() != rowHeight) {
                    JTreeTable.this.setRowHeight(getRowHeight());
                }
            }
        }

        
This is overridden to set the height to match that of the JTable.
        @Override
        public void setBounds(int xint yint wint h) {
            super.setBounds(x, 0, w, JTreeTable.this.getHeight());
        }

        
Sublcassed to translate the graphics such that the last visible row will be drawn at 0,0.
        @Override
        public void paint(Graphics g) {
            g.translate(0, - * getRowHeight());
            super.paint(g);
        }

        
TreeCellRenderer method. Overridden to update the visible row.
        public Component getTableCellRendererComponent(JTable table,
                                                       Object value,
                                                       boolean isSelected,
                                                       boolean hasFocus,
                                                       int rowint column) {
            if(isSelected)
                setBackground(table.getSelectionBackground());
            else
                setBackground(table.getBackground());
             = row;
            return this;
        }
    }


    
TreeTableCellEditor implementation. Component returned is the JTree.
    public class TreeTableCellEditor extends AbstractCellEditor implements
                 TableCellEditor {
        public Component getTableCellEditorComponent(JTable table,
                                                     Object value,
                                                     boolean isSelected,
                                                     int rint c) {
            return ;
        }

        
Overridden to return false, and if the event is a mouse event it is forwarded to the tree.

The behavior for this is debatable, and should really be offered as a property. By returning false, all keyboard actions are implemented in terms of the table. By returning true, the tree would get a chance to do something with the keyboard events. For the most part this is ok. But for certain keys, such as left/right, the tree will expand/collapse where as the table focus should really move to a different column. Page up/down should also be implemented in terms of the table. By returning false this also has the added benefit that clicking outside of the bounds of the tree node, but still in the tree column will select the row, whereas if this returned true that wouldn't be the case.

By returning false we are also enforcing the policy that the tree will never be editable (at least by a key sequence).

        @Override
        public boolean isCellEditable(EventObject e) {
            if (e instanceof MouseEvent) {
                for (int counter = getColumnCount() - 1; counter >= 0;
                     counter--) {
                    if (getColumnClass(counter) == TreeTableModel.class) {
                        MouseEvent me = (MouseEvent)e;
                        MouseEvent newME = new MouseEvent(me.getID(),
                                   me.getWhen(), me.getModifiers(),
                                   me.getX() - getCellRect(0, countertrue).,
                                   me.getY(), me.getClickCount(),
                                   me.isPopupTrigger());
                        .dispatchEvent(newME);
                        break;
                    }
                }
            }
            return false;
        }
    }


    
ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel to listen for changes in the ListSelectionModel it maintains. Once a change in the ListSelectionModel happens, the paths are updated in the DefaultTreeSelectionModel.
    public class ListToTreeSelectionModelWrapper
        extends DefaultTreeSelectionModel
    {
        private static final long serialVersionUID = 8168140829623071131L;

        
Set to true when we are updating the ListSelectionModel.
        protected boolean         updatingListSelectionModel;
        public ListToTreeSelectionModelWrapper() {
            super();
                                    (createListSelectionListener());
        }

        
Returns the list selection model. ListToTreeSelectionModelWrapper listens for changes to this model and updates the selected paths accordingly.
        public ListSelectionModel getListSelectionModel() {
            return ;
        }

        
This is overridden to set updatingListSelectionModel and message super. This is the only place DefaultTreeSelectionModel alters the ListSelectionModel.
        @Override
        public void resetRowSelection() {
            if(!) {
                 = true;
                try {
                    super.resetRowSelection();
                }
                finally {
                     = false;
                }
            }
            // Notice how we don't message super if
            // updatingListSelectionModel is true. If
            // updatingListSelectionModel is true, it implies the
            // ListSelectionModel has already been updated and the
            // paths are the only thing that needs to be updated.
        }

        
Creates and returns an instance of ListSelectionHandler.
        protected ListSelectionListener createListSelectionListener() {
            return new ListSelectionHandler();
        }

        
If updatingListSelectionModel is false, this will reset the selected paths from the selected rows in the list selection model.
        protected void updateSelectedPathsFromSelectedRows() {
            if(!) {
                 = true;
                try {
                    // This is way expensive, ListSelectionModel needs an
                    // enumerator for iterating.
                    int        min = .getMinSelectionIndex();
                    int        max = .getMaxSelectionIndex();
                    clearSelection();
                    if(min != -1 && max != -1) {
                        for(int counter = mincounter <= maxcounter++) {
                            if(.isSelectedIndex(counter)) {
                                TreePath     selPath = .getPathForRow
                                                            (counter);
                                if(selPath != null) {
                                    addSelectionPath(selPath);
                                }
                            }
                        }
                    }
                }
                finally {
                     = false;
                }
            }
        }

        
Class responsible for calling updateSelectedPathsFromSelectedRows when the selection of the list changse.
        class ListSelectionHandler implements ListSelectionListener {
            public void valueChanged(ListSelectionEvent e) {
                updateSelectedPathsFromSelectedRows();
            }
        }
    }