KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > explorer > view > TreeTableView


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.openide.explorer.view;
20
21 import java.util.logging.Logger JavaDoc;
22 import org.openide.awt.MouseUtils;
23 import org.openide.nodes.Node;
24 import org.openide.nodes.Node.Property;
25 import org.openide.util.NbBundle;
26 import org.openide.explorer.ExplorerManager;
27 import org.openide.explorer.ExplorerManager.Provider;
28 import org.openide.explorer.ExplorerUtils;
29
30 import java.awt.*;
31 import java.awt.event.*;
32
33 import java.beans.PropertyChangeEvent JavaDoc;
34 import java.beans.PropertyChangeListener JavaDoc;
35
36 import java.util.ArrayList JavaDoc;
37 import java.util.Comparator JavaDoc;
38 import java.util.Enumeration JavaDoc;
39 import java.util.HashSet JavaDoc;
40 import java.util.Iterator JavaDoc;
41 import java.util.logging.Level JavaDoc;
42
43 import javax.accessibility.AccessibleContext JavaDoc;
44
45 import javax.swing.*;
46 import javax.swing.border.Border JavaDoc;
47 import javax.swing.event.*;
48 import javax.swing.plaf.metal.MetalScrollBarUI JavaDoc;
49 import javax.swing.table.*;
50 import javax.swing.tree.*;
51 import org.openide.explorer.view.TreeView.PopupAdapter;
52 import org.openide.explorer.view.TreeView.PopupSupport;
53 import org.openide.explorer.view.TreeView.TreePropertyListener;
54
55
56 /** Explorer view. Allows to view tree of nodes on the left
57  * and its properties in table on the right.
58  * <p>
59  * The main mechanism for setting what properties are displayed is
60  * <a HREF="#setProperties"><code>setProperties (Node.Property[])</code></a>.
61  * Pass this method an
62  * array of properties. These will act as a template, and properties of
63  * the displayed nodes which share the same <i>name</i> will be used in
64  * the columns of the table.
65  *
66  * You can customize behaviour
67  * of property columns using <code>Property.setValue (String parameter,
68  * Object value)</code>. For example,
69  * assume you have following array of properties:
70  * <br><code>org.openide.nodes.Node.Property[] properties</code><br>
71  *
72  * if you need second column to be initially invisible in TreeTableView, you
73  * should set its custom parameter:
74  * <br><code>properties[1].setValue ("InvisibleInTreeTableView", Boolean.TRUE);</code>
75  *
76  * <TABLE border="1" summary="custom parameter list">
77  * <TR>
78  * <TH> Parameter name
79  * </TH>
80  * <TH> Parameter type
81  * </TH>
82  * <TH> Description
83  * </TH>
84  * </TR>
85  * <TR>
86  * <TD> InvisibleInTreeTableView</TD>
87  * <TD> Boolean </TD>
88  * <TD> This property column should be initially invisible (hidden).</TD>
89  * </TR>
90  * <TR>
91  * <TD> ComparableColumnTTV</TD>
92  * <TD> Boolean </TD>
93  * <TD> This property column should be used for sorting.</TD>
94  * </TR>
95  * <TR>
96  * <TD> SortingColumnTTV</TD>
97  * <TD> Boolean </TD>
98  * <TD> TreeTableView should be initially sorted by this property column.</TD>
99  * </TR>
100  * <TR>
101  * <TD> DescendingOrderTTV</TD>
102  * <TD> Boolean </TD>
103  * <TD> If this parameter and <code>SortingColumnTTV</code> is set, TreeTableView should
104  * be initially sorted by this property columns in descending order.
105  * </TD>
106  * </TR>
107  * <TR>
108  * <TD> OrderNumberTTV</TD>
109  * <TD> Integer </TD>
110  * <TD> If this parameter is set to <code>N</code>, this property column will be
111  * displayed as Nth column of table. If not set, column will be
112  * displayed in natural order.
113  * </TD>
114  * </TR>
115  * <TR>
116  * <TD> TreeColumnTTV</TD>
117  * <TD> Boolean </TD>
118  * <TD> Identifies special property representing first (tree) column. To allow setting
119  * of <code>SortingColumnTTV, DescendingOrderTTV, ComparableColumnTTV</code> parameters
120  * also for first (tree) column, use this special parameter and add
121  * this property to Node.Property[] array before calling
122  * TreeTableView.setProperties (Node.Property[]).
123  * </TD>
124  * </TR>
125  * <TR>
126  * <TD> ColumnMnemonicCharTTV</TD>
127  * <TD> String </TD>
128  * <TD> When set, this parameter contains the mnemonic character for column's
129  * display name (e.g. in <I>Change Visible Columns</I> dialog window).
130  * If not set, no mnemonic will be displayed.
131  * </TD>
132  * </TR>
133  * </TABLE>
134  *
135  * <p>
136  * This class is a <q>view</q>
137  * to use it properly you need to add it into a component which implements
138  * {@link Provider}. Good examples of that can be found
139  * in {@link ExplorerUtils}. Then just use
140  * {@link Provider#getExplorerManager} call to get the {@link ExplorerManager}
141  * and control its state.
142  * </p>
143  * <p>
144  * There can be multiple <q>views</q> under one container implementing {@link Provider}. Select from
145  * range of predefined ones or write your own:
146  * </p>
147  * <ul>
148  * <li>{@link org.openide.explorer.view.BeanTreeView} - shows a tree of nodes</li>
149  * <li>{@link org.openide.explorer.view.ContextTreeView} - shows a tree of nodes without leaf nodes</li>
150  * <li>{@link org.openide.explorer.view.ListView} - shows a list of nodes</li>
151  * <li>{@link org.openide.explorer.view.IconView} - shows a rows of nodes with bigger icons</li>
152  * <li>{@link org.openide.explorer.view.ChoiceView} - creates a combo box based on the explored nodes</li>
153  * <li>{@link org.openide.explorer.view.TreeTableView} - shows tree of nodes together with a set of their {@link Property}</li>
154  * <li>{@link org.openide.explorer.view.MenuView} - can create a {@link JMenu} structure based on structure of {@link Node}s</li>
155  * </ul>
156  * <p>
157  * All of these views use {@link ExplorerManager#find} to walk up the AWT hierarchy and locate the
158  * {@link ExplorerManager} to use as a controler. They attach as listeners to
159  * it and also call its setter methods to update the shared state based on the
160  * user action. Not all views make sence together, but for example
161  * {@link org.openide.explorer.view.ContextTreeView} and {@link org.openide.explorer.view.ListView} were designed to complement
162  * themselves and behaves like windows explorer. The {@link org.openide.explorer.propertysheet.PropertySheetView}
163  * for example should be able to work with any other view.
164  * </p>
165  *
166  * @author jrojcek
167  * @since 1.7
168  */

169 public class TreeTableView extends BeanTreeView {
170     // icon of column button
171
private static final String JavaDoc COLUMNS_ICON = "/org/netbeans/modules/openide/explorer/columns.gif"; // NOI18N
172

173     // icons of ascending/descending order in column header
174
private static final String JavaDoc SORT_ASC_ICON = "org/netbeans/modules/openide/explorer/columnsSortedAsc.gif"; // NOI18N
175
private static final String JavaDoc SORT_DESC_ICON = "org/netbeans/modules/openide/explorer/columnsSortedDesc.gif"; // NOI18N
176

177     /** The table */
178     protected JTable treeTable;
179     private NodeTableModel tableModel;
180
181     // Tree scroll support
182
private JScrollBar hScrollBar;
183     private JScrollPane scrollPane;
184     private ScrollListener listener;
185
186     // hiding columns allowed
187
private boolean allowHideColumns = false;
188
189     // sorting by column allowed
190
private boolean allowSortingByColumn = false;
191
192     // hide horizontal scrollbar
193
private boolean hideHScrollBar = false;
194
195     // button in corner of scroll pane
196
private JButton colsButton = null;
197
198     // tree model with sorting support
199
private SortedNodeTreeModel sortedNodeTreeModel;
200
201     /** Listener on keystroke to invoke default action */
202     private ActionListener defaultTreeActionListener;
203
204     // default treetable header renderer
205
private TableCellRenderer defaultHeaderRenderer = null;
206     private MouseUtils.PopupMouseAdapter tableMouseListener;
207
208     /** Accessible context of this class (implemented by inner class AccessibleTreeTableView). */
209     private AccessibleContext JavaDoc accessContext;
210     private TreeColumnProperty treeColumnProperty = new TreeColumnProperty();
211     private int treeColumnWidth;
212     private Component treeTableParent = null;
213
214     /** Create TreeTableView with default NodeTableModel
215      */

216     public TreeTableView() {
217         this(new NodeTableModel());
218     }
219
220     /** Creates TreeTableView with provided NodeTableModel.
221      * @param ntm node table model
222      */

223     public TreeTableView(NodeTableModel ntm) {
224         tableModel = ntm;
225
226         initializeTreeTable();
227         setPopupAllowed(true);
228         setDefaultActionAllowed(true);
229
230         initializeTreeScrollSupport();
231
232         // add scrollbar and scrollpane into a panel
233
JPanel p = new CompoundScrollPane();
234         p.setLayout(new BorderLayout());
235         scrollPane.setViewportView(treeTable);
236         p.add(BorderLayout.CENTER, scrollPane);
237
238         ImageIcon ic = new ImageIcon(TreeTable.class.getResource(COLUMNS_ICON)); // NOI18N
239
colsButton = new javax.swing.JButton JavaDoc(ic);
240         colsButton.addActionListener(
241             new ActionListener() {
242                 public void actionPerformed(ActionEvent evt) {
243                     selectVisibleColumns();
244                 }
245             }
246         );
247
248         JPanel sbp = new JPanel();
249         sbp.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
250         sbp.add(hScrollBar);
251         p.add(BorderLayout.SOUTH, sbp);
252
253         super.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
254         super.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
255         setViewportView(p);
256         setBorder(BorderFactory.createEmptyBorder()); //NOI18N
257
setViewportBorder(BorderFactory.createEmptyBorder()); //NOI18N
258
}
259
260     public void setRowHeader(JViewport rowHeader) {
261         rowHeader.setBorder(BorderFactory.createEmptyBorder());
262         super.setRowHeader(rowHeader);
263     }
264
265     /* Overriden to allow hide special horizontal scrollbar
266      */

267     public void setHorizontalScrollBarPolicy(int policy) {
268         hideHScrollBar = (policy == JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
269
270         if (hideHScrollBar) {
271             hScrollBar.setVisible(false);
272             ((TreeTable) treeTable).setTreeHScrollingEnabled(false);
273         }
274     }
275
276     /* Overriden to delegate policy of vertical scrollbar to inner scrollPane
277      */

278     public void setVerticalScrollBarPolicy(int policy) {
279         if (scrollPane == null) {
280             return;
281         }
282
283         allowHideColumns = (policy == JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
284
285         if (allowHideColumns) {
286             scrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, colsButton);
287         }
288
289         treeTable.getTableHeader().setReorderingAllowed(allowHideColumns);
290
291         scrollPane.setVerticalScrollBarPolicy(policy);
292     }
293
294     protected NodeTreeModel createModel() {
295         return getSortedNodeTreeModel();
296     }
297
298     /** Requests focus for the tree component. Overrides superclass method. */
299     public void requestFocus() {
300         if (treeTable != null) {
301             treeTable.requestFocus();
302         }
303     }
304
305     public boolean requestFocusInWindow() {
306         boolean res = super.requestFocusInWindow();
307
308         //#44856: pass the focus request to the treetable as well
309
if (null != treeTable) {
310             treeTable.requestFocus();
311         }
312
313         return res;
314     }
315
316     /* Sets sorting ability
317      */

318     private void setAllowSortingByColumn(boolean allow) {
319         if (allow && (allow != allowSortingByColumn)) {
320             addMouseListener(
321                 new MouseAdapter() {
322                     public void mouseClicked(MouseEvent evt) {
323                         // Check whether it was really a click
324
if (evt.getClickCount() == 0) return ;
325                         Component c = evt.getComponent();
326
327                         if (c instanceof JTableHeader) {
328                             JTableHeader h = (JTableHeader) c;
329                             int index = h.columnAtPoint(evt.getPoint());
330
331                             //issue 38442, column can be -1 if this is the
332
//upper right corner - there's no column there,
333
//so make sure it's an index >=0.
334
if (index >= 0) {
335                                 clickOnColumnAction(index - 1);
336                             }
337                         }
338                     }
339                 }
340             );
341         }
342
343         allowSortingByColumn = allow;
344     }
345
346     /* Change sorting after clicking on comparable column header.
347      * Cycle through ascending -> descending -> no sort -> (start over)
348      */

349     private void clickOnColumnAction(int index) {
350         if (index == -1) {
351             if (treeColumnProperty.isComparable()) {
352                 if (treeColumnProperty.isSortingColumn()) {
353                     if (!treeColumnProperty.isSortOrderDescending()) {
354                         setSortingOrder(false);
355                     } else {
356                         noSorting();
357                     }
358                 } else {
359                     int realIndex = tableModel.translateVisibleColumnIndex(index);
360                     setSortingColumn(index);
361                     setSortingOrder(true);
362                 }
363             }
364         } else if (tableModel.isComparableColumn(index)) {
365             if (tableModel.isSortingColumnEx(tableModel.translateVisibleColumnIndex(index))) {
366                 if (!tableModel.isSortOrderDescending()) {
367                     setSortingOrder(false);
368                 } else {
369                     noSorting();
370                 }
371             } else {
372                 int realIndex = tableModel.translateVisibleColumnIndex(index);
373                 setSortingColumn(realIndex);
374                 setSortingOrder(true);
375             }
376         }
377     }
378
379     private void selectVisibleColumns() {
380         setCurrentWidths();
381
382         String JavaDoc viewName = null;
383
384         if (getParent() != null) {
385             viewName = getParent().getName();
386         }
387
388         if (
389             tableModel.selectVisibleColumns(
390                     viewName, treeTable.getColumnName(0), getSortedNodeTreeModel().getRootDescription()
391                 )
392         ) {
393             if (tableModel.getSortingColumn() == -1) {
394                 getSortedNodeTreeModel().setSortedByProperty(null);
395             }
396
397             setTreePreferredWidth(treeColumnWidth);
398
399             for (int i = 0; i < tableModel.getColumnCount(); i++) {
400                 setTableColumnPreferredWidth(tableModel.getArrayIndex(i), tableModel.getVisibleColumnWidth(i));
401             }
402         }
403     }
404
405     private void setCurrentWidths() {
406         treeColumnWidth = treeTable.getColumnModel().getColumn(0).getWidth();
407
408         for (int i = 0; i < tableModel.getColumnCount(); i++) {
409             int w = treeTable.getColumnModel().getColumn(i + 1).getWidth();
410             tableModel.setVisibleColumnWidth(i, w);
411         }
412     }
413
414     /** Do not initialize tree now. We will do it from our constructor.
415      * [dafe] Used probably because this method is called *before* superclass
416      * is fully created (constructor finished) which is horrible but I don't
417      * have enough knowledge about this code to change it.
418      */

419     void initializeTree() {
420     }
421
422     /** Initialize tree and treeTable.
423      */

424     private void initializeTreeTable() {
425         treeModel = createModel();
426         treeTable = new TreeTable(treeModel, tableModel);
427         tree = ((TreeTable) treeTable).getTree();
428
429         defaultHeaderRenderer = treeTable.getTableHeader().getDefaultRenderer();
430         treeTable.getTableHeader().setDefaultRenderer(new SortingHeaderRenderer());
431
432         // init listener & attach it to closing of
433
managerListener = new TreePropertyListener();
434         tree.addTreeExpansionListener(managerListener);
435
436         // add listener to sort a new expanded folders
437
tree.addTreeExpansionListener(
438             new TreeExpansionListener() {
439                 public void treeExpanded(TreeExpansionEvent event) {
440                     TreePath path = event.getPath();
441
442                     if (path != null) {
443                         // bugfix $32480, store and recover currently expanded subnodes
444
// store expanded paths
445
Enumeration JavaDoc en = TreeTableView.this.tree.getExpandedDescendants(path);
446
447                         // sort children
448
getSortedNodeTreeModel().sortChildren((VisualizerNode) path.getLastPathComponent(), true);
449
450                         // expand again folders
451
while (en.hasMoreElements()) {
452                             TreeTableView.this.tree.expandPath((TreePath) en.nextElement());
453                         }
454                     }
455                 }
456
457                 public void treeCollapsed(TreeExpansionEvent event) {
458                     // ignore it
459
}
460             }
461         );
462
463         defaultActionListener = new PopupSupport();
464         Action popupWrapper = new AbstractAction() {
465                 public void actionPerformed(ActionEvent evt) {
466                     SwingUtilities.invokeLater( defaultActionListener );
467                 }
468
469                 public boolean isEnabled() {
470                     return treeTable.isFocusOwner() || tree.isFocusOwner();
471                 }
472             };
473             
474         treeTable.getInputMap( JTree.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).put(
475                 KeyStroke.getKeyStroke( KeyEvent.VK_F10, KeyEvent.SHIFT_DOWN_MASK ), "org.openide.actions.PopupAction" );
476         treeTable.getActionMap().put("org.openide.actions.PopupAction", popupWrapper);
477         tree.addMouseListener(defaultActionListener);
478
479         tableMouseListener = new MouseUtils.PopupMouseAdapter() {
480                     public void showPopup(MouseEvent mevt) {
481                         if (isPopupAllowed()) {
482                             if (mevt.getY() > treeTable.getHeight()) {
483                                 // clear selection, if click under the table
484
treeTable.clearSelection();
485                             } else {
486                                 int selRow = treeTable.rowAtPoint( mevt.getPoint() );
487                                 boolean isAlreadySelected = false;
488                                 int[] currentSelection = tree.getSelectionRows();
489                                 for( int i=0; null != currentSelection && i<currentSelection.length; i++ ) {
490                                     if( selRow == currentSelection[i] ) {
491                                         isAlreadySelected = true;
492                                         break;
493                                     }
494                                 }
495                                 if( !isAlreadySelected )
496                                     tree.setSelectionRow( selRow );
497                             }
498
499                             createPopup(mevt);
500                         }
501                     }
502                 };
503         treeTable.addMouseListener(tableMouseListener);
504
505         if (UIManager.getColor("control") != null) { // NOI18N
506
treeTable.setGridColor(UIManager.getColor("control")); // NOI18N
507
}
508     }
509
510     public void setSelectionMode(int mode) {
511         super.setSelectionMode(mode);
512
513         if (mode == TreeSelectionModel.SINGLE_TREE_SELECTION) {
514             treeTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
515         } else if (mode == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION) {
516             treeTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
517         } else if (mode == TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION) {
518             treeTable.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
519         }
520     }
521
522     /** Overrides JScrollPane's getAccessibleContext() method to use internal accessible context.
523      */

524     public AccessibleContext JavaDoc getAccessibleContext() {
525         if (accessContext == null) {
526             accessContext = new AccessibleTreeTableView();
527         }
528
529         return accessContext;
530     }
531
532     /** Initialize full support for horizontal scrolling.
533      */

534     private void initializeTreeScrollSupport() {
535         scrollPane = new JScrollPane();
536         scrollPane.setName("TreeTableView.scrollpane"); //NOI18N
537
scrollPane.setBorder(BorderFactory.createEmptyBorder());
538         scrollPane.setViewportBorder(BorderFactory.createEmptyBorder());
539
540         if (UIManager.getColor("Table.background") != null) { // NOI18N
541
scrollPane.getViewport().setBackground(UIManager.getColor("Table.background")); // NOI18N
542
}
543
544         hScrollBar = new JScrollBar(JScrollBar.HORIZONTAL);
545         hScrollBar.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, Boolean.FALSE);
546         hScrollBar.setVisible(false);
547
548         listener = new ScrollListener();
549
550         treeTable.addPropertyChangeListener(listener);
551         scrollPane.getViewport().addComponentListener(listener);
552         tree.addPropertyChangeListener(listener);
553         hScrollBar.getModel().addChangeListener(listener);
554     }
555
556     /* Overriden to work well with treeTable.
557      */

558     public void setPopupAllowed(boolean value) {
559         if (tree == null) {
560             return;
561         }
562
563         if ((popupListener == null) && value) {
564             // on
565
popupListener = new PopupAdapter() {
566                         protected void showPopup(MouseEvent e) {
567                             int selRow = tree.getClosestRowForLocation(e.getX(), e.getY());
568
569                             if (!tree.isRowSelected(selRow)) {
570                                 tree.setSelectionRow(selRow);
571                             }
572                         }
573                     };
574
575             tree.addMouseListener(popupListener);
576
577             return;
578         }
579
580         if ((popupListener != null) && !value) {
581             // off
582
tree.removeMouseListener(popupListener);
583             popupListener = null;
584
585             return;
586         }
587     }
588
589     /* Overriden to work well with treeTable.
590      */

591     public void setDefaultActionAllowed(boolean value) {
592         if (tree == null) {
593             return;
594         }
595
596         defaultActionEnabled = value;
597
598         if (value) {
599             defaultTreeActionListener = new DefaultTreeAction();
600             treeTable.registerKeyboardAction(
601                 defaultTreeActionListener, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false), JComponent.WHEN_FOCUSED
602             );
603         } else {
604             // Switch off.
605
defaultTreeActionListener = null;
606             treeTable.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false));
607         }
608     }
609
610     /** Set columns.
611      * @param props each column is constructed from Node.Property
612      */

613     public void setProperties(Property[] props) {
614         tableModel.setProperties(props);
615         treeColumnProperty.setProperty(tableModel.propertyForColumn(-1));
616
617         if (treeColumnProperty.isComparable() || tableModel.existsComparableColumn()) {
618             setAllowSortingByColumn(true);
619
620             if (treeColumnProperty.isSortingColumn()) {
621                 getSortedNodeTreeModel().setSortedByName(true, !treeColumnProperty.isSortOrderDescending());
622             } else {
623                 int index = tableModel.getSortingColumn();
624
625                 if (index != -1) {
626                     getSortedNodeTreeModel().setSortedByProperty(
627                         tableModel.propertyForColumnEx(index), !tableModel.isSortOrderDescending()
628                     );
629                 }
630             }
631         }
632     }
633
634     /** Sets resize mode of table.
635      *
636      * @param mode - One of 5 legal values: <pre>JTable.AUTO_RESIZE_OFF,
637      * JTable.AUTO_RESIZE_NEXT_COLUMN,
638      * JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS,
639      * JTable.AUTO_RESIZE_LAST_COLUMN,
640      * JTable.AUTO_RESIZE_ALL_COLUMNS</pre>
641      */

642     public final void setTableAutoResizeMode(int mode) {
643         treeTable.setAutoResizeMode(mode);
644     }
645
646     /** Gets resize mode of table.
647      *
648      * @return mode - One of 5 legal values: <pre>JTable.AUTO_RESIZE_OFF,
649      * JTable.AUTO_RESIZE_NEXT_COLUMN,
650      * JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS,
651      * JTable.AUTO_RESIZE_LAST_COLUMN,
652      * JTable.AUTO_RESIZE_ALL_COLUMNS</pre>
653      */

654     public final int getTableAutoResizeMode() {
655         return treeTable.getAutoResizeMode();
656     }
657
658     /** Sets preferred width of table column
659      * @param index column index
660      * @param width preferred column width
661      */

662     public final void setTableColumnPreferredWidth(int index, int width) {
663         if (index == -1) {
664             //Issue 47969 - sometimes this is called with a -1 arg
665
return;
666         }
667
668         tableModel.setArrayColumnWidth(index, width);
669
670         int j = tableModel.getVisibleIndex(index);
671
672         if (j != -1) {
673             treeTable.getColumnModel().getColumn(j + 1).setPreferredWidth(width);
674         }
675     }
676
677     /** Gets preferred width of table column
678      * @param index column index
679      * @return preferred column width
680      */

681     public final int getTableColumnPreferredWidth(int index) {
682         int j = tableModel.getVisibleIndex(index);
683
684         if (j != -1) {
685             return treeTable.getColumnModel().getColumn(j + 1).getPreferredWidth();
686         } else {
687             return tableModel.getArrayColumnWidth(index);
688         }
689     }
690
691     /** Set preferred size of tree view
692      * @param width preferred width of tree view
693      */

694     public final void setTreePreferredWidth(int width) {
695         treeTable.getColumnModel().getColumn(((TreeTable) treeTable).getTreeColumnIndex()).setPreferredWidth(width);
696     }
697
698     /** Get preferred size of tree view
699      * @return preferred width of tree view
700      */

701     public final int getTreePreferredWidth() {
702         return treeTable.getColumnModel().getColumn(((TreeTable) treeTable).getTreeColumnIndex()).getPreferredWidth();
703     }
704
705     public void addNotify() {
706         // to allow displaying popup also in blank area
707
if (treeTable.getParent() != null) {
708             treeTableParent = treeTable.getParent();
709             treeTableParent.addMouseListener(tableMouseListener);
710         }
711
712         super.addNotify();
713         listener.revalidateScrollBar();
714     }
715
716     public void removeNotify() {
717         super.removeNotify();
718
719         if (treeTableParent != null) { //IndexedEditorPanel
720
treeTableParent.removeMouseListener(tableMouseListener);
721         }
722
723         treeTableParent = null;
724
725         // clear node listeners
726
tableModel.setNodes(new Node[] { });
727     }
728
729     public void addMouseListener(MouseListener l) {
730         super.addMouseListener(l);
731         treeTable.getTableHeader().addMouseListener(l);
732     }
733
734     public void removeMouseListener(MouseListener l) {
735         super.removeMouseListener(l);
736         treeTable.getTableHeader().removeMouseListener(l);
737     }
738
739     /* DnD is not implemented for treeTable.
740      */

741     public void setDragSource(boolean state) {
742     }
743
744     /* DnD is not implemented for treeTable.
745      */

746     public void setDropTarget(boolean state) {
747     }
748
749     /* Overriden to get position for popup invoked by keyboard
750      */

751     Point getPositionForPopup() {
752         int row = treeTable.getSelectedRow();
753
754         if (row < 0) {
755             return null;
756         }
757
758         int col = treeTable.getSelectedColumn();
759
760         if (col < 0) {
761             col = 0;
762         }
763
764         Rectangle r = null;
765
766         if (col == 0) {
767             r = tree.getRowBounds(row);
768         } else {
769             r = treeTable.getCellRect(row, col, true);
770         }
771
772         Point p = SwingUtilities.convertPoint(treeTable, r.x, r.y, this);
773
774         return p;
775     }
776
777     private void createPopup(MouseEvent e) {
778         Point p = SwingUtilities.convertPoint(e.getComponent(), e.getX(), e.getY(), TreeTableView.this);
779
780         createPopup(p.x, p.y);
781
782         e.consume();
783     }
784
785     void createPopup(int xpos, int ypos) {
786         int treeXpos = xpos - ((TreeTable) treeTable).getPositionX();
787
788         if (allowHideColumns || allowSortingByColumn) {
789             int col = treeTable.getColumnModel().getColumnIndexAtX(treeXpos);
790             super.createExtendedPopup(xpos, ypos, getListMenu(col));
791         } else {
792             super.createPopup(xpos, ypos);
793         }
794     }
795
796     /* creates List Options menu
797      */

798     private JMenu getListMenu(final int col) {
799         JMenu listItem = new JMenu(NbBundle.getBundle(NodeTableModel.class).getString("LBL_ListOptions"));
800
801         if (allowHideColumns && (col > 0)) {
802             JMenu colsItem = new JMenu(NbBundle.getBundle(NodeTableModel.class).getString("LBL_ColsMenu"));
803
804             boolean addColsItem = false;
805
806             if (col > 1) {
807                 JMenuItem moveLItem = new JMenuItem(NbBundle.getBundle(NodeTableModel.class).getString("LBL_MoveLeft"));
808                 moveLItem.addActionListener(
809                     new ActionListener() {
810                         public void actionPerformed(java.awt.event.ActionEvent JavaDoc actionEvent) {
811                             treeTable.getColumnModel().moveColumn(col, col - 1);
812                         }
813                     }
814                 );
815                 colsItem.add(moveLItem);
816                 addColsItem = true;
817             }
818
819             if (col < tableModel.getColumnCount()) {
820                 JMenuItem moveRItem = new JMenuItem(
821                         NbBundle.getBundle(NodeTableModel.class).getString("LBL_MoveRight")
822                     );
823                 moveRItem.addActionListener(
824                     new ActionListener() {
825                         public void actionPerformed(java.awt.event.ActionEvent JavaDoc actionEvent) {
826                             treeTable.getColumnModel().moveColumn(col, col + 1);
827                         }
828                     }
829                 );
830                 colsItem.add(moveRItem);
831                 addColsItem = true;
832             }
833
834             if (addColsItem) {
835                 listItem.add(colsItem);
836             }
837         }
838
839         if (allowSortingByColumn) {
840             JMenu sortItem = new JMenu(NbBundle.getBundle(NodeTableModel.class).getString("LBL_SortMenu"));
841             JRadioButtonMenuItem noSortItem = new JRadioButtonMenuItem(
842                     NbBundle.getBundle(NodeTableModel.class).getString("LBL_NoSort"),
843                     !getSortedNodeTreeModel().isSortingActive()
844                 );
845             noSortItem.addActionListener(
846                 new ActionListener() {
847                     public void actionPerformed(java.awt.event.ActionEvent JavaDoc actionEvent) {
848                         noSorting();
849                     }
850                 }
851             );
852             sortItem.add(noSortItem);
853
854             int visibleComparable = 0;
855             JRadioButtonMenuItem colItem;
856
857             if (treeColumnProperty.isComparable()) {
858                 visibleComparable++;
859                 colItem = new JRadioButtonMenuItem(treeTable.getColumnName(0), treeColumnProperty.isSortingColumn());
860                 colItem.setHorizontalTextPosition(SwingConstants.LEFT);
861                 colItem.addActionListener(
862                     new ActionListener() {
863                         public void actionPerformed(java.awt.event.ActionEvent JavaDoc actionEvent) {
864                             setSortingColumn(-1);
865                         }
866                     }
867                 );
868                 sortItem.add(colItem);
869             }
870
871             for (int i = 0; i < tableModel.getColumnCount(); i++) {
872                 if (tableModel.isComparableColumn(i)) {
873                     visibleComparable++;
874                     colItem = new JRadioButtonMenuItem(
875                             tableModel.getColumnName(i),
876                             tableModel.isSortingColumnEx(tableModel.translateVisibleColumnIndex(i))
877                         );
878                     colItem.setHorizontalTextPosition(SwingConstants.LEFT);
879
880                     final int index = tableModel.translateVisibleColumnIndex(i);
881                     colItem.addActionListener(
882                         new ActionListener() {
883                             public void actionPerformed(java.awt.event.ActionEvent JavaDoc actionEvent) {
884                                 setSortingColumn(index);
885                             }
886                         }
887                     );
888                     sortItem.add(colItem);
889                 }
890             }
891
892             //add invisible columns
893
for (int i = 0; i < tableModel.getColumnCountEx(); i++) {
894                 if (tableModel.isComparableColumnEx(i) && !tableModel.isVisibleColumnEx(i)) {
895                     visibleComparable++;
896                     colItem = new JRadioButtonMenuItem(tableModel.getColumnNameEx(i), tableModel.isSortingColumnEx(i));
897                     colItem.setHorizontalTextPosition(SwingConstants.LEFT);
898
899                     final int index = i;
900                     colItem.addActionListener(
901                         new ActionListener() {
902                             public void actionPerformed(java.awt.event.ActionEvent JavaDoc actionEvent) {
903                                 setSortingColumn(index);
904                             }
905                         }
906                     );
907                     sortItem.add(colItem);
908                 }
909             }
910
911             if (visibleComparable > 0) {
912                 sortItem.addSeparator();
913
914                 boolean current_sort;
915
916                 if (treeColumnProperty.isSortingColumn()) {
917                     current_sort = treeColumnProperty.isSortOrderDescending();
918                 } else {
919                     current_sort = tableModel.isSortOrderDescending();
920                 }
921
922                 JRadioButtonMenuItem ascItem = new JRadioButtonMenuItem(
923                         NbBundle.getBundle(NodeTableModel.class).getString("LBL_Ascending"), !current_sort
924                     );
925                 ascItem.setHorizontalTextPosition(SwingConstants.LEFT);
926                 ascItem.addActionListener(
927                     new ActionListener() {
928                         public void actionPerformed(java.awt.event.ActionEvent JavaDoc actionEvent) {
929                             setSortingOrder(true);
930                         }
931                     }
932                 );
933                 sortItem.add(ascItem);
934
935                 JRadioButtonMenuItem descItem = new JRadioButtonMenuItem(
936                         NbBundle.getBundle(NodeTableModel.class).getString("LBL_Descending"), current_sort
937                     );
938                 descItem.setHorizontalTextPosition(SwingConstants.LEFT);
939                 descItem.addActionListener(
940                     new ActionListener() {
941                         public void actionPerformed(java.awt.event.ActionEvent JavaDoc actionEvent) {
942                             setSortingOrder(false);
943                         }
944                     }
945                 );
946                 sortItem.add(descItem);
947
948                 if (!getSortedNodeTreeModel().isSortingActive()) {
949                     ascItem.setEnabled(false);
950                     descItem.setEnabled(false);
951                 }
952
953                 listItem.add(sortItem);
954             }
955         }
956
957         if (allowHideColumns) {
958             JMenuItem visItem = new JMenuItem(NbBundle.getBundle(NodeTableModel.class).getString("LBL_ChangeColumns"));
959             visItem.addActionListener(
960                 new ActionListener() {
961                     public void actionPerformed(java.awt.event.ActionEvent JavaDoc actionEvent) {
962                         selectVisibleColumns();
963                     }
964                 }
965             );
966
967             listItem.add(visItem);
968         }
969
970         return listItem;
971     }
972
973     /* Sets column to be currently used for sorting
974      */

975     private void setSortingColumn(int index) {
976         tableModel.setSortingColumnEx(index);
977
978         if (index != -1) {
979             getSortedNodeTreeModel().setSortedByProperty(
980                 tableModel.propertyForColumnEx(index), !tableModel.isSortOrderDescending()
981             );
982             treeColumnProperty.setSortingColumn(false);
983         } else {
984             getSortedNodeTreeModel().setSortedByName(true, !treeColumnProperty.isSortOrderDescending());
985             treeColumnProperty.setSortingColumn(true);
986         }
987
988         // to change sort icon
989
treeTable.getTableHeader().repaint();
990     }
991
992     private void noSorting() {
993         tableModel.setSortingColumnEx(-1);
994         getSortedNodeTreeModel().setNoSorting();
995         treeColumnProperty.setSortingColumn(false);
996
997         // to change sort icon
998
treeTable.getTableHeader().repaint();
999     }
1000
1001    /* Sets sorting order for current sorting.
1002     */

1003    private void setSortingOrder(boolean ascending) {
1004        if (treeColumnProperty.isSortingColumn()) {
1005            treeColumnProperty.setSortOrderDescending(!ascending);
1006        } else {
1007            tableModel.setSortOrderDescending(!ascending);
1008        }
1009
1010        getSortedNodeTreeModel().setSortOrder(ascending);
1011
1012        // to change sort icon
1013
treeTable.getTableHeader().repaint();
1014    }
1015
1016    private synchronized SortedNodeTreeModel getSortedNodeTreeModel() {
1017        if (sortedNodeTreeModel == null) {
1018            sortedNodeTreeModel = new SortedNodeTreeModel();
1019        }
1020
1021        return sortedNodeTreeModel;
1022    }
1023
1024    /** This is internal accessible context for TreeTableView.
1025     * It delegates setAccessibleName and setAccessibleDescription methods to set these properties
1026     * in underlying TreeTable as well.
1027     */

1028    private class AccessibleTreeTableView extends AccessibleJScrollPane {
1029        AccessibleTreeTableView() {
1030        }
1031
1032        public void setAccessibleName(String JavaDoc accessibleName) {
1033            super.setAccessibleName(accessibleName);
1034
1035            if (treeTable != null) {
1036                treeTable.getAccessibleContext().setAccessibleName(accessibleName);
1037            }
1038        }
1039
1040        public void setAccessibleDescription(String JavaDoc accessibleDescription) {
1041            super.setAccessibleDescription(accessibleDescription);
1042
1043            if (treeTable != null) {
1044                treeTable.getAccessibleContext().setAccessibleDescription(accessibleDescription);
1045            }
1046        }
1047    }
1048
1049    /* Horizontal scrolling support.
1050     */

1051    private final class ScrollListener extends ComponentAdapter implements PropertyChangeListener JavaDoc, ChangeListener JavaDoc {
1052        boolean movecorrection = false;
1053
1054        ScrollListener() {
1055        }
1056
1057        //Column width
1058
public void propertyChange(PropertyChangeEvent JavaDoc evt) {
1059            if (((TreeTable) treeTable).getTreeColumnIndex() == -1) {
1060                return;
1061            }
1062
1063            if ("width".equals(evt.getPropertyName())) { // NOI18N
1064

1065                if (!treeTable.equals(evt.getSource())) {
1066                    Dimension dim = hScrollBar.getPreferredSize();
1067                    dim.width = treeTable.getColumnModel().getColumn(((TreeTable) treeTable).getTreeColumnIndex())
1068                                         .getWidth();
1069                    hScrollBar.setPreferredSize(dim);
1070                    hScrollBar.revalidate();
1071                    hScrollBar.repaint();
1072                }
1073
1074                revalidateScrollBar();
1075            } else if ("positionX".equals(evt.getPropertyName())) { // NOI18N
1076
revalidateScrollBar();
1077            } else if ("treeColumnIndex".equals(evt.getPropertyName())) { // NOI18N
1078
treeTable.getColumnModel().getColumn(((TreeTable) treeTable).getTreeColumnIndex())
1079                         .addPropertyChangeListener(listener);
1080            } else if ("column_moved".equals(evt.getPropertyName())) { // NOI18N
1081

1082                int from = ((Integer JavaDoc) evt.getOldValue()).intValue();
1083                int to = ((Integer JavaDoc) evt.getNewValue()).intValue();
1084
1085                if ((from == 0) || (to == 0)) {
1086                    if (movecorrection) {
1087                        movecorrection = false;
1088                    } else {
1089                        movecorrection = true;
1090
1091                        // not allowed to move first, tree column
1092
treeTable.getColumnModel().moveColumn(to, from);
1093                    }
1094
1095                    return;
1096                }
1097
1098                // module will be revalidated in NodeTableModel
1099
treeTable.getTableHeader().getColumnModel().getColumn(from).setModelIndex(from);
1100                treeTable.getTableHeader().getColumnModel().getColumn(to).setModelIndex(to);
1101                tableModel.moveColumn(from - 1, to - 1);
1102            }
1103        }
1104
1105        //Viewport height
1106
public void componentResized(ComponentEvent e) {
1107            revalidateScrollBar();
1108        }
1109
1110        //ScrollBar change
1111
public void stateChanged(ChangeEvent JavaDoc evt) {
1112            int value = hScrollBar.getModel().getValue();
1113            ((TreeTable) treeTable).setPositionX(value);
1114        }
1115
1116        private void revalidateScrollBar() {
1117            if (!isDisplayable()) {
1118                return;
1119            }
1120
1121            if (
1122                (treeTable.getColumnModel().getColumnCount() > 0) &&
1123                    (((TreeTable) treeTable).getTreeColumnIndex() >= 0)
1124            ) {
1125                int extentWidth = treeTable.getColumnModel().getColumn(((TreeTable) treeTable).getTreeColumnIndex())
1126                                           .getWidth();
1127                int maxWidth = tree.getPreferredSize().width;
1128                int extentHeight = scrollPane.getViewport().getSize().height;
1129                int maxHeight = tree.getPreferredSize().height;
1130                int positionX = ((TreeTable) treeTable).getPositionX();
1131
1132                int value = Math.max(0, Math.min(positionX, maxWidth - extentWidth));
1133
1134                boolean hsbvisible = hScrollBar.isVisible();
1135                boolean vsbvisible = scrollPane.getVerticalScrollBar().isVisible();
1136                int hsbheight = hsbvisible ? hScrollBar.getHeight() : 0;
1137                int vsbwidth = scrollPane.getVerticalScrollBar().getWidth();
1138
1139                hScrollBar.setValues(value, extentWidth, 0, maxWidth);
1140
1141                if (
1142                    hideHScrollBar || (maxWidth <= extentWidth) ||
1143                        (vsbvisible &&
1144                        ((maxHeight <= (extentHeight + hsbheight)) && (maxWidth <= (extentWidth + vsbwidth))))
1145                ) {
1146                    hScrollBar.setVisible(false);
1147                } else {
1148                    hScrollBar.setVisible(true);
1149                }
1150            }
1151        }
1152    }
1153
1154    /** Scrollable (better say not scrollable) pane. Used as container for
1155     * left (controlling) and rigth (controlled) scroll panes.
1156     */

1157    private static final class CompoundScrollPane extends JPanel implements Scrollable {
1158        CompoundScrollPane() {
1159        }
1160
1161        public void setBorder(Border JavaDoc b) {
1162            //do nothing
1163
}
1164
1165        public boolean getScrollableTracksViewportWidth() {
1166            return true;
1167        }
1168
1169        public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
1170            return 10;
1171        }
1172
1173        public boolean getScrollableTracksViewportHeight() {
1174            return true;
1175        }
1176
1177        public Dimension getPreferredScrollableViewportSize() {
1178            return this.getPreferredSize();
1179        }
1180
1181        public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
1182            return 10;
1183        }
1184    }
1185
1186    /** Invokes default action.
1187     */

1188    private class DefaultTreeAction implements ActionListener {
1189        DefaultTreeAction() {
1190        }
1191
1192        /**
1193         * Invoked when an action occurs.
1194         */

1195        public void actionPerformed(ActionEvent e) {
1196            if (treeTable.getSelectedColumn() != ((TreeTable) treeTable).getTreeColumnIndex()) {
1197                return;
1198            }
1199
1200            Node[] nodes = manager.getSelectedNodes();
1201
1202            if (nodes.length == 1) {
1203                Action a = nodes[0].getPreferredAction();
1204
1205                if (a != null) {
1206                    if (a.isEnabled()) {
1207                        a.actionPerformed(new ActionEvent(nodes[0], ActionEvent.ACTION_PERFORMED, "")); // NOI18N
1208
} else {
1209                        Toolkit.getDefaultToolkit().beep();
1210                    }
1211                }
1212            }
1213        }
1214    }
1215
1216    /* node tree model with added sorting support
1217     */

1218    private class SortedNodeTreeModel extends NodeTreeModel {
1219        private Node.Property sortedByProperty;
1220        private boolean sortAscending = true;
1221        private Comparator JavaDoc<VisualizerNode> rowComparator;
1222        private boolean sortedByName = false;
1223        private SortingTask sortingTask = null;
1224
1225        SortedNodeTreeModel() {
1226        }
1227
1228        void setNoSorting() {
1229            setSortedByProperty(null);
1230            setSortedByName(false);
1231            sortingChanged();
1232        }
1233
1234        boolean isSortingActive() {
1235            return ((sortedByProperty != null) || sortedByName);
1236        }
1237
1238        void setSortedByProperty(Node.Property prop) {
1239            if (sortedByProperty == prop) {
1240                return;
1241            }
1242
1243            sortedByProperty = prop;
1244
1245            if (prop == null) {
1246                rowComparator = null;
1247            } else {
1248                sortedByName = false;
1249            }
1250
1251            sortingChanged();
1252        }
1253
1254        void setSortedByProperty(Node.Property prop, boolean ascending) {
1255            if ((sortedByProperty == prop) && (ascending == sortAscending)) {
1256                return;
1257            }
1258
1259            sortedByProperty = prop;
1260            sortAscending = ascending;
1261
1262            if (prop == null) {
1263                rowComparator = null;
1264            } else {
1265                sortedByName = false;
1266            }
1267
1268            sortingChanged();
1269        }
1270
1271        void setSortedByName(boolean sorted, boolean ascending) {
1272            if ((sortedByName == sorted) && (ascending == sortAscending)) {
1273                return;
1274            }
1275
1276            sortedByName = sorted;
1277            sortAscending = ascending;
1278
1279            if (sortedByName) {
1280                sortedByProperty = null;
1281            }
1282
1283            sortingChanged();
1284        }
1285
1286        void setSortedByName(boolean sorted) {
1287            sortedByName = sorted;
1288
1289            if (sortedByName) {
1290                sortedByProperty = null;
1291            }
1292
1293            sortingChanged();
1294        }
1295
1296        void setSortOrder(boolean ascending) {
1297            if (ascending == sortAscending) {
1298                return;
1299            }
1300
1301            sortAscending = ascending;
1302            sortingChanged();
1303        }
1304
1305        private Node.Property getNodeProperty(Node node, Node.Property prop) {
1306            Node.PropertySet[] propsets = node.getPropertySets();
1307
1308            for (int i = 0, n = propsets.length; i < n; i++) {
1309                Node.Property[] props = propsets[i].getProperties();
1310
1311                for (int j = 0, m = props.length; j < m; j++) {
1312                    if (props[j].equals(prop)) {
1313                        return props[j];
1314                    }
1315                }
1316            }
1317
1318            return null;
1319        }
1320
1321        synchronized Comparator JavaDoc<VisualizerNode> getRowComparator() {
1322            if (rowComparator == null) {
1323                rowComparator = new Comparator JavaDoc<VisualizerNode>() {
1324
1325                    public int compare(VisualizerNode o1, VisualizerNode o2) {
1326                        if (o1 == o2) {
1327                            return 0;
1328                        }
1329                        Node n1 = o1.node;
1330                        Node n2 = o2.node;
1331
1332                        if ((n1 == null) && (n2 == null)) {
1333                            return 0;
1334                        }
1335                        if (n1 == null) {
1336                            return 1;
1337                        }
1338                        if (n2 == null) {
1339                            return -1;
1340                        }
1341                        if ((n1.getParentNode() == null) ||
1342                            (n2.getParentNode() == null)) {
1343                            // PENDING: throw Exception
1344
Logger.getAnonymousLogger().warning("TTV.compare: Node " +
1345                                                                n1 + " or " + n2 +
1346                                                                " has no parent!");
1347                            return 0;
1348                        }
1349                        if (!(n1.getParentNode().equals(n2.getParentNode()))) {
1350                            // PENDING: throw Exception
1351
Logger.getAnonymousLogger().warning("TTV.compare: Nodes " +
1352                                                                n1 + " and " +
1353                                                                n2 +
1354                                                                " has different parent!");
1355                            return 0;
1356                        }
1357                        int res = 0;
1358
1359                        if (sortedByName) {
1360                            res = n1.getDisplayName().compareTo(n2.getDisplayName());
1361                            return sortAscending ? res
1362                                                 : (-res);
1363                        }
1364                        Property p1 = getNodeProperty(n1, sortedByProperty);
1365                        Property p2 = getNodeProperty(n2, sortedByProperty);
1366
1367                        if ((p1 == null) && (p2 == null)) {
1368                            return 0;
1369                        }
1370                        try {
1371                            if (p1 == null) {
1372                                res = -1;
1373                            } else if (p2 == null) {
1374                                res = 1;
1375                            } else {
1376                                Object JavaDoc v1 = p1.getValue();
1377                                Object JavaDoc v2 = p2.getValue();
1378
1379                                if ((v1 == null) && (v2 == null)) {
1380                                    return 0;
1381                                } else if (v1 == null) {
1382                                    res = -1;
1383                                } else if (v2 == null) {
1384                                    res = 1;
1385                                } else {
1386                                    if ((v1.getClass() != v2.getClass()) ||
1387                                        !(v1 instanceof Comparable JavaDoc)) {
1388                                        v1 = v1.toString();
1389                                        v2 = v2.toString();
1390                                    }
1391                                    res = ((Comparable JavaDoc) v1).compareTo(v2);
1392                                }
1393                            }
1394                            return sortAscending ? res
1395                                                 : (-res);
1396                        }
1397                        catch (Exception JavaDoc ex) {
1398                            Logger.getLogger(TreeTableView.class.getName()).log(Level.WARNING, null, ex);
1399                            return 0;
1400                        }
1401                    }
1402                };
1403            }
1404
1405            return rowComparator;
1406        }
1407
1408        void sortChildren(VisualizerNode parent, boolean synchronous) {
1409            //#37802 - resorts are processed too aggressively, causing
1410
//NPEs. Except for user-invoked actions (clicking the column
1411
//header, etc.), we will defer them to run later on the EQ, so
1412
//the change in the node has a chance to be fully processed
1413
if (synchronous) {
1414                synchronized (this) {
1415                    if (sortingTask != null) {
1416                        sortingTask.remove(parent);
1417
1418                        if (sortingTask.isEmpty()) {
1419                            sortingTask = null;
1420                        }
1421                    }
1422                }
1423
1424                doSortChildren(parent);
1425            } else {
1426                synchronized (this) {
1427                    if (sortingTask == null) {
1428                        sortingTask = new SortingTask();
1429                        SwingUtilities.invokeLater(sortingTask);
1430                    }
1431                }
1432
1433                sortingTask.add(parent);
1434            }
1435        }
1436
1437        void doSortChildren(VisualizerNode parent) {
1438            if (isSortingActive()) {
1439                final Comparator JavaDoc<VisualizerNode> comparator = getRowComparator();
1440
1441                if ((comparator != null) || (parent != null)) {
1442                    parent.reorderChildren(comparator);
1443                }
1444            } else {
1445                parent.naturalOrder();
1446            }
1447        }
1448
1449        void sortingChanged() {
1450            // PENDING: remember the last sorting to avoid multiple sorting
1451
// remenber expanded folders
1452
TreeNode tn = (TreeNode) (this.getRoot());
1453            java.util.List JavaDoc<TreePath> list = new ArrayList JavaDoc<TreePath>();
1454            Enumeration JavaDoc<TreePath> en = TreeTableView.this.tree.getExpandedDescendants(new TreePath(tn));
1455
1456            while ((en != null) && en.hasMoreElements()) {
1457                TreePath path = en.nextElement();
1458
1459                // bugfix #32328, don't sort whole subtree but only expanded folders
1460
sortChildren((VisualizerNode) path.getLastPathComponent(), true);
1461                list.add(path);
1462            }
1463
1464            // expand again folders
1465
for (int i = 0; i < list.size(); i++) {
1466                TreeTableView.this.tree.expandPath(list.get(i));
1467            }
1468        }
1469
1470        String JavaDoc getRootDescription() {
1471            if (getRoot() instanceof VisualizerNode) {
1472                //#37802 commenting this out - unfathomable why you would need
1473
//to sort the root's children in order to get its short
1474
//description - Tim
1475
// sortChildren ((VisualizerNode)getRoot ());
1476
return ((VisualizerNode) getRoot()).getShortDescription();
1477            }
1478
1479            return ""; // NOI18N
1480
}
1481
1482        // overrided mothod from DefaultTreeModel
1483
public void nodesWereInserted(TreeNode node, int[] childIndices) {
1484            super.nodesWereInserted(node, childIndices);
1485
1486            if (node instanceof VisualizerNode && isSortingActive()) {
1487                sortChildren((VisualizerNode) node, false);
1488            }
1489        }
1490
1491        // overrided mothod from DefaultTreeModel
1492
public void nodesChanged(TreeNode node, int[] childIndices) {
1493            super.nodesChanged(node, childIndices);
1494
1495            if ((node != null) && (childIndices != null) && isSortingActive()) {
1496                sortChildren((VisualizerNode) node, false);
1497            }
1498        }
1499
1500        // overrided mothod from DefaultTreeModel
1501
public void setRoot(TreeNode root) {
1502            super.setRoot(root);
1503
1504            if (root instanceof VisualizerNode && isSortingActive()) {
1505                sortChildren((VisualizerNode) root, false);
1506            }
1507        }
1508
1509        private class SortingTask implements Runnable JavaDoc {
1510            private HashSet JavaDoc<VisualizerNode> toSort = new HashSet JavaDoc<VisualizerNode>();
1511
1512            public synchronized void add(VisualizerNode parent) {
1513                toSort.add(parent);
1514            }
1515
1516            public synchronized void remove(VisualizerNode parent) {
1517                toSort.remove(parent);
1518            }
1519
1520            public synchronized boolean isEmpty() {
1521                return toSort.isEmpty();
1522            }
1523
1524            public void run() {
1525                synchronized (SortedNodeTreeModel.this) {
1526                    SortedNodeTreeModel.this.sortingTask = null;
1527                }
1528
1529                for (Iterator JavaDoc<VisualizerNode> i = toSort.iterator(); i.hasNext();) {
1530                    VisualizerNode curr = i.next();
1531                    SortedNodeTreeModel.this.doSortChildren(curr);
1532                }
1533            }
1534        }
1535    }
1536
1537    /* Cell renderer for sorting column header.
1538     */

1539    private class SortingHeaderRenderer extends DefaultTableCellRenderer {
1540        SortingHeaderRenderer() {
1541        }
1542
1543        /** Overrides superclass method. */
1544        public Component getTableCellRendererComponent(
1545            JTable table, Object JavaDoc value, boolean isSelected, boolean hasFocus, int row, int column
1546        ) {
1547            Component comp = defaultHeaderRenderer.getTableCellRendererComponent(
1548                    table, value, isSelected, hasFocus, row, column
1549                );
1550
1551            if (comp instanceof JLabel) {
1552                if ((column == 0) && treeColumnProperty.isSortingColumn()) {
1553                    ((JLabel) comp).setIcon(getProperIcon(treeColumnProperty.isSortOrderDescending()));
1554                    ((JLabel) comp).setHorizontalTextPosition(SwingConstants.LEFT);
1555
1556                    // don't use deriveFont() - see #49973 for details
1557
comp.setFont(new Font(comp.getFont().getName(), Font.BOLD, comp.getFont().getSize()));
1558                } else if ((column != 0) && ((tableModel.getVisibleSortingColumn() + 1) == column)) {
1559                    ((JLabel) comp).setIcon(getProperIcon(tableModel.isSortOrderDescending()));
1560                    ((JLabel) comp).setHorizontalTextPosition(SwingConstants.LEFT);
1561
1562                    // don't use deriveFont() - see #49973 for details
1563
comp.setFont(new Font(comp.getFont().getName(), Font.BOLD, comp.getFont().getSize()));
1564                } else {
1565                    ((JLabel) comp).setIcon(null);
1566                }
1567            }
1568
1569            return comp;
1570        }
1571
1572        private ImageIcon getProperIcon(boolean descending) {
1573            if (descending) {
1574                return new ImageIcon(org.openide.util.Utilities.loadImage(SORT_DESC_ICON));
1575            } else {
1576                return new ImageIcon(org.openide.util.Utilities.loadImage(SORT_ASC_ICON));
1577            }
1578        }
1579    }
1580     // End of inner class SortingHeaderRenderer.
1581

1582    private static class TreeColumnProperty {
1583        private Property p = null;
1584
1585        TreeColumnProperty() {
1586        }
1587
1588        void setProperty(Property p) {
1589            this.p = p;
1590        }
1591
1592        boolean isComparable() {
1593            if (p == null) {
1594                return false;
1595            }
1596
1597            Object JavaDoc o = p.getValue(NodeTableModel.ATTR_COMPARABLE_COLUMN);
1598
1599            if ((o != null) && o instanceof Boolean JavaDoc) {
1600                return ((Boolean JavaDoc) o).booleanValue();
1601            }
1602
1603            return false;
1604        }
1605
1606        boolean isSortingColumn() {
1607            if (p == null) {
1608                return false;
1609            }
1610
1611            Object JavaDoc o = p.getValue(NodeTableModel.ATTR_SORTING_COLUMN);
1612
1613            if ((o != null) && o instanceof Boolean JavaDoc) {
1614                return ((Boolean JavaDoc) o).booleanValue();
1615            }
1616
1617            return false;
1618        }
1619
1620        void setSortingColumn(boolean sorting) {
1621            if (p == null) {
1622                return;
1623            }
1624
1625            p.setValue(NodeTableModel.ATTR_SORTING_COLUMN, sorting ? Boolean.TRUE : Boolean.FALSE);
1626        }
1627
1628        boolean isSortOrderDescending() {
1629            if (p == null) {
1630                return false;
1631            }
1632
1633            Object JavaDoc o = p.getValue(NodeTableModel.ATTR_DESCENDING_ORDER);
1634
1635            if ((o != null) && o instanceof Boolean JavaDoc) {
1636                return ((Boolean JavaDoc) o).booleanValue();
1637            }
1638
1639            return false;
1640        }
1641
1642        void setSortOrderDescending(boolean descending) {
1643            if (p == null) {
1644                return;
1645            }
1646
1647            p.setValue(NodeTableModel.ATTR_DESCENDING_ORDER, descending ? Boolean.TRUE : Boolean.FALSE);
1648        }
1649    }
1650
1651    /* For testing - use internal execution
1652    public static void main(String[] args) {
1653        SwingUtilities.invokeLater(new Runnable() {
1654            public void run() {
1655                Node n = //new org.netbeans.core.ModuleNode();
1656                    RepositoryNodeFactory.getDefault().repository(DataFilter.ALL);
1657
1658                org.openide.explorer.ExplorerManager em = new org.openide.explorer.ExplorerManager();
1659                em.setRootContext(n);
1660
1661                org.openide.explorer.ExplorerPanel ep = new org.openide.explorer.ExplorerPanel(em);
1662                ep.setLayout (new BorderLayout ());
1663                ep.setBorder(new EmptyBorder(20, 20, 20, 20));
1664
1665                TreeTableView ttv = new TreeTableView();
1666                ttv.setRootVisible(false);
1667                ttv.setPopupAllowed(true);
1668                ttv.setDefaultActionAllowed(true);
1669                ttv.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS );
1670                ttv.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
1671
1672                org.openide.nodes.PropertySupport.ReadOnly prop2
1673                    = new org.openide.nodes.PropertySupport.ReadOnly (
1674                            "name", // NOI18N
1675                            String.class,
1676                            "name",
1677                            "Name Tooltip"
1678                        ) {
1679                            public Object getValue () {
1680                                return null;
1681                            }
1682
1683                        };
1684                //prop2.setValue( "InvisibleInTreeTableView", Boolean.TRUE );
1685                prop2.setValue( "SortingColumnTTV", Boolean.TRUE );
1686                prop2.setValue( "DescendingOrderTTV", Boolean.TRUE );
1687                prop2.setValue( "ComparableColumnTTV", Boolean.TRUE );
1688
1689                ttv.setProperties(
1690    // n.getChildren().getNodes()[0].getPropertySets()[0].getProperties());
1691                    new Property[]{
1692                        new org.openide.nodes.PropertySupport.ReadWrite (
1693                            "hidden", // NOI18N
1694                            Boolean.TYPE,
1695                            "hidden",
1696                            "Hidden tooltip"
1697                        ) {
1698                            public Object getValue () {
1699                                return null;
1700                            }
1701
1702                            public void setValue (Object o) {
1703                            }
1704                        },
1705                        prop2,
1706                        new org.openide.nodes.PropertySupport.ReadOnly (
1707                            "template", // NOI18N
1708                            Boolean.TYPE,
1709                            "template",
1710                            "Template Tooltip"
1711                        ) {
1712                            public Object getValue () {
1713                                return null;
1714                            }
1715
1716                        }
1717
1718                    }
1719                );
1720                ttv.setTreePreferredWidth(200);
1721
1722                ttv.setTableColumnPreferredWidth(0, 60);
1723                ttv.setTableColumnPreferredWidth(1, 150);
1724                ttv.setTableColumnPreferredWidth(2, 100);
1725
1726                ep.add("Center", ttv);
1727                ep.open();
1728            }
1729        });
1730    }
1731    */

1732}
1733
Popular Tags