/*
* Beryl - A web platform based on XML, XSLT and Java
* This file is part of the Beryl XML GUI
*
* Copyright (C) 2004 Wenzel Jakob <wazlaf@tigris.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-3107 USA
*/
package org.beryl.gui.widgets;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import org.beryl.gui.GUIEvent;
import org.beryl.gui.GUIEventListener;
import org.beryl.gui.GUIException;
import org.beryl.gui.Widget;
import org.beryl.gui.WidgetInfo;
import org.beryl.gui.model.MapChangeEvent;
import org.beryl.gui.model.MapDataModel;
import org.beryl.gui.model.ModelChangeEvent;
public class Tree extends Widget {
protected static WidgetInfo treeInfo = null;
private JTree tree = null;
private JScrollPane scrollPane = null;
private TreeItem rootNode = null;
private String key = null;
private boolean sendEvents = true;
private boolean processEvents = true;
static {
treeInfo = new WidgetInfo(Tree.class, widgetInfo);
treeInfo.addProperty("key", "string", "");
treeInfo.addProperty("verticalScrollBar", "bool", Boolean.FALSE);
treeInfo.addProperty("horizontalScrollBar", "bool", Boolean.FALSE);
treeInfo.addEvent("rightclick");
treeInfo.addEvent("doubleclick");
};
private class TreeCellRenderer extends DefaultTreeCellRenderer {
public Component getTreeCellRendererComponent(
JTree tree,
Object value,
boolean sel,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
try {
Item node = (Item) value;
if (node.getIcon() != null) {
setIcon(node.getIcon());
}
} catch (ClassCastException e) {
/* Ignore */
}
return this;
}
}
public Tree(Widget parent, String name) throws GUIException {
super(parent, name);
tree = new JTree(rootNode);
tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
if (sendEvents) {
try {
sendEvents = false;
processEvents = false;
MapDataModel model = getDataModel();
if (model != null && key != null) {
model.setValue(Tree.this, key, getSelectedItems());
}
} catch (GUIException ex) {
throw new RuntimeException(ex);
} finally {
processEvents = true;
sendEvents = true;
}
}
}
});
tree.setCellRenderer(new TreeCellRenderer());
scrollPane = new JScrollPane(tree);
tree.putClientProperty("JTree.lineStyle", "Angled");
}
public void setProperty(String name, Object value) throws GUIException {
if ("key".equals(name)) {
key = (String) value;
} else if ("verticalScrollBar".equals(name)) {
scrollPane.setVerticalScrollBarPolicy(
((Boolean) value).booleanValue()
? JScrollPane.VERTICAL_SCROLLBAR_ALWAYS
: JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
} else if ("horizontalScrollBar".equals(name)) {
scrollPane.setHorizontalScrollBarPolicy(
((Boolean) value).booleanValue()
? JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS
: JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
} else {
super.setProperty(name, value);
}
}
public void addChild(Widget widget, Object constraint) throws GUIException {
if (widget instanceof TreeItem) {
if (rootNode == null) {
rootNode = (TreeItem) widget;
rootNode.setTree(this);
tree.setModel(new DefaultTreeModel(rootNode, false));
addChild(rootNode);
} else {
throw new GUIException("A Tree can only have one root node");
}
} else {
throw new GUIException("Only TreeItem children are supported");
}
}
public void addListener(String event, final String name, final GUIEventListener listener) throws GUIException {
if (event.equals("rightclick")) {
tree.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
if (me.isPopupTrigger()) {
listener.eventOccured(new GUIEvent(Tree.this, name, me));
}
}
public void mouseReleased(MouseEvent me) {
mousePressed(me);
}
});
} else if (event.equals("doubleclick")) {
tree.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
if (me.getClickCount() == 2) {
listener.eventOccured(new GUIEvent(Tree.this, name, me));
}
}
});
} else {
super.addListener(event, name, listener);
}
}
private TreeItem[] getSelectedItems() {
TreePath paths[] = tree.getSelectionPaths();
if (paths == null)
return new TreeItem[0];
TreeItem items[] = new TreeItem[paths.length];
for (int i = 0; i < paths.length; i++) {
items[i] = (TreeItem) paths[i].getLastPathComponent();
}
return items;
}
/**
* Inform the tree that a tree structure changed, beginning at <tt>item</tt>
* @param item The tree item
*/
public void structureChanged(TreeItem item) {
((DefaultTreeModel) tree.getModel()).nodeStructureChanged(item);
}
/**
* Inform the tree that a tree item changed
* @param item The tree item
*/
public void nodeChanged(TreeItem item) {
((DefaultTreeModel) tree.getModel()).nodeChanged(item);
}
private void setSelectedItems(TreeItem items[]) {
TreePath paths[] = new TreePath[items.length];
ArrayList pathItems = new ArrayList();
for (int i = 0; i < items.length; i++) {
TreeItem item = items[i];
while (item != null) {
pathItems.add(0, item);
item = (TreeItem) item.getParent();
}
paths[i] = new TreePath(pathItems.toArray());
pathItems.clear();
}
tree.setSelectionPaths(paths);
}
private void reload() throws GUIException {
MapDataModel model = getDataModel();
if (model != null) {
try {
processEvents = false;
if (key != null) {
System.out.println("getting " + key);
TreeItem items[] = (TreeItem[]) model.getValue(key);
System.out.println(items);
if (items != null)
setSelectedItems(items);
else
model.setValue(Tree.this, key, getSelectedItems());
}
} finally {
processEvents = true;
}
}
}
public void modelChanged(ModelChangeEvent e) throws GUIException {
if (processEvents) {
try {
sendEvents = false;
if (e.getSource() == this) {
/* New data model */
try {
reload();
} catch (IllegalArgumentException ex) {
/*List data model not yet set */
}
} else if (e instanceof MapChangeEvent) {
MapChangeEvent event = (MapChangeEvent) e;
if (event.getKey() == null) {
reload();
} else if (event.getKey().equals(key)) {
setSelectedItems((TreeItem[]) event.getNewValue());
}
}
} finally {
sendEvents = true;
}
}
}
public Component getWidget() {
return scrollPane;
}
public Component getRealWidget() {
return tree;
}
public WidgetInfo getWidgetInfo() {
return treeInfo;
}
}