001: /*
002: #IFNDEF ALT_LICENSE
003: ThinWire(R) RIA Ajax Framework
004: Copyright (C) 2003-2007 Custom Credit Systems
005:
006: This library is free software; you can redistribute it and/or modify it under
007: the terms of the GNU Lesser General Public License as published by the Free
008: Software Foundation; either version 2.1 of the License, or (at your option) any
009: later version.
010:
011: This library is distributed in the hope that it will be useful, but WITHOUT ANY
012: WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
013: PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
014:
015: You should have received a copy of the GNU Lesser General Public License along
016: with this library; if not, write to the Free Software Foundation, Inc., 59
017: Temple Place, Suite 330, Boston, MA 02111-1307 USA
018:
019: Users who would rather have a commercial license, warranty or support should
020: contact the following company who invented, built and supports the technology:
021:
022: Custom Credit Systems, Richardson, TX 75081, USA.
023: email: info@thinwire.com ph: +1 (888) 644-6405
024: http://www.thinwire.com
025: #ENDIF
026: [ v1.2_RC2 ]
027: */
028: package thinwire.ui;
029:
030: import java.util.List;
031:
032: import thinwire.ui.event.ActionEvent;
033: import thinwire.ui.event.ItemChangeEvent;
034: import thinwire.ui.event.ItemChangeListener;
035:
036: /**
037: * A component that displays a set of hierarchical data as an outline.
038: * <p>
039: * <b>Example:</b> <br>
040: * <img src="doc-files/Tree-1.png"> <br>
041: *
042: * <pre>
043: * Dialog treeFrame = new Dialog("Tree Test");
044: * treeFrame.setBounds(25, 25, 400, 350);
045: *
046: * final Label label = new Label("???????");
047: * label.setBounds(225, 25, 150, 20);
048: *
049: * Tree tree = new Tree();
050: * tree.setBounds(10, 10, 200, 300);
051: * Tree.Item root = tree.getRootItem();
052: * root.setText("System Root");
053: * root.setImage(SCORE_IMAGE);
054: * tree.setRootItemVisible(false);
055: * root.setExpanded(false);
056: *
057: * Tree.Item readme = new Tree.Item("readme", FILE_IMAGE);
058: * root.getChildren().add(readme);
059: * Tree.Item bill = new Tree.Item("bill", FOLDER_IMAGE);
060: * root.getChildren().add(bill);
061: * Tree.Item startup = new Tree.Item("startup", FILE_IMAGE);
062: * bill.getChildren().add(startup);
063: * Tree.Item billFiles = new Tree.Item("files", FOLDER_IMAGE);
064: * bill.getChildren().add(billFiles);
065: * Tree.Item billFilesAddress = new Tree.Item("Address List", FILE_IMAGE);
066: * billFiles.getChildren().add(billFilesAddress);
067: * Tree.Item billFilesPhone = new Tree.Item("Phone List", FILE_IMAGE);
068: * billFiles.getChildren().add(billFilesPhone);
069: * bill.setExpanded(true);
070: * billFiles.setExpanded(true);
071: *
072: * Tree.Item billMusic = new Tree.Item("music", FOLDER_IMAGE);
073: * bill.getChildren().add(billMusic);
074: * Tree.Item billMusicSong1 = new Tree.Item("song1.mp3", FILE_IMAGE);
075: * billMusic.getChildren().add(billMusicSong1);
076: *
077: * Tree.Item billPic = new Tree.Item("pictures", FOLDER_IMAGE);
078: * bill.getChildren().add(billPic);
079: * Tree.Item billPicHome = new Tree.Item("home", FOLDER_IMAGE);
080: * billPic.getChildren().add(billPicHome);
081: * Tree.Item billPicS = new Tree.Item("s.jpg", PICTURE_IMAGE);
082: * billPicHome.getChildren().add(billPicS);
083: * Tree.Item billPicP = new Tree.Item("p.jpg", PICTURE_IMAGE);
084: * billPicHome.getChildren().add(billPicP);
085: * Tree.Item billPicM = new Tree.Item("m.jpg", PICTURE_IMAGE);
086: * billPicHome.getChildren().add(billPicM);
087: *
088: * Tree.Item jim = new Tree.Item("jim", FOLDER_IMAGE);
089: * root.getChildren().add(jim);
090: * Tree.Item jimStartup = new Tree.Item("startup", FILE_IMAGE);
091: * jim.getChildren().add(jimStartup);
092: * Tree.Item copyright = new Tree.Item("copyright", FILE_IMAGE);
093: * root.getChildren().add(copyright);
094: *
095: * tree.addPropertyChangeListener(Tree.Item.PROPERTY_SELECTED, new PropertyChangeListener() {
096: * public void propertyChange(PropertyChangeEvent ev) {
097: * label.setText(((Tree.Item) ev.getSource()).getText());
098: * }
099: * });
100: *
101: * treeFrame.getChildren().add(tree);
102: * treeFrame.getChildren().add(label);
103: * treeFrame.setVisible(true);
104: * </pre>
105: *
106: * </p>
107: * <p>
108: * <b>Keyboard Navigation:</b><br>
109: * <table border="1">
110: * <tr>
111: * <td>KEY</td>
112: * <td>RESPONSE</td>
113: * <td>NOTE</td>
114: * </tr>
115: * <tr>
116: * <td>Space</td>
117: * <td>Fires PropertyChangeEvent( propertyName = Tree.Item.PROPERTY_CHECKED )</td>
118: * <td>Only if the component has focus.</td>
119: * </tr>
120: * </table>
121: * </p>
122: *
123: * @author Joshua J. Gertzen
124: */
125: public class Tree extends AbstractHierarchyComponent<Tree.Item> {
126: /**
127: * An object that represents an item in a <code>Tree</code> component.
128: */
129: public final static class Item extends
130: AbstractHierarchyComponent.Item<Tree, Tree.Item> {
131: public static final String PROPERTY_ITEM_SELECTED = "itemSelected";
132: public static final String PROPERTY_ITEM_EXPANDED = "itemExpanded";
133:
134: private boolean expanded;
135: private boolean selected;
136:
137: /**
138: * Constructs a new Item with no text and no image.
139: */
140: public Item() {
141: this (null, null);
142: }
143:
144: /**
145: * Constructs a new Item with the specified text and no image.
146: * @param text the text to display for the item.
147: */
148: public Item(String text) {
149: this (text, null);
150: }
151:
152: /**
153: * Constructs a new Item with the specified text and image.
154: * @param text the text to display for the item.
155: * @param image the image to display to the left of the text of the item.
156: */
157: public Item(String text, String image) {
158: if (text != null)
159: setText(text);
160: if (image != null)
161: setImage(image);
162: }
163:
164: /**
165: * Get the selected state of the item.
166: * @return true if this item is the selected item in the tree, false otherwise.
167: */
168: public boolean isSelected() {
169: return selected;
170: }
171:
172: /**
173: * Set the selected state of the item.
174: * @param selected true if you want to select the item, false if you want to unselect it and select the root item.
175: * @throws IllegalStateException if the item has not been added to a tree.
176: */
177: public void setSelected(boolean selected) {
178: this .selected = selected;
179: Tree tree = getHierarchy();
180:
181: if (tree != null) {
182: if (selected && tree.selectedItem != this ) {
183: if (tree.selectedItem != null) {
184: tree.firePropertyChange(tree.selectedItem,
185: PROPERTY_ITEM_SELECTED, true, false);
186: tree.selectedItem.selected = false;
187: }
188:
189: tree.priorSelectedItem = tree.selectedItem;
190: tree.priorSelectedItem.selected = false;
191: tree.selectedItem = this ;
192: tree.firePropertyChange(this ,
193: PROPERTY_ITEM_SELECTED, false, true);
194: } else if (!selected && tree.selectedItem == this ) {
195: tree.firePropertyChange(this ,
196: PROPERTY_ITEM_SELECTED, true, false);
197: tree.priorSelectedItem = this ;
198: tree.selectedItem = tree.getRootItem();
199: tree.firePropertyChange(tree.selectedItem,
200: PROPERTY_ITEM_SELECTED, false, true);
201: }
202: }
203: }
204:
205: /**
206: * Get the expanded state of the item.
207: * @return true if this branch item in the tree is expanded and its children are shown, false otherwise.
208: */
209: public boolean isExpanded() {
210: return expanded;
211: }
212:
213: /**
214: * Set the expanded state of the item.
215: * @param expanded true if you want to expand this branch item and show its children, false if you want to collapse it and
216: * hide the children.
217: */
218: public void setExpanded(boolean expanded) {
219: boolean oldExpanded = this .expanded;
220: this .expanded = expanded;
221: Tree tree = getHierarchy();
222: if (tree != null)
223: tree.firePropertyChange(this , PROPERTY_ITEM_EXPANDED,
224: oldExpanded, expanded);
225: }
226:
227: public String toString() {
228: return "Tree.Item{" + super .toString() + ",selected:"
229: + this .isSelected() + ",expanded:"
230: + this .isExpanded() + "}";
231: }
232: }
233:
234: public static final String PROPERTY_ROOT_ITEM_VISIBLE = "rootItemVisible";
235:
236: private boolean rootItemVisible;
237: private Item selectedItem;
238: private Item priorSelectedItem;
239:
240: /**
241: * Constructs a new Tree.
242: */
243: public Tree() {
244: super (new Item(), EventListenerImpl.ACTION_VALIDATOR);
245: selectedItem = getRootItem();
246: selectedItem.setExpanded(true);
247: }
248:
249: void removingItem(Tree.Item item) {
250: if (priorSelectedItem == item)
251: priorSelectedItem = null;
252:
253: if (selectedItem == item || childSelected(item)) {
254: if (item.getParent() instanceof Tree.Item) {
255: Tree.Item parent = (Tree.Item) item.getParent();
256: List<Tree.Item> kids = parent.getChildren();
257: int index = kids.indexOf(item);
258:
259: if ((index - 1) >= 0) {
260: kids.get(index - 1).setSelected(true);
261: } else if ((index + 1) < parent.getChildren().size()) {
262: kids.get(index + 1).setSelected(true);
263: } else {
264: parent.setSelected(true);
265: }
266: }
267: }
268: }
269:
270: private boolean childSelected(Tree.Item item) {
271: if (!item.hasChildren())
272: return false;
273: for (Tree.Item i : item.getChildren())
274: if (selectedItem.equals(i) || childSelected(i))
275: return true;
276: return false;
277: }
278:
279: /**
280: * Get the rootItemVisible state of the Tree.
281: * @return true if the root item is visible, false otherwise.
282: */
283: public boolean isRootItemVisible() {
284: return rootItemVisible;
285: }
286:
287: /**
288: * Set the rootItemVisible state of the Tree.
289: * @param rootItemVisible true if you want the root item of the tree to be visible, false otherwise.
290: */
291: public void setRootItemVisible(boolean rootItemVisible) {
292: boolean oldRootItemVisible = this .rootItemVisible;
293: this .rootItemVisible = rootItemVisible;
294: Tree.Item root = getRootItem();
295:
296: if (rootItemVisible
297: && (!root.hasChildren() || root.getChildren().size() == 0)) {
298: root.setSelected(true);
299: } else if (!rootItemVisible
300: && (root.hasChildren() && root.getChildren().size() > 0)) {
301: root.getChildren().get(0).setSelected(true);
302: }
303:
304: firePropertyChange(this , PROPERTY_ROOT_ITEM_VISIBLE,
305: oldRootItemVisible, rootItemVisible);
306: }
307:
308: /**
309: * Get the selected item of Tree.
310: * @return the selected item of Tree.
311: */
312: public Item getSelectedItem() {
313: return selectedItem;
314: }
315:
316: /**
317: * Gets the prior selected item of Tree.
318: * @return the prior selected item of Tree.
319: */
320: public Item getPriorSelectedItem() {
321: if (priorSelectedItem.getParent() == null
322: || priorSelectedItem.getHierarchy() != this )
323: priorSelectedItem = null;
324: return priorSelectedItem;
325: }
326:
327: public void fireAction(ActionEvent ev) {
328: if (ev == null)
329: throw new IllegalArgumentException("ev == null");
330: if (!(ev.getSource() instanceof Item))
331: throw new IllegalArgumentException(
332: "!(ev.getSource() instanceof Tree.Item)");
333: ((Item) ev.getSource()).setSelected(true);
334: super.fireAction(ev);
335: }
336: }
|