001: /*
002: * Beryl - A web platform based on XML, XSLT and Java
003: * This file is part of the Beryl XML GUI
004: *
005: * Copyright (C) 2004 Wenzel Jakob <wazlaf@tigris.org>
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011:
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-3107 USA
020: */
021:
022: package org.beryl.gui.widgets;
023:
024: import java.awt.Component;
025: import java.awt.Point;
026: import java.awt.event.MouseAdapter;
027: import java.awt.event.MouseEvent;
028: import java.util.ArrayList;
029: import java.util.List;
030:
031: import javax.swing.JScrollPane;
032: import javax.swing.JTree;
033: import javax.swing.event.TreeSelectionEvent;
034: import javax.swing.event.TreeSelectionListener;
035: import javax.swing.tree.DefaultTreeCellRenderer;
036: import javax.swing.tree.DefaultTreeModel;
037: import javax.swing.tree.TreePath;
038:
039: import org.beryl.gui.GUIEvent;
040: import org.beryl.gui.GUIEventListener;
041: import org.beryl.gui.GUIException;
042: import org.beryl.gui.Widget;
043: import org.beryl.gui.WidgetInfo;
044: import org.beryl.gui.model.MapChangeEvent;
045: import org.beryl.gui.model.MapDataModel;
046: import org.beryl.gui.model.ModelChangeEvent;
047:
048: public class Tree extends Widget {
049: protected static WidgetInfo treeInfo = null;
050: private JTree tree = null;
051: private JScrollPane scrollPane = null;
052: private TreeItem rootNode = null;
053: private String key = null;
054: private boolean sendEvents = true;
055: private boolean processEvents = true;
056:
057: static {
058: treeInfo = new WidgetInfo(Tree.class, widgetInfo);
059: treeInfo.addProperty("key", "string", "");
060: treeInfo
061: .addProperty("verticalScrollBar", "bool", Boolean.FALSE);
062: treeInfo.addProperty("horizontalScrollBar", "bool",
063: Boolean.FALSE);
064: treeInfo.addEvent("rightclick");
065: treeInfo.addEvent("doubleclick");
066: }
067:
068: private class TreeCellRenderer extends DefaultTreeCellRenderer {
069: public Component getTreeCellRendererComponent(JTree tree,
070: Object value, boolean sel, boolean expanded,
071: boolean leaf, int row, boolean hasFocus) {
072: super .getTreeCellRendererComponent(tree, value, sel,
073: expanded, leaf, row, hasFocus);
074: try {
075: Item node = (Item) value;
076: if (node.getIcon() != null) {
077: setIcon(node.getIcon());
078: }
079: } catch (ClassCastException e) {
080: /* Ignore */
081: }
082:
083: return this ;
084: }
085: }
086:
087: public Tree(Widget parent, String name) throws GUIException {
088: super (parent, name);
089:
090: tree = new JTree(rootNode);
091: tree.addTreeSelectionListener(new TreeSelectionListener() {
092: public void valueChanged(TreeSelectionEvent e) {
093: if (sendEvents) {
094: try {
095: sendEvents = false;
096: processEvents = false;
097: MapDataModel model = getDataModel();
098: if (model != null && key != null) {
099: model.setValue(Tree.this , key,
100: getSelectedItems());
101: }
102: } catch (GUIException ex) {
103: throw new RuntimeException(ex);
104: } finally {
105: processEvents = true;
106: sendEvents = true;
107: }
108: }
109: }
110: });
111: tree.setCellRenderer(new TreeCellRenderer());
112: scrollPane = new JScrollPane(tree);
113: tree.putClientProperty("JTree.lineStyle", "Angled");
114: tree.setShowsRootHandles(true);
115: }
116:
117: public void setProperty(String name, Object value)
118: throws GUIException {
119: if ("key".equals(name)) {
120: key = (String) value;
121: } else if ("verticalScrollBar".equals(name)) {
122: scrollPane
123: .setVerticalScrollBarPolicy(((Boolean) value)
124: .booleanValue() ? JScrollPane.VERTICAL_SCROLLBAR_ALWAYS
125: : JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
126: } else if ("horizontalScrollBar".equals(name)) {
127: scrollPane
128: .setHorizontalScrollBarPolicy(((Boolean) value)
129: .booleanValue() ? JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS
130: : JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
131: } else {
132: super .setProperty(name, value);
133: }
134: }
135:
136: public void addChild(Widget widget, Object constraint)
137: throws GUIException {
138: if (widget instanceof TreeItem) {
139: if (rootNode == null) {
140: rootNode = (TreeItem) widget;
141: tree.setModel(new DefaultTreeModel(rootNode, false));
142: addChild(rootNode);
143: } else {
144: throw new GUIException(
145: "A Tree can only have one root node");
146: }
147: } else {
148: throw new GUIException(
149: "Only TreeItem children are supported");
150: }
151: }
152:
153: public void addListener(String event, final String name,
154: final GUIEventListener listener) throws GUIException {
155: if (event.equals("rightclick")) {
156: tree.addMouseListener(new MouseAdapter() {
157: public void mousePressed(MouseEvent me) {
158: Point p = me.getPoint();
159: int selRow = tree.getRowForLocation(p.x, p.y);
160: if (me.isPopupTrigger() && selRow != -1) {
161: if (tree.getSelectionPaths().length < 2)
162: tree.setSelectionPath(tree
163: .getPathForRow(selRow));
164: listener.eventOccured(new GUIEvent(Tree.this ,
165: name, me));
166: }
167: }
168:
169: public void mouseReleased(MouseEvent me) {
170: mousePressed(me);
171: }
172: });
173: } else if (event.equals("doubleclick")) {
174: tree.addMouseListener(new MouseAdapter() {
175: public void mousePressed(MouseEvent me) {
176: Point p = me.getPoint();
177: int selRow = tree.getRowForLocation(p.x, p.y);
178: if (me.getClickCount() == 2 && !me.isPopupTrigger()
179: && selRow != -1) {
180: listener.eventOccured(new GUIEvent(Tree.this ,
181: name, me));
182: }
183: }
184: });
185: } else {
186: super .addListener(event, name, listener);
187: }
188: }
189:
190: private TreeItem[] getSelectedItems() {
191: TreePath paths[] = tree.getSelectionPaths();
192: if (paths == null)
193: return new TreeItem[0];
194: TreeItem items[] = new TreeItem[paths.length];
195: for (int i = 0; i < paths.length; i++) {
196: items[i] = (TreeItem) paths[i].getLastPathComponent();
197: }
198: return items;
199: }
200:
201: /**
202: * Inform the tree that a tree structure changed, beginning at <tt>item</tt>
203: * @param item The tree item
204: */
205: public void structureChanged(TreeItem item) {
206: ((DefaultTreeModel) tree.getModel()).nodeStructureChanged(item);
207: }
208:
209: /**
210: * Expand the tree to the selected tree item
211: * @param item The tree item
212: */
213: public void expandTo(TreeItem item) {
214: tree.expandPath(getPathToNode(item));
215: }
216:
217: private TreePath getPathToNode(TreeItem item) {
218: List pathItems = new ArrayList();
219: while (item != null) {
220: pathItems.add(0, item);
221: item = (TreeItem) item.getParent();
222: }
223: return new TreePath(pathItems.toArray());
224: }
225:
226: /**
227: * Expand all tree nodes
228: */
229: public void expandAll() {
230: for (int row = 0; row < tree.getRowCount(); row++) {
231: tree.expandRow(row);
232: }
233: }
234:
235: /**
236: * Collapse all tree nodes
237: */
238: public void collapseAll() {
239: for (int row = tree.getRowCount() - 1; row >= 0; row--) {
240: tree.collapseRow(row);
241: }
242: }
243:
244: /**
245: * Inform the tree that a tree item changed
246: * @param item The tree item
247: */
248: public void nodeChanged(TreeItem item) {
249: ((DefaultTreeModel) tree.getModel()).nodeChanged(item);
250: }
251:
252: private void setSelectedItems(TreeItem items[]) {
253: TreePath paths[] = new TreePath[items.length];
254:
255: for (int i = 0; i < items.length; i++) {
256: paths[i] = getPathToNode(items[i]);
257: }
258: tree.setSelectionPaths(paths);
259: }
260:
261: private void reload() throws GUIException {
262: MapDataModel model = getDataModel();
263: if (model != null) {
264: try {
265: processEvents = false;
266: if (key != null) {
267: TreeItem items[] = (TreeItem[]) model.getValue(key);
268: if (items != null)
269: setSelectedItems(items);
270: else
271: model.setValue(Tree.this , key,
272: getSelectedItems());
273: }
274: } finally {
275: processEvents = true;
276: }
277: }
278: }
279:
280: public void modelChanged(ModelChangeEvent e) throws GUIException {
281: if (processEvents) {
282: try {
283: sendEvents = false;
284: if (e.getSource() == this ) {
285: /* New data model */
286: try {
287: reload();
288: } catch (IllegalArgumentException ex) {
289: /*List data model not yet set */
290: }
291: } else if (e instanceof MapChangeEvent) {
292: MapChangeEvent event = (MapChangeEvent) e;
293: if (event.getKey() == null) {
294: reload();
295: } else if (event.getKey().equals(key)) {
296: setSelectedItems((TreeItem[]) event
297: .getNewValue());
298: }
299: }
300: } finally {
301: sendEvents = true;
302: }
303: }
304: }
305:
306: public Component getWidget() {
307: return scrollPane;
308: }
309:
310: public Component getRealWidget() {
311: return tree;
312: }
313:
314: public WidgetInfo getWidgetInfo() {
315: return treeInfo;
316: }
317: }
|