alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.explorer.view;

import java.awt.*;
import java.awt.event.*;
import java.util.HashSet;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.plaf.metal.MetalScrollBarUI;
import javax.swing.border.Border;
import javax.swing.event.*;
import javax.swing.tree.*;
import javax.accessibility.AccessibleContext;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Comparator;
import java.util.Collections;
import java.util.Enumeration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;

import org.openide.ErrorManager;
import org.openide.nodes.Node;
import org.openide.nodes.Node.Property;
import org.openide.util.NbBundle;
import org.openide.awt.MouseUtils;

/** Explorer view. Allows to view tree of nodes on the left
 * and its properties in table on the right.
 *
 * @author  jrojcek
 * @since 1.7
 * @see Customizing the TreeTableView (Explorer API)
 */
public class TreeTableView extends BeanTreeView {
   
    /** The table */
    protected JTable treeTable;
    private NodeTableModel tableModel;

    // Tree scroll support
    private JScrollBar hScrollBar;
    private JScrollPane scrollPane;
    private ScrollListener listener;

    // hiding columns allowed
    private boolean allowHideColumns = false;
    // sorting by column allowed
    private boolean allowSortingByColumn = false;
    // hide horizontal scrollbar
    private boolean hideHScrollBar = false;
    // button in corner of scroll pane
    private JButton colsButton = null;
    // tree model with sorting support
    private SortedNodeTreeModel sortedNodeTreeModel;
    /** Listener on keystroke to invoke default action */
    private ActionListener defaultTreeActionListener;
    // default treetable header renderer
    private TableCellRenderer defaultHeaderRenderer = null;
    
    private MouseUtils.PopupMouseAdapter tableMouseListener;
    
    /** Accessible context of this class (implemented by inner class AccessibleTreeTableView). */
    private AccessibleContext accessContext;

    // icon of column button
    private static final String COLUMNS_ICON = "/org/openide/resources/columns.gif"; // NOI18N
    // icons of ascending/descending order in column header
    private static final String SORT_ASC_ICON = "org/openide/resources/columnsSortedAsc.gif"; // NOI18N
    private static final String SORT_DESC_ICON = "org/openide/resources/columnsSortedDesc.gif"; // NOI18N
    
    private TreeColumnProperty treeColumnProperty = new TreeColumnProperty();
    private int treeColumnWidth;

    /** Create TreeTableView with default NodeTableModel
     */
    public TreeTableView() {
        this(new NodeTableModel());
    }

    /** Creates TreeTableView with provided NodeTableModel.
     * @param ntm node table model
     */
    public TreeTableView(NodeTableModel ntm) {
        tableModel = ntm;
        
        initializeTreeTable();
        setPopupAllowed(true);
        setDefaultActionAllowed(true);
        
        initializeTreeScrollSupport();
        
        // add scrollbar and scrollpane into a panel
        JPanel p = new CompoundScrollPane();
        p.setLayout(new BorderLayout());
        scrollPane.setViewportView(treeTable);
        p.add(BorderLayout.CENTER, scrollPane);

        ImageIcon ic = new ImageIcon(TreeTable.class.getResource ( COLUMNS_ICON )); // NOI18N
        colsButton = new javax.swing.JButton( ic );
        colsButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
               selectVisibleColumns();
            }
        });

        JPanel sbp = new JPanel();
        sbp.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        sbp.add(hScrollBar);
        p.add(BorderLayout.SOUTH, sbp);
        
        super.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        super.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
        setViewportView(p);
        setBorder (BorderFactory.createEmptyBorder()); //NOI18N
        setViewportBorder (BorderFactory.createEmptyBorder()); //NOI18N
    }
    
    public void setRowHeader(JViewport rowHeader) {
        rowHeader.setBorder(BorderFactory.createEmptyBorder());
        super.setRowHeader(rowHeader);
    }    
    
    /* Overriden to allow hide special horizontal scrollbar
     */
    public void setHorizontalScrollBarPolicy(int policy) {
        hideHScrollBar = ( policy == JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
        if ( hideHScrollBar ) {
            hScrollBar.setVisible( false );
            ((TreeTable)treeTable).setTreeHScrollingEnabled( false );
        }
    }
    
    /* Overriden to delegate policy of vertical scrollbar to inner scrollPane
     */
    public void setVerticalScrollBarPolicy(int policy) {
        if ( scrollPane == null )
            return;
        
        allowHideColumns = ( policy == JScrollPane.VERTICAL_SCROLLBAR_ALWAYS );
        if ( allowHideColumns )
            scrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, colsButton);
        treeTable.getTableHeader().setReorderingAllowed( allowHideColumns );

        scrollPane.setVerticalScrollBarPolicy( policy );
    }
    
    protected NodeTreeModel createModel() {
        return getSortedNodeTreeModel ();
    }
    
    /** Requests focus for the tree component. Overrides superclass method. */
    public void requestFocus () {
        if (treeTable != null) {
            treeTable.requestFocus();
        }
    }
    
    /* Sets sorting ability
     */
    private void setAllowSortingByColumn( boolean allow ) {
        if ( allow && (allow != allowSortingByColumn )) {
            addMouseListener( new MouseAdapter() {
                public void mouseClicked (MouseEvent evt) {
                    Component c = evt.getComponent ();
                    if (c instanceof JTableHeader) {
                        JTableHeader h = (JTableHeader)c;
                        int index = h.columnAtPoint (evt.getPoint ());
                        //issue 38442, column can be -1 if this is the
                        //upper right corner - there's no column there,
                        //so make sure it's an index >=0.
                        if (index >= 0) {
                            clickOnColumnAction( index - 1 );
                        }
                    }
                }
            });
        }
        allowSortingByColumn = allow;
    }
    
    /* Change sorting after clicking on comparable column header.
     * Cycle through ascending -> descending -> no sort -> (start over)
     */
    private void clickOnColumnAction(int index ) {
        if ( index == -1 ) {
            if ( treeColumnProperty.isComparable() )
                if ( treeColumnProperty.isSortingColumn() ) {
                    if (!treeColumnProperty.isSortOrderDescending())
                        setSortingOrder(false);
                    else {
                        noSorting();
                    }
                }
                else {
                    setSortingColumn( index );
                    setSortingOrder( true );
                }
        } else if ( tableModel.isComparableColumn( index ) ) {
            if ( tableModel.isSortingColumn( index ) ) {
                if (!tableModel.isSortOrderDescending())
                    setSortingOrder(false);
                else {
                    noSorting();
                }
            }
            else {
                setSortingColumn( index );
                setSortingOrder( true );
            }
        }
    }
    
    private void selectVisibleColumns() {
        setCurrentWidths();
        String viewName = null;
        if ( getParent() != null )
            viewName = getParent().getName();
        if ( tableModel.selectVisibleColumns( viewName, treeTable.getColumnName(0),
                                              getSortedNodeTreeModel ().getRootDescription() ) ) {
            if ( tableModel.getVisibleSortingColumn() == -1 )
                getSortedNodeTreeModel ().setSortedByProperty( null );
            setTreePreferredWidth( treeColumnWidth );
            for (int i=0; i < tableModel.getColumnCount(); i++) {
                setTableColumnPreferredWidth( tableModel.getArrayIndex( i ), tableModel.getVisibleColumnWidth( i ) );
            }
        }
    }
    
    private void setCurrentWidths() {
        treeColumnWidth = treeTable.getColumnModel().getColumn( 0 ).getWidth();
        for (int i=0; i < tableModel.getColumnCount(); i++) {
            int w = treeTable.getColumnModel().getColumn(i + 1).getWidth();
            tableModel.setVisibleColumnWidth( i, w );
        }
    }
    
    /** Do not initialize tree now. We will do it from our constructor.
     * [dafe] Used probably because this method is called *before* superclass
     * is fully created (constructor finished) which is horrible but I don't
     * have enough knowledge about this code to change it.
     */
    void initializeTree () {
    }

    /** Initialize tree and treeTable.
     */
    private void initializeTreeTable() {
        treeModel = createModel();
        treeTable = new TreeTable(treeModel, tableModel);
        tree = ((TreeTable)treeTable).getTree();

        defaultHeaderRenderer = treeTable.getTableHeader().getDefaultRenderer();
        treeTable.getTableHeader().setDefaultRenderer( new SortingHeaderRenderer() );
        
        // init listener & attach it to closing of
        managerListener = new TreePropertyListener();
        tree.addTreeExpansionListener (managerListener);
        
        // add listener to sort a new expanded folders
        tree.addTreeExpansionListener (new TreeExpansionListener () {
            public void treeExpanded (TreeExpansionEvent event) {
                TreePath path = event.getPath ();
                if (path != null) {
                    // bugfix $32480, store and recover currently expanded subnodes
                    // store expanded paths
                    Enumeration en = TreeTableView.this.tree.getExpandedDescendants (path);
                    // sort children
                    getSortedNodeTreeModel ().sortChildren ((VisualizerNode)path.getLastPathComponent (), true);
                    // expand again folders
                    while (en.hasMoreElements ()) {
                        TreeTableView.this.tree.expandPath ((TreePath)en.nextElement ());
                    }
                }
            }

            public void treeCollapsed (TreeExpansionEvent event) {
                // ignore it
            }
        });
        
	defaultActionListener = new PopupSupport();
        treeTable.getActionMap().put ("org.openide.actions.PopupAction", defaultActionListener.popup);
        tree.addMouseListener(defaultActionListener);
        
        tableMouseListener = new MouseUtils.PopupMouseAdapter() {
                public void showPopup(MouseEvent mevt) {
                    if (isPopupAllowed()) {
                        if ( mevt.getY() > treeTable.getHeight() )
                            // clear selection, if click under the table
                            treeTable.clearSelection();
                        createPopup(mevt);
                    }
                }
            };
        treeTable.addMouseListener( tableMouseListener );
        if (UIManager.getColor ("control") != null) { // NOI18N
            treeTable.setGridColor (UIManager.getColor ("control")); // NOI18N
        }
    }
    
    public void setSelectionMode (int mode) {
        super.setSelectionMode(mode);
        if (mode == TreeSelectionModel.SINGLE_TREE_SELECTION) {
            treeTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        } else if (mode == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION) {
            treeTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
        } else if (mode == TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION) {
            treeTable.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        }
    }
    
    /** Overrides JScrollPane's getAccessibleContext() method to use internal accessible context.
     */
    public AccessibleContext getAccessibleContext()
    {
        if (accessContext == null)
            accessContext = new AccessibleTreeTableView();
        return accessContext;
    }
    
    /** This is internal accessible context for TreeTableView.
     * It delegates setAccessibleName and setAccessibleDescription methods to set these properties
     * in underlying TreeTable as well.
     */
    private class AccessibleTreeTableView extends AccessibleJScrollPane
    {
        
        AccessibleTreeTableView()
        {
        }
        
        public void setAccessibleName(String accessibleName)
        {
            super.setAccessibleName(accessibleName);
            if (treeTable != null)
                treeTable.getAccessibleContext().setAccessibleName(accessibleName);
        }
        
        public void setAccessibleDescription(String accessibleDescription)
        {
            super.setAccessibleDescription(accessibleDescription);
            if (treeTable != null)
                treeTable.getAccessibleContext().setAccessibleDescription(accessibleDescription);
        }
        
    }
    
    /** Initialize full support for horizontal scrolling.
     */
    private void initializeTreeScrollSupport() {
        scrollPane = new JScrollPane();
        scrollPane.setName("TreeTableView.scrollpane"); //NOI18N
        scrollPane.setBorder(BorderFactory.createEmptyBorder());
        scrollPane.setViewportBorder(BorderFactory.createEmptyBorder());
        
        if (UIManager.getColor("Table.background") != null) { // NOI18N
            scrollPane.getViewport().setBackground(UIManager.getColor("Table.background")); // NOI18N
        }

        hScrollBar = new JScrollBar(JScrollBar.HORIZONTAL);
	hScrollBar.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, Boolean.FALSE);
        hScrollBar.setVisible(false);
        
        listener = new ScrollListener();
        
        treeTable.addPropertyChangeListener(listener);
        scrollPane.getViewport().addComponentListener(listener);
        tree.addPropertyChangeListener(listener);
        hScrollBar.getModel().addChangeListener(listener);
    }

    /* Overriden to work well with treeTable.
     */
    public void setPopupAllowed (boolean value) {
        if (tree == null) {
            return;
        }
        
        if (popupListener == null && value) {
            // on
            popupListener = new PopupAdapter () {
                    protected void showPopup (MouseEvent e) {
                        int selRow = tree.getRowForLocation(e.getX(), e.getY());

                        if (!tree.isRowSelected(selRow)) {
                            tree.setSelectionRow(selRow);
                        }
                    }
            };

            tree.addMouseListener (popupListener);
            return;
        }
        if (popupListener != null && !value) {
            // off
            tree.removeMouseListener (popupListener);
            popupListener = null;
            return;
        }
    }
    
    /* Overriden to work well with treeTable.
     */
    public void setDefaultActionAllowed(boolean value) {
        if (tree == null)
            return;

        defaultActionEnabled = value;

        if(value) {
            defaultTreeActionListener = new DefaultTreeAction();
            treeTable.registerKeyboardAction(
                defaultTreeActionListener,
                KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false),
                JComponent.WHEN_FOCUSED
            );
        } else {
            // Switch off.
            defaultTreeActionListener = null;
            treeTable.unregisterKeyboardAction(
                KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false)
            );
        }
    }

    /** Set columns.
     * @param props each column is constructed from Node.Property
     */
    public void setProperties(Property[] props) {        
        tableModel.setProperties(props);
        treeColumnProperty.setProperty( tableModel.propertyForColumn( -1 ) );
        if ( treeColumnProperty.isComparable() || tableModel.existsComparableColumn() ) {
            setAllowSortingByColumn( true );
            if ( treeColumnProperty.isSortingColumn() ) {
                getSortedNodeTreeModel ().setSortedByName( true,
                                        !treeColumnProperty.isSortOrderDescending() );
            }
            else {
                int index = tableModel.getVisibleSortingColumn();
                if ( index != -1 ) {
                    getSortedNodeTreeModel ().setSortedByProperty( tableModel.propertyForColumn( index ), 
                                            !tableModel.isSortOrderDescending() );
                }
            }
        }
    }

    /** Sets resize mode of table.
     * 
     * @param mode - One of 5 legal values: 
JTable.AUTO_RESIZE_OFF,
     *                                           JTable.AUTO_RESIZE_NEXT_COLUMN,
     *                                           JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS, 
     *                                           JTable.AUTO_RESIZE_LAST_COLUMN, 
     *                                           JTable.AUTO_RESIZE_ALL_COLUMNS
*/ public final void setTableAutoResizeMode(int mode) { treeTable.setAutoResizeMode(mode); } /** Gets resize mode of table. * * @return mode - One of 5 legal values:
JTable.AUTO_RESIZE_OFF,
     *                                           JTable.AUTO_RESIZE_NEXT_COLUMN,
     *                                           JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS, 
     *                                           JTable.AUTO_RESIZE_LAST_COLUMN, 
     *                                           JTable.AUTO_RESIZE_ALL_COLUMNS
*/ public final int getTableAutoResizeMode() { return treeTable.getAutoResizeMode(); } /** Sets preferred width of table column * @param index column index * @param width preferred column width */ public final void setTableColumnPreferredWidth(int index, int width) { if (index == -1) { //Issue 47969 - sometimes this is called with a -1 arg return; } tableModel.setArrayColumnWidth( index, width ); int j = tableModel.getVisibleIndex( index ); if ( j != -1 ) treeTable.getColumnModel().getColumn(j + 1).setPreferredWidth( width ); } /** Gets preferred width of table column * @param index column index * @return preferred column width */ public final int getTableColumnPreferredWidth(int index) { int j = tableModel.getVisibleIndex( index ); if ( j != -1 ) { return treeTable.getColumnModel().getColumn(j + 1).getPreferredWidth(); } else { return tableModel.getArrayColumnWidth( index ); } } /** Set preferred size of tree view * @param width preferred width of tree view */ public final void setTreePreferredWidth(int width) { treeTable.getColumnModel().getColumn(((TreeTable)treeTable).getTreeColumnIndex()).setPreferredWidth(width); } /** Get preferred size of tree view * @return preferred width of tree view */ public final int getTreePreferredWidth() { return treeTable.getColumnModel().getColumn(((TreeTable)treeTable).getTreeColumnIndex()).getPreferredWidth(); } public void addNotify() { // to allow displaying popup also in blank area if ( treeTable.getParent() != null ) { treeTableParent = treeTable.getParent(); treeTableParent.addMouseListener( tableMouseListener ); } super.addNotify(); listener.revalidateScrollBar(); } private Component treeTableParent = null; public void removeNotify() { super.removeNotify(); if (treeTableParent != null) { //IndexedEditorPanel treeTableParent.removeMouseListener ( tableMouseListener ); } treeTableParent = null; // clear node listeners tableModel.setNodes(new Node[] {}); } public void addMouseListener(MouseListener l) { super.addMouseListener(l); treeTable.getTableHeader().addMouseListener(l); } public void removeMouseListener(MouseListener l) { super.removeMouseListener(l); treeTable.getTableHeader().removeMouseListener(l); } /* DnD is not implemented for treeTable. */ public void setDragSource (boolean state) { } /* DnD is not implemented for treeTable. */ public void setDropTarget (boolean state) { } /* Overriden to get position for popup invoked by keyboard */ Point getPositionForPopup() { int row = treeTable.getSelectedRow(); if ( row < 0 ) return null; int col = treeTable.getSelectedColumn(); if ( col < 0 ) col = 0; Rectangle r = null; if ( col == 0 ) r = tree.getRowBounds( row ); else r = treeTable.getCellRect( row, col, true ); Point p = SwingUtilities.convertPoint( treeTable, r.x, r.y, this); return p; } private void createPopup(MouseEvent e) { int xpos=e.getX(); int ypos=e.getY(); Point p = SwingUtilities.convertPoint(e.getComponent(),xpos, ypos,TreeTableView.this); int mxpos=(int)p.getX(); int mypos=(int)p.getY(); xpos -= ((TreeTable)treeTable).getPositionX(); if ( allowHideColumns || allowSortingByColumn ) { int col = treeTable.getColumnModel().getColumnIndexAtX( xpos ); super.createExtendedPopup( mxpos, mypos, getListMenu( col ) ); } else super.createPopup(mxpos, mypos); e.consume(); } /* creates List Options menu */ private JMenu getListMenu(final int col) { JMenu listItem = new JMenu( NbBundle.getBundle(NodeTableModel.class).getString("LBL_ListOptions") ); if ( allowHideColumns && col > 0 ) { JMenu colsItem = new JMenu( NbBundle.getBundle(NodeTableModel.class).getString("LBL_ColsMenu") ); boolean addColsItem = false; if ( col > 1 ) { JMenuItem moveLItem = new JMenuItem( NbBundle.getBundle(NodeTableModel.class).getString("LBL_MoveLeft") ); moveLItem.addActionListener( new ActionListener() { public void actionPerformed(java.awt.event.ActionEvent actionEvent) { treeTable.getColumnModel().moveColumn( col, col - 1 ); } }); colsItem.add( moveLItem ); addColsItem = true; } if ( col < tableModel.getColumnCount() ) { JMenuItem moveRItem = new JMenuItem( NbBundle.getBundle(NodeTableModel.class).getString("LBL_MoveRight") ); moveRItem.addActionListener( new ActionListener() { public void actionPerformed(java.awt.event.ActionEvent actionEvent) { treeTable.getColumnModel().moveColumn( col, col + 1 ); } }); colsItem.add( moveRItem ); addColsItem = true; } if ( addColsItem ) listItem.add( colsItem ); } if ( allowSortingByColumn ) { JMenu sortItem = new JMenu( NbBundle.getBundle(NodeTableModel.class).getString("LBL_SortMenu") ); JRadioButtonMenuItem noSortItem = new JRadioButtonMenuItem( NbBundle.getBundle(NodeTableModel.class).getString("LBL_NoSort"), !getSortedNodeTreeModel ().isSortingActive() ); noSortItem.addActionListener( new ActionListener() { public void actionPerformed(java.awt.event.ActionEvent actionEvent) { noSorting(); } }); sortItem.add( noSortItem ); int visibleComparable = 0; JRadioButtonMenuItem colItem; if ( treeColumnProperty.isComparable() ) { visibleComparable++; colItem = new JRadioButtonMenuItem( treeTable.getColumnName(0), treeColumnProperty.isSortingColumn() ); colItem.setHorizontalTextPosition( SwingConstants.LEFT ); colItem.addActionListener( new ActionListener() { public void actionPerformed(java.awt.event.ActionEvent actionEvent) { setSortingColumn( -1 ); } }); sortItem.add( colItem ); } for (int i=0; i < tableModel.getColumnCount(); i++) { if ( tableModel.isComparableColumn( i ) ) { visibleComparable++; colItem = new JRadioButtonMenuItem( tableModel.getColumnName( i ), tableModel.isSortingColumn( i ) ); colItem.setHorizontalTextPosition( SwingConstants.LEFT ); final int index = i; colItem.addActionListener( new ActionListener() { public void actionPerformed(java.awt.event.ActionEvent actionEvent) { setSortingColumn( index ); } }); sortItem.add( colItem ); } } if ( visibleComparable > 0 ) { sortItem.addSeparator(); boolean current_sort; if ( treeColumnProperty.isSortingColumn() ) current_sort = treeColumnProperty.isSortOrderDescending(); else current_sort = tableModel.isSortOrderDescending(); JRadioButtonMenuItem ascItem = new JRadioButtonMenuItem( NbBundle.getBundle(NodeTableModel.class).getString("LBL_Ascending"), !current_sort ); ascItem.setHorizontalTextPosition( SwingConstants.LEFT ); ascItem.addActionListener( new ActionListener() { public void actionPerformed(java.awt.event.ActionEvent actionEvent) { setSortingOrder( true ); } }); sortItem.add( ascItem ); JRadioButtonMenuItem descItem = new JRadioButtonMenuItem( NbBundle.getBundle(NodeTableModel.class).getString("LBL_Descending"), current_sort ); descItem.setHorizontalTextPosition( SwingConstants.LEFT ); descItem.addActionListener( new ActionListener() { public void actionPerformed(java.awt.event.ActionEvent actionEvent) { setSortingOrder( false ); } }); sortItem.add( descItem ); if ( ! getSortedNodeTreeModel ().isSortingActive() ) { ascItem.setEnabled( false ); descItem.setEnabled( false ); } listItem.add( sortItem ); } } if ( allowHideColumns ) { JMenuItem visItem = new JMenuItem( NbBundle.getBundle(NodeTableModel.class).getString("LBL_ChangeColumns") ); visItem.addActionListener( new ActionListener() { public void actionPerformed(java.awt.event.ActionEvent actionEvent) { selectVisibleColumns(); } }); listItem.add( visItem ); } return listItem; } /* Sets column to be currently used for sorting */ private void setSortingColumn(int index) { tableModel.setSortingColumn( index ); if ( index != -1 ) { getSortedNodeTreeModel ().setSortedByProperty( tableModel.propertyForColumn( index ), !tableModel.isSortOrderDescending()); treeColumnProperty.setSortingColumn( false ); } else { getSortedNodeTreeModel ().setSortedByName( true, !treeColumnProperty.isSortOrderDescending() ); treeColumnProperty.setSortingColumn( true ); } // to change sort icon treeTable.getTableHeader().repaint(); } private void noSorting() { tableModel.setSortingColumn( -1 ); getSortedNodeTreeModel ().setNoSorting(); treeColumnProperty.setSortingColumn( false ); // to change sort icon treeTable.getTableHeader().repaint(); } /* Sets sorting order for current sorting. */ private void setSortingOrder(boolean ascending) { if ( treeColumnProperty.isSortingColumn() ) treeColumnProperty.setSortOrderDescending( !ascending ); else tableModel.setSortOrderDescending( !ascending ); getSortedNodeTreeModel ().setSortOrder( ascending ); // to change sort icon treeTable.getTableHeader().repaint(); } /* Horizontal scrolling support. */ private final class ScrollListener extends ComponentAdapter implements PropertyChangeListener, ChangeListener { ScrollListener() {} boolean movecorrection = false; //Column width public void propertyChange(PropertyChangeEvent evt) { if (((TreeTable)treeTable).getTreeColumnIndex() == -1) return; if ("width".equals(evt.getPropertyName())) { // NOI18N if (!treeTable.equals(evt.getSource())) { Dimension dim = hScrollBar.getPreferredSize(); dim.width = treeTable.getColumnModel().getColumn(((TreeTable)treeTable).getTreeColumnIndex()).getWidth(); hScrollBar.setPreferredSize(dim); hScrollBar.revalidate(); hScrollBar.repaint(); } revalidateScrollBar(); } else if ("positionX".equals(evt.getPropertyName())) { // NOI18N revalidateScrollBar(); } else if ("treeColumnIndex".equals(evt.getPropertyName())) { // NOI18N treeTable.getColumnModel().getColumn(((TreeTable)treeTable).getTreeColumnIndex()).addPropertyChangeListener(listener); } else if ("column_moved".equals(evt.getPropertyName())) { // NOI18N int from = ((Integer)evt.getOldValue()).intValue(); int to = ((Integer)evt.getNewValue()).intValue(); if ( from == 0 || to == 0 ) { if ( movecorrection ) movecorrection = false; else { movecorrection = true; // not allowed to move first, tree column treeTable.getColumnModel().moveColumn( to, from ); } return; } // module will be revalidated in NodeTableModel treeTable.getTableHeader().getColumnModel().getColumn( from ).setModelIndex( from ); treeTable.getTableHeader().getColumnModel().getColumn( to ).setModelIndex( to ); tableModel.moveColumn( from - 1, to - 1); } } //Viewport height public void componentResized(ComponentEvent e) { revalidateScrollBar(); } //ScrollBar change public void stateChanged(ChangeEvent evt) { int value = hScrollBar.getModel().getValue(); ((TreeTable)treeTable).setPositionX(value); } private void revalidateScrollBar() { if (!isDisplayable()) { return; } if (treeTable.getColumnModel().getColumnCount() > 0 && ((TreeTable)treeTable).getTreeColumnIndex() >= 0) { int extentWidth = treeTable.getColumnModel().getColumn(((TreeTable)treeTable).getTreeColumnIndex()).getWidth(); int maxWidth = tree.getPreferredSize().width; int extentHeight = scrollPane.getViewport().getSize().height; int maxHeight = tree.getPreferredSize().height; int positionX = ((TreeTable)treeTable).getPositionX(); int value = Math.max(0, Math.min(positionX, maxWidth - extentWidth)); boolean hsbvisible = hScrollBar.isVisible(); boolean vsbvisible = scrollPane.getVerticalScrollBar().isVisible(); int hsbheight = hsbvisible ? hScrollBar.getHeight() : 0; int vsbwidth = scrollPane.getVerticalScrollBar().getWidth(); hScrollBar.setValues(value, extentWidth, 0, maxWidth); if (hideHScrollBar || maxWidth <= extentWidth || (vsbvisible && (maxHeight <= extentHeight + hsbheight && maxWidth <= extentWidth + vsbwidth))) hScrollBar.setVisible(false); else hScrollBar.setVisible(true); } } } /** Scrollable (better say not scrollable) pane. Used as container for * left (controlling) and rigth (controlled) scroll panes. */ private static final class CompoundScrollPane extends JPanel implements Scrollable { CompoundScrollPane() {} public void setBorder (Border b) { //do nothing } public boolean getScrollableTracksViewportWidth() { return true; } public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { return 10; } public boolean getScrollableTracksViewportHeight() { return true; } public Dimension getPreferredScrollableViewportSize() { return this.getPreferredSize(); } public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { return 10; } } /** Invokes default action. */ private class DefaultTreeAction implements ActionListener { DefaultTreeAction() {} /** * Invoked when an action occurs. */ public void actionPerformed(ActionEvent e) { if (treeTable.getSelectedColumn() != ((TreeTable)treeTable).getTreeColumnIndex()) return; Node[] nodes = manager.getSelectedNodes(); if (nodes.length == 1) { Action a = nodes[0].getPreferredAction(); if (a != null) { if (a.isEnabled()) { a.actionPerformed(new ActionEvent(nodes[0], ActionEvent.ACTION_PERFORMED, "")); // NOI18N } else { Toolkit.getDefaultToolkit().beep(); } } } } } private synchronized SortedNodeTreeModel getSortedNodeTreeModel () { if (sortedNodeTreeModel == null) { sortedNodeTreeModel = new SortedNodeTreeModel(); } return sortedNodeTreeModel; } /* node tree model with added sorting support */ private class SortedNodeTreeModel extends NodeTreeModel { SortedNodeTreeModel() {} private Node.Property sortedByProperty; private boolean sortAscending = true; private Comparator rowComparator; private boolean sortedByName = false; void setNoSorting() { setSortedByProperty( null ); setSortedByName( false ); } boolean isSortingActive () { return ( sortedByProperty != null || sortedByName ); } void setSortedByProperty(Node.Property prop) { if (sortedByProperty == prop) return; sortedByProperty = prop; if (prop == null) rowComparator = null; else sortedByName = false; sortingChanged(); } void setSortedByProperty(Node.Property prop, boolean ascending) { if (sortedByProperty == prop && ascending == sortAscending) return; sortedByProperty = prop; sortAscending = ascending; if (prop == null) rowComparator = null; else sortedByName = false; sortingChanged(); } void setSortedByName(boolean sorted, boolean ascending) { if (sortedByName == sorted && ascending == sortAscending) return; sortedByName = sorted; sortAscending = ascending; if ( sortedByName ) sortedByProperty = null; sortingChanged(); } void setSortedByName(boolean sorted) { sortedByName = sorted; if ( sortedByName ) sortedByProperty = null; sortingChanged(); } void setSortOrder(boolean ascending) { if (ascending == sortAscending) return; sortAscending = ascending; sortingChanged(); } private Node.Property getNodeProperty(Node node, Node.Property prop) { Node.PropertySet[] propsets = node.getPropertySets(); for (int i = 0, n = propsets.length; i < n; i++) { Node.Property[] props = propsets[i].getProperties(); for (int j = 0, m = props.length; j < m; j++) { if (props[j].equals(prop)) { return props[j]; } } } return null; } synchronized Comparator getRowComparator() { if (rowComparator == null) { rowComparator = new Comparator() { public int compare(Object o1, Object o2) { if (o1 == o2) return 0; Node n1 = ((VisualizerNode) o1).node; Node n2 = ((VisualizerNode) o2).node; if (n1 == null && n2 == null) return 0; if (n1 == null) return 1; if (n2 == null) return -1; if (n1.getParentNode () == null || n2.getParentNode () == null) { // PENDING: throw Exception ErrorManager.getDefault ().log ("Warning: TTV.compare: Node " + n1 + " or " + n2 + " has no parent!"); // NOI18N return 0; } if (!(n1.getParentNode ().equals (n2.getParentNode ()))) { // PENDING: throw Exception ErrorManager.getDefault ().log ("Warning: TTV.compare: Nodes " + n1 + " and " + n2 + " has different parent!"); // NOI18N return 0; } int res = 0; if ( sortedByName ) { res = n1.getDisplayName().compareTo(n2.getDisplayName()); return sortAscending ? res : -res; } Node.Property p1 = getNodeProperty(n1, sortedByProperty); Node.Property p2 = getNodeProperty(n2, sortedByProperty); if ( p1 == null && p2 == null ) { return 0; } try { if ( p1 == null ) res = -1; else if ( p2 == null ) res = 1; else { Object v1 = p1.getValue(); Object v2 = p2.getValue(); if ( v1 == null && v2 == null ) return 0; else if( v1 == null ) res = -1; else if ( v2 == null ) res = 1; else { if (v1.getClass() != v2.getClass() || !(v1 instanceof Comparable)) { v1 = v1.toString(); v2 = v2.toString(); } res = ((Comparable)v1).compareTo(v2); } } return sortAscending ? res : -res; } catch (Exception ex) { ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, ex); return 0; } } }; } return rowComparator; } void sortChildren (VisualizerNode parent, boolean synchronous) { //#37802 - resorts are processed too aggressively, causing //NPEs. Except for user-invoked actions (clicking the column //header, etc.), we will defer them to run later on the EQ, so //the change in the node has a chance to be fully processed if (synchronous) { synchronized (this) { if (sortingTask != null) { sortingTask.remove (parent); if (sortingTask.isEmpty()) { sortingTask = null; } } } doSortChildren (parent); } else { synchronized (this) { if (sortingTask == null) { sortingTask = new SortingTask(); SwingUtilities.invokeLater (sortingTask); } } sortingTask.add (parent); } } private SortingTask sortingTask = null; private class SortingTask implements Runnable { private HashSet toSort = new HashSet(); public synchronized void add (VisualizerNode parent) { toSort.add(parent); } public synchronized void remove (VisualizerNode parent) { toSort.remove(parent); } public synchronized boolean isEmpty() { return toSort.isEmpty(); } public void run() { synchronized (SortedNodeTreeModel.this) { SortedNodeTreeModel.this.sortingTask = null; } for (Iterator i=toSort.iterator(); i.hasNext();) { VisualizerNode curr = (VisualizerNode) i.next(); SortedNodeTreeModel.this.doSortChildren(curr); } } } void doSortChildren (VisualizerNode parent) { final Comparator comparator = getRowComparator(); if (comparator != null || parent != null) { parent.reorderChildren (comparator); } } void sortingChanged() { // PENDING: remember the last sorting to avoid multiple sorting // remenber expanded folders TreeNode tn = (TreeNode)(this.getRoot()); java.util.List list = new ArrayList(); Enumeration en = TreeTableView.this.tree.getExpandedDescendants( new TreePath( tn ) ); while ( en != null && en.hasMoreElements() ) { TreePath path = (TreePath)en.nextElement(); // bugfix #32328, don't sort whole subtree but only expanded folders sortChildren ((VisualizerNode)path.getLastPathComponent (), true); list.add( path ); } // expand again folders for (int i=0; i
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.