blob: d9991f7bf13a479e30c3b4c98394175410b86f2b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.openide.explorer.view;
import java.util.logging.Logger;
import org.openide.awt.MouseUtils;
import org.openide.nodes.Node;
import org.openide.nodes.Node.Property;
import org.openide.nodes.NodeListener;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerManager.Provider;
import org.openide.explorer.ExplorerUtils;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import javax.accessibility.AccessibleContext;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.*;
import javax.swing.plaf.metal.MetalScrollBarUI;
import javax.swing.table.*;
import javax.swing.tree.*;
import org.openide.awt.QuickSearch;
import org.openide.explorer.view.TreeView.PopupAdapter;
import org.openide.explorer.view.TreeView.PopupSupport;
import org.openide.explorer.view.TreeView.TreePropertyListener;
import org.openide.nodes.Children;
import org.openide.nodes.FilterNode;
import org.openide.nodes.NodeMemberEvent;
import org.openide.nodes.NodeReorderEvent;
import org.openide.util.Utilities;
/** Explorer view. Allows to view tree of nodes on the left
* and its properties in table on the right.
* <p>
* The main mechanism for setting what properties are displayed is
* <a href="#setProperties"><code>setProperties (Node.Property[])</code></a>.
* Pass this method an
* array of properties. These will act as a template, and properties of
* the displayed nodes which share the same <i>name</i> will be used in
* the columns of the table.
*
* You can customize behaviour
* of property columns using <code>Property.setValue (String parameter,
* Object value)</code>. For example,
* assume you have following array of properties:
* <br><code>org.openide.nodes.Node.Property[] properties</code><br>
*
* if you need second column to be initially invisible in TreeTableView, you
* should set its custom parameter:
* <br><code>properties[1].setValue ("InvisibleInTreeTableView", Boolean.TRUE);</code>
*
* <TABLE border="1" summary="custom parameter list">
* <TR>
* <TH> Parameter name
* </TH>
* <TH> Parameter type
* </TH>
* <TH> Description
* </TH>
* </TR>
* <TR>
* <TD> InvisibleInTreeTableView</TD>
* <TD> Boolean </TD>
* <TD> This property column should be initially invisible (hidden).</TD>
* </TR>
* <TR>
* <TD> ComparableColumnTTV</TD>
* <TD> Boolean </TD>
* <TD> This property column should be used for sorting.</TD>
* </TR>
* <TR>
* <TD> SortingColumnTTV</TD>
* <TD> Boolean </TD>
* <TD> TreeTableView should be initially sorted by this property column.</TD>
* </TR>
* <TR>
* <TD> DescendingOrderTTV</TD>
* <TD> Boolean </TD>
* <TD> If this parameter and <code>SortingColumnTTV</code> is set, TreeTableView should
* be initially sorted by this property columns in descending order.
* </TD>
* </TR>
* <TR>
* <TD> OrderNumberTTV</TD>
* <TD> Integer </TD>
* <TD> If this parameter is set to <code>N</code>, this property column will be
* displayed as Nth column of table. If not set, column will be
* displayed in natural order.
* </TD>
* </TR>
* <TR>
* <TD> TreeColumnTTV</TD>
* <TD> Boolean </TD>
* <TD> Identifies special property representing first (tree) column. To allow setting
* of <code>SortingColumnTTV, DescendingOrderTTV, ComparableColumnTTV</code> parameters
* also for first (tree) column, use this special parameter and add
* this property to Node.Property[] array before calling
* TreeTableView.setProperties (Node.Property[]).
* </TD>
* </TR>
* <TR>
* <TD> ColumnMnemonicCharTTV</TD>
* <TD> String </TD>
* <TD> When set, this parameter contains the mnemonic character for column's
* display name (e.g. in <I>Change Visible Columns</I> dialog window).
* If not set, no mnemonic will be displayed.
* </TD>
* </TR>
* <TR>
* <TD> ColumnDisplayNameWithMnemonicTTV</TD>
* <TD> String </TD>
* <TD> When set, this parameter contains column's display name with
* '&amp;' as the mnemonic. This parameter should be preferred over
* ColumnMnemonicCharTTV.
* </TD>
* </TR>
* </TABLE>
*
* <p>
* This class is a <q>view</q>
* to use it properly you need to add it into a component which implements
* {@link Provider}. Good examples of that can be found
* in {@link ExplorerUtils}. Then just use
* {@link Provider#getExplorerManager} call to get the {@link ExplorerManager}
* and control its state.
* </p>
* <p>
* There can be multiple <q>views</q> under one container implementing {@link Provider}. Select from
* range of predefined ones or write your own:
* </p>
* <ul>
* <li>{@link org.openide.explorer.view.BeanTreeView} - shows a tree of nodes</li>
* <li>{@link org.openide.explorer.view.ContextTreeView} - shows a tree of nodes without leaf nodes</li>
* <li>{@link org.openide.explorer.view.ListView} - shows a list of nodes</li>
* <li>{@link org.openide.explorer.view.IconView} - shows a rows of nodes with bigger icons</li>
* <li>{@link org.openide.explorer.view.ChoiceView} - creates a combo box based on the explored nodes</li>
* <li>{@link org.openide.explorer.view.TreeTableView} - shows tree of nodes together with a set of their {@link Property}</li>
* <li>{@link org.openide.explorer.view.MenuView} - can create a {@link JMenu} structure based on structure of {@link Node}s</li>
* </ul>
* <p>
* All of these views use {@link ExplorerManager#find} to walk up the AWT hierarchy and locate the
* {@link ExplorerManager} to use as a controler. They attach as listeners to
* it and also call its setter methods to update the shared state based on the
* user action. Not all views make sence together, but for example
* {@link org.openide.explorer.view.ContextTreeView} and {@link org.openide.explorer.view.ListView} were designed to complement
* themselves and behaves like windows explorer. The {@link org.openide.explorer.propertysheet.PropertySheetView}
* for example should be able to work with any other view.
* </p>
*
* @author jrojcek
* @since 1.7
*/
public class TreeTableView extends BeanTreeView {
// icon of column button
private static final String COLUMNS_ICON = "org/netbeans/modules/openide/explorer/columns.gif"; // NOI18N
// icons of ascending/descending order in column header
private static final String SORT_ASC_ICON = "org/netbeans/modules/openide/explorer/columnsSortedAsc.gif"; // NOI18N
private static final String SORT_DESC_ICON = "org/netbeans/modules/openide/explorer/columnsSortedDesc.gif"; // NOI18N
/** 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;
private TreeColumnProperty treeColumnProperty = new TreeColumnProperty();
private int treeColumnWidth;
private Component treeTableParent = null;
private QuickSearch quickSearch;
private Component searchpanel;
/** Create TreeTableView with default NodeTableModel
*/
public TreeTableView() {
this(new NodeTableModel());
}
/** Creates TreeTableView with provided NodeTableModel.
* @param ntm node table model
*/
public TreeTableView(NodeTableModel ntm) {
setLayout(new SearchScrollPaneLayout());
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);
Icon icon = ImageUtilities.image2Icon(ImageUtilities.loadImage(COLUMNS_ICON)); // NOI18N
colsButton = new javax.swing.JButton(icon);
// For HiDPI support.
colsButton.setDisabledIcon(ImageUtilities.createDisabledIcon(icon));
colsButton.getAccessibleContext().setAccessibleName(NbBundle.getMessage(TreeTableView.class, "ACN_ColumnsSelector")); //NOI18N
colsButton.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(TreeTableView.class, "ACD_ColumnsSelector")); //NOI18N
colsButton.addActionListener(
new ActionListener() {
@Override
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
}
@Override
public void setRowHeader(JViewport rowHeader) {
rowHeader.setBorder(BorderFactory.createEmptyBorder());
super.setRowHeader(rowHeader);
}
/* Overriden to allow hide special horizontal scrollbar
*/
@Override
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
*/
@Override
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);
}
@Override
protected NodeTreeModel createModel() {
return getSortedNodeTreeModel();
}
/** Requests focus for the tree component. Overrides superclass method. */
@Override
public void requestFocus() {
if (treeTable != null) {
treeTable.requestFocus();
}
}
@Override
public boolean requestFocusInWindow() {
boolean res = super.requestFocusInWindow();
//#44856: pass the focus request to the treetable as well
if (null != treeTable) {
treeTable.requestFocus();
}
return res;
}
/* Sets sorting ability
*/
private void setAllowSortingByColumn(boolean allow) {
if (allow && (allow != allowSortingByColumn)) {
addMouseListener(
new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent evt) {
// Check whether it was really a click
if (evt.getClickCount() == 0) return ;
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 {
int realIndex = tableModel.translateVisibleColumnIndex(index);
setSortingColumn(index);
setSortingOrder(true);
}
}
} else if (tableModel.isComparableColumn(index)) {
if (tableModel.isSortingColumnEx(tableModel.translateVisibleColumnIndex(index))) {
if (!tableModel.isSortOrderDescending()) {
setSortingOrder(false);
} else {
noSorting();
}
} else {
int realIndex = tableModel.translateVisibleColumnIndex(index);
setSortingColumn(realIndex);
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.getSortingColumn() == -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.
*/
@Override
void initializeTree() {
}
private final Object searchConstraints = new Object();
@Override
public void add(Component comp, Object constraints) {
if (constraints == searchConstraints) {
searchpanel = comp;
constraints = null;
}
super.add(comp, constraints);
}
@Override
public void remove(Component comp) {
if (comp == searchpanel) {
searchpanel = null;
}
super.remove(comp);
}
/** Initialize tree and treeTable.
*/
private void initializeTreeTable() {
treeModel = createModel();
TreeTable tt = new TreeTable(treeModel, tableModel);
treeTable = tt;
tree = ((TreeTable) treeTable).getTree();
TableQuickSearchSupport tqss = new TableQuickSearchSupport(tt, tt.getQuickSearchTableFilter(), tt.getQuickSearchSettings());
quickSearch = QuickSearch.attach(this, searchConstraints, tqss, tqss.createSearchPopupMenu());
tt.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
quickSearch.processKeyEvent(e);
}
@Override
public void keyPressed(KeyEvent e) {
quickSearch.processKeyEvent(e);
}
@Override
public void keyReleased(KeyEvent e) {
quickSearch.processKeyEvent(e);
}
});
defaultHeaderRenderer = treeTable.getTableHeader().getDefaultRenderer();
treeTable.getTableHeader().setDefaultRenderer(new SortingHeaderRenderer());
// init listener & attach it to closing of
managerListener = new TreePropertyListener();
tree.addTreeExpansionListener(managerListener);
defaultActionListener = new PopupSupport();
Action popupWrapper = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent evt) {
SwingUtilities.invokeLater( defaultActionListener );
}
@Override
public boolean isEnabled() {
return treeTable.isFocusOwner() || tree.isFocusOwner();
}
};
treeTable.getInputMap( JTree.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).put(
KeyStroke.getKeyStroke( KeyEvent.VK_F10, KeyEvent.SHIFT_DOWN_MASK ), "org.openide.actions.PopupAction" );
treeTable.getActionMap().put("org.openide.actions.PopupAction", popupWrapper);
tree.addMouseListener(defaultActionListener);
tableMouseListener = new MouseUtils.PopupMouseAdapter() {
@Override
public void showPopup(MouseEvent mevt) {
if (isPopupAllowed()) {
if (mevt.getY() > treeTable.getHeight()) {
// clear selection, if click under the table
treeTable.clearSelection();
} else {
int selRow = treeTable.rowAtPoint( mevt.getPoint() );
boolean isAlreadySelected = false;
int[] currentSelection = tree.getSelectionRows();
for( int i=0; null != currentSelection && i<currentSelection.length; i++ ) {
if( selRow == currentSelection[i] ) {
isAlreadySelected = true;
break;
}
}
if( !isAlreadySelected )
tree.setSelectionRow( selRow );
}
createPopup(mevt);
}
}
};
treeTable.addMouseListener(tableMouseListener);
if (UIManager.getColor("control") != null) { // NOI18N
treeTable.setGridColor(UIManager.getColor("control")); // NOI18N
}
}
@Override
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.
*/
@Override
public AccessibleContext getAccessibleContext() {
if (accessContext == null) {
accessContext = new AccessibleTreeTableView();
}
return accessContext;
}
/** 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.
*/
@Override
public void setPopupAllowed(boolean value) {
if (tree == null) {
return;
}
if ((popupListener == null) && value) {
// on
popupListener = new PopupAdapter() {
@Override
protected void showPopup(MouseEvent e) {
int selRow = tree.getClosestRowForLocation(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.
*/
@Override
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));
}
}
@Override
public boolean isQuickSearchAllowed() {
return quickSearch.isEnabled();
}
@Override
public void setQuickSearchAllowed(boolean allowedQuickSearch) {
quickSearch.setEnabled(allowedQuickSearch);
}
/** 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.getSortingColumn();
if (index != -1) {
getSortedNodeTreeModel().setSortedByProperty(
tableModel.propertyForColumnEx(index), !tableModel.isSortOrderDescending()
);
}
}
}
}
/** Sets resize mode of table.
*
* @param mode - One of 5 legal values: <pre>JTable.AUTO_RESIZE_OFF,
* JTable.AUTO_RESIZE_NEXT_COLUMN,
* JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS,
* JTable.AUTO_RESIZE_LAST_COLUMN,
* JTable.AUTO_RESIZE_ALL_COLUMNS</pre>
*/
public final void setTableAutoResizeMode(int mode) {
treeTable.setAutoResizeMode(mode);
}
/** Gets resize mode of table.
*
* @return mode - One of 5 legal values: <pre>JTable.AUTO_RESIZE_OFF,
* JTable.AUTO_RESIZE_NEXT_COLUMN,
* JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS,
* JTable.AUTO_RESIZE_LAST_COLUMN,
* JTable.AUTO_RESIZE_ALL_COLUMNS</pre>
*/
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();
}
@Override
public void addNotify() {
// to allow displaying popup also in blank area
if (treeTable.getParent() != null) {
treeTableParent = treeTable.getParent();
treeTableParent.addMouseListener(tableMouseListener);
}
super.addNotify();
if( tableModel.getRowCount() == 0 ) {
//re-attach node listeners
Node[] nodes = new Node[tree.getRowCount()];
for (int i = 0; i < tree.getRowCount(); i++) {
nodes[i] = Visualizer.findNode(tree.getPathForRow(i).getLastPathComponent());
}
tableModel.setNodes(nodes);
}
listener.revalidateScrollBar();
ViewUtil.adjustBackground(treeTable);
ViewUtil.adjustBackground(scrollPane);
ViewUtil.adjustBackground(scrollPane.getViewport());
}
@Override
public void removeNotify() {
super.removeNotify();
if (treeTableParent != null) { //IndexedEditorPanel
treeTableParent.removeMouseListener(tableMouseListener);
}
treeTableParent = null;
// clear node listeners
tableModel.setNodes(new Node[] { });
}
@Override
public void addMouseListener(MouseListener l) {
super.addMouseListener(l);
treeTable.getTableHeader().addMouseListener(l);
}
@Override
public void removeMouseListener(MouseListener l) {
super.removeMouseListener(l);
treeTable.getTableHeader().removeMouseListener(l);
}
/**
* Drag and drop is not supported in TreeTableView.
*/
@Override
public void setDragSource(boolean state) {
}
/**
* Drag and drop is not supported in TreeTableView.
*/
@Override
public void setDropTarget(boolean state) {
}
/* Overriden to get position for popup invoked by keyboard
*/
@Override
Point getPositionForPopup() {
int row = treeTable.getSelectedRow();
if (row < 0) {
return null;
}
int col = treeTable.getSelectedColumn();
if (col < 0) {
col = 0;
}
Rectangle r;
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) {
Point p = SwingUtilities.convertPoint(e.getComponent(), e.getX(), e.getY(), TreeTableView.this);
createPopup(p.x, p.y);
e.consume();
}
@Override
void createPopup(int xpos, int ypos) {
int treeXpos = xpos - ((TreeTable) treeTable).getPositionX();
if (allowHideColumns || allowSortingByColumn) {
int col = treeTable.getColumnModel().getColumnIndexAtX(treeXpos);
super.createExtendedPopup(xpos, ypos, getListMenu(col));
} else {
super.createPopup(xpos, ypos);
}
}
/* creates List Options menu
*/
private JMenu getListMenu(final int col) {
JMenu listItem = new JMenu(NbBundle.getMessage(NodeTableModel.class, "LBL_ListOptions"));
if (allowHideColumns && (col > 0)) {
JMenu colsItem = new JMenu(NbBundle.getMessage(NodeTableModel.class, "LBL_ColsMenu"));
boolean addColsItem = false;
if (col > 1) {
JMenuItem moveLItem = new JMenuItem(NbBundle.getMessage(NodeTableModel.class, "LBL_MoveLeft"));
moveLItem.addActionListener(
new ActionListener() {
@Override
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.getMessage(NodeTableModel.class, "LBL_MoveRight")
);
moveRItem.addActionListener(
new ActionListener() {
@Override
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.getMessage(NodeTableModel.class, "LBL_SortMenu"));
JRadioButtonMenuItem noSortItem = new JRadioButtonMenuItem(
NbBundle.getMessage(NodeTableModel.class, "LBL_NoSort"),
!getSortedNodeTreeModel().isSortingActive()
);
noSortItem.addActionListener(
new ActionListener() {
@Override
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() {
@Override
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.isSortingColumnEx(tableModel.translateVisibleColumnIndex(i))
);
colItem.setHorizontalTextPosition(SwingConstants.LEFT);
final int index = tableModel.translateVisibleColumnIndex(i);
colItem.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent actionEvent) {
setSortingColumn(index);
}
}
);
sortItem.add(colItem);
}
}
//add invisible columns
for (int i = 0; i < tableModel.getColumnCountEx(); i++) {
if (tableModel.isComparableColumnEx(i) && !tableModel.isVisibleColumnEx(i)) {
visibleComparable++;
colItem = new JRadioButtonMenuItem(tableModel.getColumnNameEx(i), tableModel.isSortingColumnEx(i));
colItem.setHorizontalTextPosition(SwingConstants.LEFT);
final int index = i;
colItem.addActionListener(
new ActionListener() {
@Override
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.getMessage(NodeTableModel.class, "LBL_Ascending"), !current_sort
);
ascItem.setHorizontalTextPosition(SwingConstants.LEFT);
ascItem.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent actionEvent) {
setSortingOrder(true);
}
}
);
sortItem.add(ascItem);
JRadioButtonMenuItem descItem = new JRadioButtonMenuItem(
NbBundle.getMessage(NodeTableModel.class, "LBL_Descending"), current_sort
);
descItem.setHorizontalTextPosition(SwingConstants.LEFT);
descItem.addActionListener(
new ActionListener() {
@Override
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.getMessage(NodeTableModel.class, "LBL_ChangeColumns"));
visItem.addActionListener(
new ActionListener() {
@Override
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.setSortingColumnEx(index);
if (index != -1) {
getSortedNodeTreeModel().setSortedByProperty(
tableModel.propertyForColumnEx(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.setSortingColumnEx(-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();
}
private synchronized SortedNodeTreeModel getSortedNodeTreeModel() {
if (sortedNodeTreeModel == null) {
sortedNodeTreeModel = new SortedNodeTreeModel();
}
return sortedNodeTreeModel;
}
/** 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() {
}
@Override
public void setAccessibleName(String accessibleName) {
super.setAccessibleName(accessibleName);
if (treeTable != null) {
treeTable.getAccessibleContext().setAccessibleName(accessibleName);
}
}
@Override
public void setAccessibleDescription(String accessibleDescription) {
super.setAccessibleDescription(accessibleDescription);
if (treeTable != null) {
treeTable.getAccessibleContext().setAccessibleDescription(accessibleDescription);
}
}
}
/* Horizontal scrolling support.
*/
private final class ScrollListener extends ComponentAdapter implements PropertyChangeListener, ChangeListener {
boolean movecorrection = false;
ScrollListener() {
}
//Column width
@Override
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
@Override
public void componentResized(ComponentEvent e) {
revalidateScrollBar();
}
//ScrollBar change
@Override
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() {
}
@Override
public void setBorder(Border b) {
//do nothing
}
@Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
@Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 10;
}
@Override
public boolean getScrollableTracksViewportHeight() {
return true;
}
@Override
public Dimension getPreferredScrollableViewportSize() {
return this.getPreferredSize();
}
@Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return 10;
}
}
@Override
public Insets getInsets() {
Insets res = getInnerInsets();
res = new Insets(res.top, res.left, res.bottom, res.right);
if( null != searchpanel && searchpanel.isVisible() ) {
res.bottom += searchpanel.getPreferredSize().height;
}
return res;
}
private Insets getInnerInsets() {
Insets res = super.getInsets();
if( null == res ) {
res = new Insets(0,0,0,0);
}
return res;
}
private class SearchScrollPaneLayout extends ScrollPaneLayout {
public SearchScrollPaneLayout() {
}
@Override
public void layoutContainer( Container parent ) {
super.layoutContainer(parent);
if( null != searchpanel && searchpanel.isVisible() ) {
Insets innerInsets = getInnerInsets();
Dimension prefSize = searchpanel.getPreferredSize();
searchpanel.setBounds(innerInsets.left, parent.getHeight()-innerInsets.bottom-prefSize.height,
parent.getWidth()-innerInsets.left-innerInsets.right, prefSize.height);
}
}
}
/** Invokes default action.
*/
private class DefaultTreeAction implements ActionListener {
DefaultTreeAction() {
}
/**
* Invoked when an action occurs.
*/
@Override
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 {
Utilities.disabledActionBeep();
}
}
}
}
}
@Override
Node getOriginalNode (Node n) {
if (n instanceof SortedNodeTreeModel.SortedNode) {
SortedNodeTreeModel.SortedNode sn = (SortedNodeTreeModel.SortedNode) n;
return sn.getOriginalNode ();
}
return n;
}
/* Synchronizes selected nodes from the manager of this Explorer.
*/
@Override
protected void showSelection(TreePath[] treePaths) {
TreePath [] modifiedTreePaths = new TreePath [treePaths.length];
for (int i = 0; i < treePaths.length; i++) {
TreePath tp = treePaths [i];
Node o = ((VisualizerNode) tp.getLastPathComponent ()).node;
TreePath mtp = getTreePath (getSortedNodeFromOriginal (o));
modifiedTreePaths [i] = mtp;
}
super.showSelection (modifiedTreePaths);
}
@Override
public void collapseNode(Node n) {
super.collapseNode(getSortedNodeFromOriginal(n));
}
@Override
public void expandNode(Node n) {
super.expandNode(getSortedNodeFromOriginal(n));
}
@Override
public boolean isExpanded(Node n) {
return super.isExpanded(getSortedNodeFromOriginal(n));
}
private Node getSortedNodeFromOriginal (Node orig) {
if (getSortedNodeTreeModel () != null) {
if (getSortedNodeTreeModel ().original2filter != null) {
SortedNodeTreeModel.SortedNode sn = getSortedNodeTreeModel ().original2filter.get (orig);
if (sn != null) {
return sn;
}
}
}
return orig;
}
/* node tree model with added sorting support
*/
private class SortedNodeTreeModel extends NodeTreeModel {
private Node.Property sortedByProperty;
private boolean sortAscending = true;
private Comparator<Node> rowComparator;
private boolean sortedByName = false;
private Map<Node, SortedNode> original2filter = new WeakHashMap<Node, SortedNode> (11);
@Override
void setNode (Node root, TreeView.VisualizerHolder visHolder) {
visHolder.clear ();
original2filter.clear ();
super.setNode (new SortedNode (root), null);
}
private class SortedNode extends FilterNode {
public SortedNode (Node original) {
super(original, original.isLeaf() ? Children.LEAF : new SortedChildren(original));
original2filter.put (original, this);
}
public Node getOriginalNode () {
return super.getOriginal ();
}
@Override
protected NodeListener createNodeListener() {
return new FilterNode.NodeAdapter(this) {
@Override
protected void propertyChange(FilterNode fn, PropertyChangeEvent ev) {
super.propertyChange(fn, ev);
if (ev.getPropertyName().equals(Node.PROP_LEAF)) {
final org.openide.nodes.Children[] newChildren = new org.openide.nodes.Children[1];
Children.MUTEX.readAccess(new Runnable() {
@Override
public void run() {
boolean origIsLeaf = getOriginal().isLeaf();
boolean thisIsLeaf = isLeaf();
if (origIsLeaf && !thisIsLeaf) {
newChildren[0] = Children.LEAF;
} else if (!origIsLeaf && thisIsLeaf) {
newChildren[0] = new SortedChildren(getOriginal());
}
}
});
if (newChildren[0] != null) {
Children.MUTEX.postWriteRequest(
new Runnable() {
@Override
public void run() {
setChildren(newChildren[0]);
}
}
);
}
}
if (ev.getPropertyName().equals(Node.PROP_PARENT_NODE)) {
final Node node = (Node)ev.getSource();
if (node.getParentNode() == null) {
original2filter.remove(node);
}
}
}
};
}
}
private class SortedChildren extends FilterNode.Children {
public SortedChildren (Node n) {
super (n);
sortNodes ();
}
@Override
protected Node[] createNodes (Node key) {
return new Node [] { new SortedNode (key) };
}
@Override
protected void addNotify () {
super.addNotify ();
sortNodes ();
}
@Override
protected void filterChildrenAdded (NodeMemberEvent ev) {
super.filterChildrenAdded (ev);
sortNodes ();
}
@Override
protected void filterChildrenRemoved (NodeMemberEvent ev) {
super.filterChildrenRemoved (ev);
sortNodes ();
}
@Override
protected void filterChildrenReordered (NodeReorderEvent ev) {
super.filterChildrenReordered (ev);
sortNodes ();
}
private void sortNodes() {
Node[] origNodes = original.getChildren().getNodes();
if (isSortingActive()) {
Node[] sortedNodes = new Node[origNodes.length];
System.arraycopy(origNodes, 0, sortedNodes, 0, origNodes.length);
Collections.sort(Arrays.asList(sortedNodes), getRowComparator());
setKeys(sortedNodes);
} else {
setKeys(origNodes);
}
}
}
void setNoSorting() {
setSortedByProperty(null);
setSortedByName(false);
sortingChanged();
}
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<Node> getRowComparator() {
if (rowComparator == null) {
rowComparator = new Comparator<Node>() {
@Override
@SuppressWarnings("unchecked")
public int compare(Node n1, Node n2) {
if (n1 == n2) {
return 0;
}
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
Logger.getAnonymousLogger().warning("TTV.compare: Node " +
n1 + " or " + n2 +
" has no parent!");
return 0;
}
if (!(n1.getParentNode().equals(n2.getParentNode()))) {
// PENDING: throw Exception
Logger.getAnonymousLogger().warning("TTV.compare: Nodes " +
n1 + " and " +
n2 +
" has different parent!");
return 0;
}
int res;
if (sortedByName) {
res = n1.getDisplayName().compareTo(n2.getDisplayName());
return sortAscending ? res
: (-res);
}
Property p1 = getNodeProperty(n1, sortedByProperty);
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) {
Logger.getLogger(TreeTableView.class.getName()).log(Level.WARNING, null, ex);
return 0;
}
}
};
}
return rowComparator;
}
void sortingChanged() {
// PENDING: remember the last sorting to avoid multiple sorting
// remenber expanded folders
TreeNode tn = (TreeNode) (this.getRoot());
java.util.List<TreePath> list = new ArrayList<TreePath>();
Enumeration<TreePath> en = TreeTableView.this.tree.getExpandedDescendants(new TreePath(tn));
while ((en != null) && en.hasMoreElements()) {
TreePath path = en.nextElement();
// bugfix #32328, don't sort whole subtree but only expanded folders
Node n = ((VisualizerNode) path.getLastPathComponent ()).node;
Children children = n.getChildren();
if (children instanceof SortedChildren) {
((SortedChildren) children).sortNodes ();
list.add(path);
} // else Children.LEAF
}
// expand again folders
for (int i = 0; i < list.size(); i++) {
TreeTableView.this.tree.expandPath(list.get(i));
}
}
String getRootDescription() {
if (getRoot() instanceof VisualizerNode) {
//#37802 commenting this out - unfathomable why you would need
//to sort the root's children in order to get its short
//description - Tim
// sortChildren ((VisualizerNode)getRoot ());
return ((VisualizerNode) getRoot()).getShortDescription();
}
return ""; // NOI18N
}
}
/* Cell renderer for sorting column header.
*/
private class SortingHeaderRenderer extends DefaultTableCellRenderer {
SortingHeaderRenderer() {
}
/** Overrides superclass method. */
@Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column
) {
Component comp = defaultHeaderRenderer.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column
);
if (comp instanceof JLabel) {
if ((column == 0) && treeColumnProperty.isSortingColumn()) {
((JLabel) comp).setIcon(getProperIcon(treeColumnProperty.isSortOrderDescending()));
((JLabel) comp).setHorizontalTextPosition(SwingConstants.LEFT);
if( Utilities.isWindows() ) {
comp.setFont(getFont().deriveFont(Font.BOLD, getFont().getSize()));
} else {
// don't use deriveFont() - see #49973 for details
comp.setFont(new Font(getFont().getName(), Font.BOLD, getFont().getSize()));
}
} else if ((column != 0) && ((tableModel.getVisibleSortingColumn() + 1) == column)) {
((JLabel) comp).setIcon(getProperIcon(tableModel.isSortOrderDescending()));
((JLabel) comp).setHorizontalTextPosition(SwingConstants.LEFT);
if( Utilities.isWindows() ) {
comp.setFont(getFont().deriveFont(Font.BOLD, getFont().getSize()));
} else {
// don't use deriveFont() - see #49973 for details
comp.setFont(new Font(getFont().getName(), Font.BOLD, getFont().getSize()));
}
} else {
((JLabel) comp).setIcon(null);
}
}
return comp;
}
private ImageIcon getProperIcon(boolean descending) {
if (descending) {
return ImageUtilities.loadImageIcon(SORT_DESC_ICON, false);
} else {
return ImageUtilities.loadImageIcon(SORT_ASC_ICON, false);
}
}
}
// End of inner class SortingHeaderRenderer.
private static class TreeColumnProperty {
private Property p = null;
TreeColumnProperty() {
}
void setProperty(Property p) {
this.p = p;
}
boolean isComparable() {
if (p == null) {
return false;
}
Object o = p.getValue(NodeTableModel.ATTR_COMPARABLE_COLUMN);
if ((o != null) && o instanceof Boolean) {
return ((Boolean) o).booleanValue();
}
return false;
}
boolean isSortingColumn() {
if (p == null) {
return false;
}
Object o = p.getValue(NodeTableModel.ATTR_SORTING_COLUMN);
if ((o != null) && o instanceof Boolean) {
return ((Boolean) o).booleanValue();
}
return false;
}
void setSortingColumn(boolean sorting) {
if (p == null) {
return;
}
p.setValue(NodeTableModel.ATTR_SORTING_COLUMN, sorting ? Boolean.TRUE : Boolean.FALSE);
}
boolean isSortOrderDescending() {
if (p == null) {
return false;
}
Object o = p.getValue(NodeTableModel.ATTR_DESCENDING_ORDER);
if ((o != null) && o instanceof Boolean) {
return ((Boolean) o).booleanValue();
}
return false;
}
void setSortOrderDescending(boolean descending) {
if (p == null) {
return;
}
p.setValue(NodeTableModel.ATTR_DESCENDING_ORDER, descending ? Boolean.TRUE : Boolean.FALSE);
}
}
/* For testing - use internal execution
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Node n = //new org.netbeans.core.ModuleNode();
RepositoryNodeFactory.getDefault().repository(DataFilter.ALL);
org.openide.explorer.ExplorerManager em = new org.openide.explorer.ExplorerManager();
em.setRootContext(n);
org.openide.explorer.ExplorerPanel ep = new org.openide.explorer.ExplorerPanel(em);
ep.setLayout (new BorderLayout ());
ep.setBorder(new EmptyBorder(20, 20, 20, 20));
TreeTableView ttv = new TreeTableView();
ttv.setRootVisible(false);
ttv.setPopupAllowed(true);
ttv.setDefaultActionAllowed(true);
ttv.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS );
ttv.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
org.openide.nodes.PropertySupport.ReadOnly prop2
= new org.openide.nodes.PropertySupport.ReadOnly (
"name", // NOI18N
String.class,
"name",
"Name Tooltip"
) {
public Object getValue () {
return null;
}
};
//prop2.setValue( "InvisibleInTreeTableView", Boolean.TRUE );
prop2.setValue( "SortingColumnTTV", Boolean.TRUE );
prop2.setValue( "DescendingOrderTTV", Boolean.TRUE );
prop2.setValue( "ComparableColumnTTV", Boolean.TRUE );
ttv.setProperties(
// n.getChildren().getNodes()[0].getPropertySets()[0].getProperties());
new Property[]{
new org.openide.nodes.PropertySupport.ReadWrite (
"hidden", // NOI18N
Boolean.TYPE,
"hidden",
"Hidden tooltip"
) {
public Object getValue () {
return null;
}
public void setValue (Object o) {
}
},
prop2,
new org.openide.nodes.PropertySupport.ReadOnly (
"template", // NOI18N
Boolean.TYPE,
"template",
"Template Tooltip"
) {
public Object getValue () {
return null;
}
}
}
);
ttv.setTreePreferredWidth(200);
ttv.setTableColumnPreferredWidth(0, 60);
ttv.setTableColumnPreferredWidth(1, 150);
ttv.setTableColumnPreferredWidth(2, 100);
ep.add("Center", ttv);
ep.open();
}
});
}
*/
}