/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide.explorer.view;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.TreeMap;
import java.util.Iterator;
import org.openide.nodes.Node;
import org.openide.nodes.Node.Property;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.util.NbBundle;
/**
* Table model with properties (<code>Node.Property</code>) as columns and nodes (<code>Node</code>) as rows.
* It is used as model for displaying node properties in table. Each column is represented by
* <code>Node.Property</code> object. Each row is represented by <code>Node</code> object.
* Each cell contains <code>Node.Property</code> property which equals with column object
* and should be in property sets of row representant (<code>Node</code>).
*
* @author Jan Rojcek
* @since 1.7
*/
public class NodeTableModel extends AbstractTableModel {
private static final String ATTR_INVISIBLE = "InvisibleInTreeTableView"; // NOI18N
static final String ATTR_COMPARABLE_COLUMN = "ComparableColumnTTV"; // NOI18N
static final String ATTR_SORTING_COLUMN = "SortingColumnTTV"; // NOI18N
static final String ATTR_DESCENDING_ORDER = "DescendingOrderTTV"; // NOI18N
private static final String ATTR_ORDER_NUMBER = "OrderNumberTTV"; // NOI18N
private static final String ATTR_TREE_COLUMN = "TreeColumnTTV"; // NOI18N
/** all columns of model */
private ArrayColumn[] allPropertyColumns = new ArrayColumn[]{};
/** visible columns of model */
private int[] propertyColumns = new int[]{};
/** rows of model */
private Node[] nodeRows = new Node[]{};
/** sorting column */
private int sortColumn = -1;
/** if true, at least one column can be used to sort */
private boolean existsComparableColumn = false;
private Property treeColumnProperty = null;
/** listener on node properties changes, recreates displayed data */
private PropertyChangeListener pcl = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
//fireTableDataChanged();
int row = rowForNode((Node)evt.getSource());
if (row == -1) {
return;
}
int column = columnForProperty(evt.getPropertyName());
if (column == -1) {
fireTableRowsUpdated(row, row);
} else {
fireTableCellUpdated(row, column);
}
}
};
/** Set rows.
* @param nodes the rows
*/
public void setNodes(Node[] nodes) {
for (int i = 0; i < nodeRows.length; i++)
nodeRows[i].removePropertyChangeListener(pcl);
nodeRows = nodes;
for (int i = 0; i < nodeRows.length; i++)
nodeRows[i].addPropertyChangeListener(pcl);
fireTableDataChanged();
}
/** Set columns.
* @param props the columns
*/
public void setProperties(Property[] props) {
int size = props.length;
sortColumn = -1;
int treePosition = -1;
for ( int i=0; i<props.length; i++ ) {
Object o = props[i].getValue( ATTR_TREE_COLUMN );
boolean x;
if ( o != null && o instanceof Boolean )
if ( ((Boolean)o).booleanValue() ) {
treeColumnProperty = props[i];
size--;
treePosition = i;
}
}
allPropertyColumns = new ArrayColumn[size];
int visibleCount = 0;
existsComparableColumn = false;
TreeMap sort = new TreeMap();
int i = 0;
int ia = 0;
while ( i < props.length ) {
if ( i != treePosition ) {
allPropertyColumns[ia] = new ArrayColumn();
allPropertyColumns[ia].setProperty( props[i] );
if ( isVisible(props[i]) ) {
visibleCount++;
Object o = props[i].getValue( ATTR_ORDER_NUMBER );
if ( o != null && o instanceof Integer )
sort.put( new Double(((Integer)o).doubleValue()), new Integer( ia ) );
else
sort.put( new Double( ia + 0.1 ), new Integer( ia ) );
}
else {
allPropertyColumns[ia].setVisibleIndex( -1 );
Object o = props[i].getValue( ATTR_SORTING_COLUMN );
if ( o != null && o instanceof Boolean )
props[i].setValue( ATTR_SORTING_COLUMN, Boolean.FALSE );
}
if ( !existsComparableColumn ) {
Object o = props[i].getValue( ATTR_COMPARABLE_COLUMN );
if ( o != null && o instanceof Boolean )
existsComparableColumn = ((Boolean)o).booleanValue();
}
ia++;
}
i++;
}
// visible columns
propertyColumns = new int[visibleCount];
int j=0;
Iterator it = sort.values().iterator();
while ( it.hasNext() ) {
i = ((Integer)it.next()).intValue();
allPropertyColumns[i].setVisibleIndex( j );
propertyColumns[j] = i;
j++;
}
fireTableStructureChanged();
}
/* recompute set of visible columns
*/
private void computeVisiblePorperties(int visCount) {
propertyColumns = new int[visCount];
TreeMap sort = new TreeMap();
for ( int i=0; i<allPropertyColumns.length; i++ ) {
int vi = allPropertyColumns[i].getVisibleIndex();
if ( vi == -1 )
sort.put( new Double( i - 0.1 ), new Integer( i ) );
else
sort.put( new Double( vi ), new Integer( i ) );
}
int j=0;
Iterator it = sort.values().iterator();
while ( it.hasNext() ) {
int i = ((Integer)it.next()).intValue();
Property p = allPropertyColumns[i].getProperty();
if ( isVisible( p ) ) {
propertyColumns[j] = i;
allPropertyColumns[i].setVisibleIndex( j );
j++;
}
else {
allPropertyColumns[i].setVisibleIndex( -1 );
Object o = p.getValue( ATTR_SORTING_COLUMN );
if ( o != null && o instanceof Boolean )
if ( ((Boolean)o).booleanValue() ) {
p.setValue( ATTR_SORTING_COLUMN, Boolean.FALSE );
p.setValue( ATTR_DESCENDING_ORDER, Boolean.FALSE );
}
}
}
fireTableStructureChanged();
}
/** Get width of visible column.
* @param column number
* @return column width
*/
int getVisibleColumnWidth(int column) {
return allPropertyColumns[propertyColumns[column]].getWidth();
}
/** Get width of column from whole property set
* @param column number
* @return column width
*/
int getArrayColumnWidth(int column) {
return allPropertyColumns[column].getWidth();
}
/** Set width of visible column.
* @param column number
* @param column width
*/
void setVisibleColumnWidth(int column, int width) {
allPropertyColumns[propertyColumns[column]].setWidth( width );
}
/** Set width of column from whole property set
* @param column number
* @param column width
*/
void setArrayColumnWidth(int column, int width) {
allPropertyColumns[column].setWidth( width );
}
/** Get index of visible column
* @param column number from whole property set
* @return column index
*/
int getVisibleIndex(int arrayIndex) {
return allPropertyColumns[arrayIndex].getVisibleIndex();
}
/** Get index of visible column
* @param column number from whole property set
* @return column index
*/
int getArrayIndex(int visibleIndex) {
for ( int i = 0; i < allPropertyColumns.length; i++ ) {
if ( allPropertyColumns[i].getVisibleIndex() == visibleIndex )
return i;
}
return -1;
}
/* If true, column property should be comparable - allows sorting
*/
boolean isComparableColumn(int column) {
Property p = allPropertyColumns[propertyColumns[column]].getProperty();
Object o = p.getValue( ATTR_COMPARABLE_COLUMN );
if ( o != null && o instanceof Boolean )
return ((Boolean)o).booleanValue();
return false;
}
/* If true, at least one column is comparable
*/
boolean existsComparableColumn() {
return existsComparableColumn;
}
/* If true, column is currently used for sorting
*/
boolean isSortingColumn(int column) {
Property p = allPropertyColumns[propertyColumns[column]].getProperty();
Object o = p.getValue( ATTR_SORTING_COLUMN );
if ( o != null && o instanceof Boolean )
return ((Boolean)o).booleanValue();
return false;
}
/* Sets column to be currently used for sorting
*/
void setSortingColumn(int column) {
if ( sortColumn != -1 ) {
Property p = allPropertyColumns[ sortColumn ].getProperty();
p.setValue( ATTR_SORTING_COLUMN, Boolean.FALSE );
p.setValue( ATTR_DESCENDING_ORDER, Boolean.FALSE );
}
if ( column != -1 ) {
sortColumn = propertyColumns[column];
Property p = allPropertyColumns[ sortColumn ].getProperty();
p.setValue( ATTR_SORTING_COLUMN, Boolean.TRUE );
}
else
sortColumn = -1;
}
/* Gets column index of sorting column, if it's visible.
* Otherwise returns -1.
*/
int getVisibleSortingColumn() {
if ( sortColumn == -1 ) {
for (int i = 0; i < propertyColumns.length; i++) {
if ( isSortingColumn( i ) ) {
sortColumn = propertyColumns[i];
return i;
}
}
}
else {
if ( isVisible( allPropertyColumns[ sortColumn ].getProperty() ) )
return getVisibleIndex( sortColumn );
sortColumn = -1;
}
return -1;
}
/* If true, current sorting uses descending order.
*/
boolean isSortOrderDescending() {
if ( sortColumn == -1 )
return false;
Property p = allPropertyColumns[ sortColumn ].getProperty();
Object o = p.getValue( ATTR_DESCENDING_ORDER );
if ( o != null && o instanceof Boolean )
return ((Boolean)o).booleanValue();
return false;
}
/* Sets sorting order for current sorting.
*/
void setSortOrderDescending(boolean descending) {
if ( sortColumn != -1 ) {
Property p = allPropertyColumns[ sortColumn ].getProperty();
p.setValue( ATTR_DESCENDING_ORDER, descending ? Boolean.TRUE : Boolean.FALSE );
}
}
/** Returns node property if found in nodes property sets. Could be overriden to
* return property which is not in nodes property sets.
* @param node represents single row
* @param prop represents column
* @return nodes property
*/
protected Property getPropertyFor(Node node, Property prop) {
Node.PropertySet[] propSets = node.getPropertySets();
for (int i = 0; i < propSets.length; i++) {
Node.Property[] props = propSets[i].getProperties();
for (int j = 0; j < props.length; j++) {
if (prop.equals(props[j]))
return props[j];
}
}
return null;
}
/** Helper method to ask for a node representant of row.
*/
Node nodeForRow(int row) {
return nodeRows[row];
}
/** Helper method to ask for a property representant of column.
*/
Property propertyForColumn(int column) {
if ( column == -1 )
return treeColumnProperty;
else
return allPropertyColumns[propertyColumns[column]].getProperty();
}
int getArrayColumnCount() {
return allPropertyColumns.length;
}
private int rowForNode(Node node) {
for (int i = 0; i < nodeRows.length; i++) {
if (node.equals(nodeRows[i]))
return i;
}
return -1;
}
private int columnForProperty(String propName) {
for (int i = 0; i < propertyColumns.length; i++) {
if (allPropertyColumns[propertyColumns[i]].getProperty().getName().equals(propName))
return i;
}
return -1;
}
/** Helper method to ask if column representing a property should be
* visible
*/
private boolean isVisible(Property p) {
Object o = p.getValue( ATTR_INVISIBLE );
if ( o != null && o instanceof Boolean )
return !((Boolean)o).booleanValue();
return true;
}
/** Set column representing a property to be visible
*/
private void setVisible(Property p, boolean visible) {
p.setValue( ATTR_INVISIBLE, !visible ? Boolean.TRUE : Boolean.FALSE );
}
//
// TableModel methods
//
/** Getter for row count.
* @return row count
*/
public int getRowCount() {
return nodeRows.length;
}
/** Getter for column count.
* @return column count
*/
public int getColumnCount() {
return propertyColumns.length;
}
/** Getter for property.
* @param row table row index
* @param column table column index
* @return property at (row, column)
*/
public Object getValueAt(int row, int column) {
return getPropertyFor(nodeRows[row], allPropertyColumns[propertyColumns[column]].getProperty());
}
/** Cell is editable only if it has non null value.
* @param row table row index
* @param column table column index
* @return true if cell contains non null value
*/
public boolean isCellEditable(int row, int column) {
return getValueAt(row, column) != null;
}
/** Getter for column class.
* @param column table column index
* @return <code>Node.Property.class</code>
*/
public Class getColumnClass(int column) {
return Node.Property.class;
}
/** Getter for column name
* @param column table column index
* @return display name of property which represents column
*/
public String getColumnName(int column) {
return allPropertyColumns[propertyColumns[column]].getProperty().getDisplayName();
}
/* display panel to set/unset set of visible columns
*/
boolean selectVisibleColumns(String viewName, String treeColumnName, String treeColumnDesc) {
boolean changed = false;
javax.swing.JPanel panel = new javax.swing.JPanel();
panel.setLayout(new GridBagLayout());
ArrayList boxes = new ArrayList(allPropertyColumns.length);
boolean[] oldvalues = new boolean[ allPropertyColumns.length ];
int[] sortpointer = new int[ allPropertyColumns.length ];
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(0, 12, 0, 12);
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.weightx = 1.0;
GridBagConstraints labelConstraints = new GridBagConstraints();
labelConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
labelConstraints.anchor = java.awt.GridBagConstraints.WEST;
labelConstraints.insets = new java.awt.Insets(12, 12, 0, 12);
labelConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
labelConstraints.weightx = 1.0;
JLabel desc = new JLabel( NbBundle.getBundle(NodeTableModel.class).getString("LBL_ColumnDialogDesc") );
panel.add(desc, labelConstraints);
GridBagConstraints firstConstraints = new GridBagConstraints();
firstConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
firstConstraints.anchor = java.awt.GridBagConstraints.WEST;
firstConstraints.insets = new java.awt.Insets(12, 12, 0, 12);
firstConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
firstConstraints.weightx = 1.0;
JCheckBox first = new JCheckBox( treeColumnName + ": " + treeColumnDesc, true ); // NOI18N
first.setEnabled( false );
panel.add(first, firstConstraints);
String boxtext;
TreeMap sort = new TreeMap();
for (int i = 0; i < allPropertyColumns.length; i++) {
oldvalues[i] = isVisible( allPropertyColumns[i].getProperty() );
boxtext = allPropertyColumns[i].getProperty().getDisplayName()
+ ": " + allPropertyColumns[i].getProperty().getShortDescription(); // NOI18N
sort.put( boxtext, new Integer( i ));
}
Iterator it = sort.keySet().iterator();
int j = 0;
while ( it.hasNext() ) {
boxtext = ((String)it.next());
int i = ((Integer)sort.get( boxtext )).intValue();
JCheckBox b = new JCheckBox(
boxtext,
oldvalues[i] );
sortpointer[j] = i;
panel.add(b, gridBagConstraints);
boxes.add(b);
j++;
}
String title = NbBundle.getBundle(NodeTableModel.class).getString("LBL_ColumnDialogTitle");
if ( viewName != null && viewName.length() > 0 )
title = viewName + " - " + title; // NOI18N
DialogDescriptor dlg = new DialogDescriptor(
panel,
title,
true,
DialogDescriptor.OK_CANCEL_OPTION,
DialogDescriptor.OK_OPTION,
DialogDescriptor.DEFAULT_ALIGN,
null,
null
);
final Dialog dialog = DialogDisplayer.getDefault().createDialog(dlg);
dialog.show();
if (dlg.getValue().equals( DialogDescriptor.OK_OPTION )) {
int num = boxes.size();
int nv = 0;
for ( int i = 0; i < num; i++ ) {
JCheckBox b = (JCheckBox)boxes.get(i);
j = sortpointer[i];
if ( b.isSelected() != oldvalues[j] ) {
setVisible( allPropertyColumns[j].getProperty(), b.isSelected() );
changed = true;
}
if ( b.isSelected() ) {
nv++;
}
}
// Don't allow the user to disable ALL columns
/*
if (nv == 0) {
setVisible( allPropertyColumns[0].getProperty(), true );
nv = 1;
}
*/
if ( changed )
computeVisiblePorperties( nv );
}
return changed;
}
void moveColumn(int from, int to) {
int i = propertyColumns[ from ];
int j = propertyColumns[ to ];
propertyColumns[ from ] = j;
propertyColumns[ to ] = i;
allPropertyColumns[i].setVisibleIndex( to );
allPropertyColumns[j].setVisibleIndex( from );
sortColumn = -1;
}
/* class representing property column
*/
private class ArrayColumn {
ArrayColumn() {}
/** Property representing column */
private Property property;
/** Preferred width of column */
private int width;
/** Column index in table, if it's visible */
private int visibleIndex;
/** Getter for property property.
* @return Value of property property.
*/
public Property getProperty() {
return this.property;
}
/** Setter for property property.
* @param property New value of property property.
*/
public void setProperty(Property property) {
this.property = property;
}
/** Getter for property width.
* @return Value of property width.
*/
public int getWidth() {
return this.width;
}
/** Setter for property width.
* @param width New value of property width.
*/
public void setWidth(int width) {
this.width = width;
}
/** Getter for property visibleIndex.
* @return Value of property visibleIndex.
*/
public int getVisibleIndex() {
return this.visibleIndex;
}
/** Setter for property visibleIndex.
* @param visibleIndex New value of property visibleIndex.
*/
public void setVisibleIndex(int visibleIndex) {
this.visibleIndex = visibleIndex;
property.setValue( ATTR_ORDER_NUMBER, new Integer( visibleIndex ) );
}
}
}