package org.openswing.swing.tree.client;
import java.lang.reflect.*;
import java.text.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import org.openswing.swing.message.receive.java.*;
import org.openswing.swing.tree.java.*;
import org.openswing.swing.util.client.*;
/**
* <p>Title: OpenSwing Framework</p>
* <p>Description: Panel that contains an expandable tree+grid.</p>
* <p>Copyright: Copyright (C) 2006 Mauro Carniel</p>
*
* <p> This file is part of OpenSwing Framework.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the (LGPL) Lesser General Public
* License as published by the Free Software Foundation;
*
* GNU LESSER GENERAL PUBLIC LICENSE
* Version 2.1, February 1999
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* The author may be contacted at:
* maurocarniel@tin.it</p>
*
* @author Mauro Carniel
* @version 1.0
*/
public class TreeGridPanel extends JPanel {
/** attribute names to used to show grid columns */
private ArrayList gridColumns = new ArrayList();
/** grid columns sizes */
private ArrayList gridColumnSizes = new ArrayList();
/** grid columns alignments: collection of pairs <attribute name,alingment: Integer> */
private Hashtable gridColumnAlignments = new Hashtable();
/** determines whether or not the root node from the <code>TreeModel</code> is visible; default value: <code>true<code> */
private boolean rootVisible = true;
/** expandable tree */
private TreeGrid tree = new TreeGrid(new TreeGridModel(),"",new ArrayList(),gridColumnSizes,gridColumnAlignments,ClientSettings.PERC_TREE_FOLDER,ClientSettings.PERC_TREE_NODE,null,rootVisible);
/** tree root */
private DefaultMutableTreeNode treeRoot;
/** tree container */
private JScrollPane treePane = new JScrollPane();
/** data source used to fill in the tree */
private TreeDataLocator treeDataLocator;
/** tree controller: it manages tree events */
private TreeController treeController;
/** pop-up menu related to right mouse click on a tree node (optional) */
private JPopupMenu popup = new JPopupMenu();
/** collection of pairs: menu item description (not yet translated), menu item object; used to change the menu item abilitation */
private Hashtable menuItems = new Hashtable();
/** flag used inside addNotify method */
private boolean firstTime = true;
/** image icon used for leaves; default value: as for folders */
private String leavesImageName = ClientSettings.getInstance().PERC_TREE_FOLDER;
/** define if tree will be filled on viewing this panel; default value: true */
private boolean loadWhenVisibile = true;
/** define if all tree nodes must be expanded after loading */
private boolean expandAllNodes = false;
/** folder node image name */
private String folderIconName = ClientSettings.PERC_TREE_FOLDER;
/** getter methods in v.o. inside the tree node related to gridColumns */
private Method[] getterMethods = null;;
/** column headers; as default value are setted as attribute names translations */
private ArrayList columnHeaders = new ArrayList();
/** column data formatters; as default settings all columns are converted into String objects */
private ArrayList columnFormatters = new ArrayList();
/** define if root node must be automatically expanded when "expandAllNodes" property is set to <code>false</code>; default value: <code>true</code> */
private boolean expandRoot = true;
/** optional attribute name that identifies the name of the image to show as tree node */
private String iconAttributeName;
/** background color for this component */
private Color backgroundColor = null;
/**
* Constructor.
*/
public TreeGridPanel() {
try {
jbInit();
treePane.getViewport().setBackground(ClientSettings.GRID_CELL_BACKGROUND);
}
catch(Exception ex) {
ex.printStackTrace();
}
}
public final void addNotify() {
super.addNotify();
if (firstTime && loadWhenVisibile) {
firstTime = false;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createTree();
}
});
}
}
/**
* Determines whether or not the root node from
* the <code>TreeModel</code> is visible.
*
* @param rootVisible true if the root node of the tree is to be displayed
* @see #rootVisible
* @beaninfo
* bound: true
* description: Whether or not the root node
* from the TreeModel is visible.
*/
public final void setRootVisible(boolean rootVisible) {
this.rootVisible = rootVisible;
}
/**
* @return determines whether or not the root node from the <code>TreeModel</code> is visible
*/
public final boolean isRootVisible() {
return rootVisible;
}
/**
* Force tree reloading.
*/
public final void reloadTree() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (!loadWhenVisibile && firstTime) {
firstTime = false;
createTree();
if (expandAllNodes)
expandAllNodes();
else
tree.getTree().collapseRow(0);
}
else {
Response response = treeDataLocator.getTreeModel(tree.getTree());
if (response.isError())
treeRoot = new OpenSwingTreeNode();
else
treeRoot = (DefaultMutableTreeNode)((DefaultTreeModel)((VOResponse)response).getVo()).getRoot();
recreateTree();
treeDataLocator.loadDataCompleted(response.isError());
if (expandAllNodes)
expandAllNodes();
else
tree.getTree().collapseRow(0);
}
}
});
}
/**
* Expand a tree node.
* @param index index of node to expand
*/
public final void expandNode(int index) {
tree.getTree().expandRow(index);
}
/**
* Collapse a tree node.
* @param index index of node to collapse
*/
public final void collapseNode(int index) {
tree.getTree().collapseRow(index);
}
/**
* Expand all tree nodes.
*/
private final void expandAllNodes() {
int i=0;
while(i<tree.getRowCount())
tree.getTree().expandRow(i++);
}
void jbInit() throws Exception {
this.setLayout(new java.awt.BorderLayout());
treePane.getViewport().add(tree,null);
this.add(treePane,BorderLayout.CENTER);
treePane.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createRaisedBevelBorder(),
BorderFactory.createLoweredBevelBorder()
));
treePane.setAutoscrolls(true);
}
/**
* Redraw the tree. Used when the tree model has been modified.
*/
public final void repaintTree() {
recreateTree();
TreePath selPath = tree.getTree().getSelectionPath();
tree.repaint();
try {
tree.getTree().setSelectionPath(selPath.getParentPath());
}
catch (Exception ex) {
}
try {
tree.getTree().setSelectionPath(selPath);
}
catch (Exception ex) {
}
}
/**
* Remove all nodes (expept the root node) from the tree.
*/
public final void clearTree() {
treeRoot = new OpenSwingTreeNode();
repaintTree();
}
/**
* Fill in the tree.
*/
private void createTree() {
if (iconAttributeName!=null && !iconAttributeName.equals(""))
tree.setIconAttributeName(iconAttributeName);
Response response = treeDataLocator.getTreeModel(tree.getTree());
if (response.isError())
treeRoot = new OpenSwingTreeNode();
else
treeRoot = (DefaultMutableTreeNode)((DefaultTreeModel)((VOResponse)response).getVo()).getRoot();
recreateTree();
treeDataLocator.loadDataCompleted(response.isError());
}
private void recreateTree() {
treePane.getViewport().remove(tree);
TreeExpansionListener[] l1 = tree.getTree().getTreeExpansionListeners();
TreeWillExpandListener[] l2 = tree.getTree().getTreeWillExpandListeners();
TreeSelectionListener[] l3 = tree.getTree().getTreeSelectionListeners();
tree = new TreeGrid(new TreeGridModel(treeRoot),gridColumns.get(0).toString(),gridColumns,gridColumnSizes,gridColumnAlignments,folderIconName,leavesImageName,(Format)columnFormatters.get(0),rootVisible);
if (backgroundColor!=null) {
tree.setBackground(backgroundColor);
treePane.setBackground(backgroundColor);
treePane.getViewport().setBackground(backgroundColor);
}
if (iconAttributeName!=null && !iconAttributeName.equals(""))
tree.setIconAttributeName(iconAttributeName);
tree.getTree().setShowsRootHandles(true);
for(int i=0;i<l1.length;i++)
tree.getTree().addTreeExpansionListener(l1[i]);
for(int i=0;i<l2.length;i++)
tree.getTree().addTreeWillExpandListener(l2[i]);
for(int i=0;i<l3.length;i++)
tree.getTree().addTreeSelectionListener(l3[i]);
tree.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
// tree.getTree().setShowsRootHandles(true);
// try {
// TreeNodeRenderer renderer = new TreeNodeRenderer(this,folderIconName,leavesImageName);
// tree.setCellRenderer(renderer);
// } catch (Exception ex) {
// ex.printStackTrace();
// }
treePane.getViewport().add(tree);
tree.setSize(new Dimension((int)this.getPreferredSize().getWidth()/2,
(int)this.getPreferredSize().getHeight()));
tree.setMinimumSize(new Dimension(0,200));
tree.revalidate();
MouseListener ml = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if(e.getClickCount() == 1 && SwingUtilities.isLeftMouseButton(e))
treeLeftClick(e, tree.getTree());
else if(e.getClickCount() == 1 && SwingUtilities.isRightMouseButton(e))
treeRightClick(e, tree.getTree());
if(e.getClickCount() == 2)
treeDoubleClick(e, tree.getTree());
}
};
tree.addMouseListener(ml);
if (expandAllNodes)
expandAllNodes();
else if (expandRoot && rootVisible)
tree.getTree().expandRow(0);
}
/**
* @return selected node or null if no node is selected
*/
public final DefaultMutableTreeNode getSelectedNode() {
try {
javax.swing.tree.TreePath selPath = tree.getTree().getSelectionPath();
if (selPath != null)
return (DefaultMutableTreeNode)(selPath.getPathComponent(selPath.getPathCount()-1));
else
return null;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
/**
* Method called when user has double clicked.
* @param e double click event
* @param tree tree
*/
public final void treeDoubleClick(MouseEvent e,JTree tree) {
try {
javax.swing.tree.TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
if (selPath != null) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)(selPath.getPathComponent(selPath.getPathCount()-1));
treeController.doubleClick(node);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Method called when user has clicked on the left mouse button.
* @param e left mouse button click event
* @param tree tree
*/
public final void treeLeftClick(MouseEvent e,JTree tree) {
try {
int selRow = tree.getRowForLocation(e.getX(), e.getY());
javax.swing.tree.TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
if (selPath != null) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)(selPath.getPathComponent(selPath.getPathCount()-1));
treeController.leftClick(node);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
treePane.setEnabled(enabled);
}
/**
* Method called when user has clicked on the right mouse button.
* @param e right mouse button click event
* @param tree tree
*/
public final void treeRightClick(MouseEvent e,JTree tree) {
try {
int selRow = tree.getRowForLocation(e.getX(), e.getY());
javax.swing.tree.TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
if (selPath != null) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)(selPath.getPathComponent(selPath.getPathCount()-1));
if (treeController.rightClick(node) && popup.getComponentCount()>0 && treePane.isEnabled()) {
// visualizzazione del menu' a pop-up associato al nodo dell'albero,
// SOLO se il metodo rightClick ha ritornato valore "true" e c'e almeno un elemento nel menu' a pop-up
popup.show(e.getComponent(), e.getX(), e.getY());
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Add a pop-up menu item.
* @param menuName menu item description (not yet translated)
* @param mnemonic mnemonic value
* @param enabled flag used to set menu item abilitation
* @param menuListener listener used to capture menu item selection
*/
public final void addPopupMenuItem(String menuName, int mnemonic,boolean enabled,ActionListener menuListener) {
JMenuItem cbMenuItem = new JMenuItem(ClientSettings.getInstance().getResources().getResource(menuName));
cbMenuItem.setMnemonic(mnemonic);
cbMenuItem.setEnabled(enabled);
cbMenuItem.addActionListener(menuListener);
popup.add(cbMenuItem);
menuItems.put(menuName,cbMenuItem);
}
/**
* Add a pop-up menu item to a parent menu item.
* @param menuName menu item description (not yet translated)
* @param parentMenuName
* @param mnemonic mnemonic value
* @param enabled flag used to set menu item abilitation
* @param menuListener listener used to capture menu item selection
*/
public final void addPopupMenuItem(String menuName, String parentMenuName,char mnemonic,
boolean enabled,
ActionListener menuListener) {
JMenuItem cbMenuItem = new JMenuItem(ClientSettings.getInstance().
getResources().getResource(menuName));
cbMenuItem.setMnemonic(mnemonic);
cbMenuItem.setEnabled(enabled);
cbMenuItem.addActionListener(menuListener);
JMenuItem parentItem = (JMenuItem)menuItems.get(parentMenuName);
if (parentItem!=null) {
parentItem.add(cbMenuItem);
menuItems.put(menuName, cbMenuItem);
}
}
/**
* Set menu item abilitation.
* @param menuName menu item description (not yet translated)
* @param enabled flag used to enable the menu item
*/
public final void setMenuItemEnabled(String menuName,boolean enabled) {
JMenuItem menu = (JMenuItem)menuItems.get(menuName);
if (menu!=null)
menu.setEnabled(enabled);
}
/**
* @return tree controller: it manages tree events
*/
public final TreeController getTreeController() {
return treeController;
}
/**
* @return data source used to fill in the tree
*/
public final TreeDataLocator getTreeDataLocator() {
return treeDataLocator;
}
/**
* Set the data source used to fill in the tree
* @param treeDataLocator data source used to fill in the tree
*/
public final void setTreeDataLocator(TreeDataLocator treeDataLocator) {
this.treeDataLocator = treeDataLocator;
}
/**
* Set the tree controller: it manages tree events.
* @param treeController tree controller: it manages tree events.
*/
public final void setTreeController(TreeController treeController) {
this.treeController = treeController;
}
/**
* @return image icon used for leaves
*/
public final String getLeavesImageName() {
return leavesImageName;
}
/**
* Set image icon used for leaves.
* @param leavesImageName image icon used for leaves
*/
public final void setLeavesImageName(String leavesImageName) {
this.leavesImageName = leavesImageName;
}
/**
* Sets the background color of this component.
*
* @param bg the desired background <code>Color</code>
* @see java.awt.Component#getBackground
*
* @beaninfo
* preferred: true
* bound: true
* attribute: visualUpdate true
* description: The background color of the component.
*/
public final void setBackground(Color backgroundColor) {
this.backgroundColor = backgroundColor;
if (tree!=null)
tree.setBackground(backgroundColor);
if (treePane!=null) {
treePane.setBackground(backgroundColor);
treePane.getViewport().setBackground(backgroundColor);
}
}
/**
* @return background color of this component
*/
public final Color getBackground() {
return this.backgroundColor;
}
/**
* @return define if tree will be filled on viewing this panel
*/
public final boolean isLoadWhenVisibile() {
return loadWhenVisibile;
}
/**
* Define if tree will be filled on viewing this panel.
* @param loadWhenVisibile define if tree will be filled on viewing this panel
*/
public final void setLoadWhenVisibile(boolean loadWhenVisibile) {
this.loadWhenVisibile = loadWhenVisibile;
}
/**
* @return boolean define if all tree nodes must be expanded after loading
*/
public final boolean isExpandAllNodes() {
return expandAllNodes;
}
/**
* Define if all tree nodes must be expanded after loading.
* @param expandAllNodes boolean define if all tree nodes must be expanded after loading
*/
public final void setExpandAllNodes(boolean expandAllNodes) {
this.expandAllNodes = expandAllNodes;
}
/**
* @return optional attribute name that identifies the name of the image to show as tree node
*/
public final String getIconAttributeName() {
return iconAttributeName;
}
/**
* Optional attribute name that identifies the name of the image to show as tree node.
*/
public final void setIconAttributeName(String iconAttributeName) {
this.iconAttributeName = iconAttributeName;
}
/**
* @return folder icon name
*/
public final String getFolderIconName() {
return folderIconName;
}
/**
* Set the folder icon name.
* @param treeFolderName folder icon name
*/
public final void setFolderIconName(String folderIconName) {
this.folderIconName = folderIconName;
}
/**
* Add a column to tree+grid component and specifies attribute to map and column size.
* @param attributeName attribute name to map to this column
* @param colSize column size
*/
public final void addGridColumn(String attributeName,int colSize) {
this.gridColumns.add(attributeName);
this.gridColumnSizes.add(new Integer(colSize));
this.columnHeaders.add(attributeName);
this.columnFormatters.add(null);
}
/**
* Set the column format for the column identified by the specified attribute name.
* @param attributeName column identifier
* @param formatter Format object to used for data showed inside the specified column
*/
public void setColumnFormatter(String attributeName,Format formatter) {
int index = gridColumns.indexOf(attributeName);
if (index!=-1)
columnFormatters.set(index,formatter);
}
/**
* Set the column header for the column identified by the specified attribute name.
* @param attributeName column identifier
* @param description description to translate and set as column header
* @param gridColumnAlignment column alignments; allowed values:
*/
public final void setColumnHeader(String attributeName,String description) {
int index = gridColumns.indexOf(attributeName);
if (index!=-1)
columnHeaders.set(index,description);
}
/**
* Set the column alingment for the column identified by the specified attribute name.
* @param attributeName column identifier
* @param alignment One of the following constants
* defined in <code>SwingConstants</code>:
* <code>LEFT</code>,
* <code>CENTER</code> (the default for image-only labels),
* <code>RIGHT</code>,
* <code>LEADING</code> (the default for text-only labels) or
* <code>TRAILING</code>.
*/
public final void setColumnAlignment(String attributeName,int alignment) {
int index = gridColumns.indexOf(attributeName);
if (index!=-1)
this.gridColumnAlignments.put(attributeName,new Integer(alignment));
}
/**
* @return define if root node must be automatically expanded when "expandAllNodes" property is set to <code>false</code>
*/
public final boolean isExpandRoot() {
return expandRoot;
}
/**
* Define if root node must be automatically expanded when "expandAllNodes" property is set to <code>false</code>; default value: <code>true</code>.
* @param expandRoot define if root node must be automatically expanded when "expandAllNodes" property is set to <code>false</code>
*/
public final void setExpandRoot(boolean expandRoot) {
this.expandRoot = expandRoot;
}
/**
* <p>Title: OpenSwing Framework</p>
* <p>Description: Inner class used to create the tree+grid table model.</p>
* <p>Copyright: Copyright (C) 2006 Mauro Carniel</p>
* <p> </p>
* @author Mauro Carniel
* @version 1.0
*/
class TreeGridModel extends AbstractTreeTableModel {
public TreeGridModel() {
this(new OpenSwingTreeNode());
}
public TreeGridModel(DefaultMutableTreeNode root) {
super(root);
}
/**
* Returns <code>true</code> if <code>node</code> is a leaf.
* It is possible for this method to return <code>false</code>
* even if <code>node</code> has no children.
* A directory in a filesystem, for example,
* may contain no files; the node representing
* the directory is not a leaf, but it also has no children.
*
* @param node a node in the tree, obtained from this data source
* @return true if <code>node</code> is a leaf
*/
public boolean isLeaf(Object node) {
return !((TreeNode)node).getAllowsChildren();
}
public Object getValueAt(Object node, int column) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)node;
ValueObject vo = (ValueObject)treeNode.getUserObject();
if (vo==null)
return vo;
if (getterMethods==null) {
try {
getterMethods = new Method[gridColumns.size()];
for (int i = 0; i < gridColumns.size(); i++) {
getterMethods[i] = vo.getClass().getMethod(
"get" +
gridColumns.get(i).toString().substring(0, 1).toUpperCase() +
gridColumns.get(i).toString().substring(1), new Class[0]
);
}
}
catch (Throwable ex) {
ex.printStackTrace();
}
}
if (getterMethods==null || getterMethods[column]==null)
return null;
try {
Object value = getterMethods[column].invoke(vo, new Object[0]);
if (value!=null && columnFormatters.get(column)!=null) {
Format formatter = (Format)columnFormatters.get(column);
value = formatter.format(value);
}
return value;
}
catch (Throwable ex) {
ex.printStackTrace();
return null;
}
}
public int getChildCount(Object node) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)node;
return treeNode.getChildCount();
}
public Object getChild(Object node, int childNumber) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)node;
return treeNode.getChildAt(childNumber);
}
public int getColumnCount() {
return gridColumns.size();
}
public String getColumnName(int col) {
return ClientSettings.getInstance().getResources().getResource(columnHeaders.get(col).toString());
}
/**
* Returns the class for the particular column.
*/
public Class getColumnClass(int column) {
if (column==0)
return TreeTableModel.class;
else {
if (getterMethods==null)
return super.getColumnClass(column);
else {
return getterMethods[column].getReturnType();
}
}
}
}
}