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.render.web;
029:
030: import java.util.List;
031:
032: import thinwire.ui.Component;
033: import thinwire.ui.HierarchyComponent;
034: import thinwire.ui.Tree;
035: import thinwire.ui.event.ItemChangeEvent;
036: import thinwire.ui.event.ItemChangeListener;
037: import thinwire.ui.event.PropertyChangeEvent;
038:
039: /**
040: * @author Joshua J. Gertzen
041: * @author Tom E. Kimball
042: */
043: final class TreeRenderer extends ComponentRenderer implements
044: ItemChangeListener {
045: private static final String TREE_CLASS = "tw_Tree";
046: private static final String ITEM_REMOVE = "itemRemove";
047: private static final String ITEM_EXPAND = "itemExpand";
048: private static final String ITEM_CHANGE = "itemChange";
049: private static final String ITEM_SELECT = "itemSelect";
050: private static final String SET_ROOT_ITEM_VISIBLE = "setRootItemVisible";
051: private static final String ITEM_ADD = "itemAdd";
052: private Tree tree;
053:
054: void render(WindowRenderer wr, Component c,
055: ComponentRenderer container) {
056: init(TREE_CLASS, wr, c, container);
057: tree = (Tree) c;
058: StringBuilder sb = new StringBuilder();
059: prepareInitData(sb);
060: addClientSideProperty(Tree.Item.PROPERTY_ITEM_EXPANDED);
061: addClientSideProperty(Tree.Item.PROPERTY_ITEM_SELECTED);
062: addInitProperty("initData", sb);
063: tree.addItemChangeListener(this );
064: super .render(wr, c, container);
065: Tree.Item item = tree.getSelectedItem();
066: if (item != null)
067: postClientEvent(ITEM_SELECT, fullIndex((Tree.Item) item));
068: }
069:
070: void destroy() {
071: super .destroy();
072: tree.removeItemChangeListener(this );
073: tree = null;
074: }
075:
076: public void propertyChange(PropertyChangeEvent pce) {
077: String name = pce.getPropertyName();
078: if (isPropertyChangeIgnored(name))
079: return;
080: Object source = pce.getSource();
081: Object newValue = pce.getNewValue();
082:
083: if (source instanceof Tree.Item) {
084: String fullIndex = fullIndex((Tree.Item) source);
085:
086: if (name.equals(Tree.Item.PROPERTY_ITEM_EXPANDED)) {
087: if (((Tree.Item) source).hasChildren())
088: postClientEvent(ITEM_EXPAND, fullIndex, newValue);
089: } else if (name
090: .equals(HierarchyComponent.Item.PROPERTY_ITEM_IMAGE)
091: || name
092: .equals(HierarchyComponent.Item.PROPERTY_ITEM_TEXT)) {
093: Object newTextValue = GridBoxRenderer.getValue(this ,
094: ((Tree.Item) source).getText(), null, null);
095: postClientEvent(
096: ITEM_CHANGE,
097: fullIndex,
098: newTextValue,
099: getQualifiedURL(((Tree.Item) source).getImage()));
100: } else if (name.equals(Tree.Item.PROPERTY_ITEM_SELECTED)) {
101: postClientEvent(ITEM_SELECT, fullIndex);
102: }
103: } else if (source instanceof Tree) {
104: if (name.equals(Tree.PROPERTY_ROOT_ITEM_VISIBLE)) {
105: postClientEvent(SET_ROOT_ITEM_VISIBLE, newValue);
106: } else {
107: super .propertyChange(pce);
108: }
109: } else {
110: super .propertyChange(pce);
111: }
112: }
113:
114: public void itemChange(ItemChangeEvent ice) {
115: ItemChangeEvent.Type type = ice.getType();
116: Tree.Item newValue = (Tree.Item) ice.getNewValue();
117: Integer index = (Integer) ice.getPosition();
118: Tree.Item parent = (Tree.Item) ice.getSource();
119: String fullIndex = index.toString();
120: if (parent != tree.getRootItem())
121: fullIndex = fullIndex(parent) + "." + index;
122:
123: if (type == ItemChangeEvent.Type.REMOVE
124: || type == ItemChangeEvent.Type.SET) {
125: postClientEvent(ITEM_REMOVE, fullIndex);
126: }
127:
128: if (type == ItemChangeEvent.Type.ADD
129: || type == ItemChangeEvent.Type.SET) {
130: Object newTextValue = GridBoxRenderer.getValue(this ,
131: newValue.getText(), null, null);
132: postClientEvent(ITEM_ADD, fullIndex, newTextValue,
133: getQualifiedURL(newValue.getImage()));
134: if (newValue.hasChildren())
135: renderChildren(newValue);
136: if (((Tree.Item) newValue.getParent()).isExpanded()
137: && newValue.getIndex() == 0) {
138: postClientEvent(ITEM_EXPAND,
139: fullIndex(((Tree.Item) newValue.getParent())),
140: true);
141: }
142: }
143:
144: if (type == ItemChangeEvent.Type.ADD) {
145: Tree tree = newValue.getHierarchy();
146: if (tree.getRootItem().getChildren().size() == 1
147: || newValue.isSelected())
148: newValue.setSelected(true);
149: } else if (type == ItemChangeEvent.Type.SET) {
150: if (((Tree.Item) ice.getOldValue()).isSelected())
151: (newValue).setSelected(true);
152: }
153: }
154:
155: private void renderChildren(Tree.Item parent) {
156: //XXX: This may not be the most efficient way to handle this, consider sending initdata instead.
157: for (Tree.Item item : parent.getChildren()) {
158: int index = item.getIndex();
159: String fullIndex = String.valueOf(index);
160: if (parent != tree.getRootItem())
161: fullIndex = fullIndex(parent) + "." + index;
162: Object newTextValue = GridBoxRenderer.getValue(this , item
163: .getText(), null, null);
164: postClientEvent(ITEM_ADD, fullIndex, newTextValue,
165: getQualifiedURL(item.getImage()));
166: if (item.getChildren().size() > 0)
167: renderChildren(item);
168: }
169: }
170:
171: public void componentChange(WebComponentEvent event) {
172: String name = event.getName();
173: String value = (String) event.getValue();
174:
175: if (name.equals(Tree.Item.PROPERTY_ITEM_SELECTED)) {
176: Tree.Item ti = (Tree.Item) fullIndexItem(tree, value);
177: setPropertyChangeIgnored(name, true);
178: ti.setSelected(true);
179: setPropertyChangeIgnored(name, false);
180: } else if (name.equals(Tree.Item.PROPERTY_ITEM_EXPANDED)) {
181: Tree.Item ti = (Tree.Item) fullIndexItem(tree, value
182: .substring(1));
183: setPropertyChangeIgnored(name, true);
184: ti.setExpanded(value.charAt(0) == 't');
185: setPropertyChangeIgnored(name, false);
186: } else {
187: super .componentChange(event);
188: }
189: }
190:
191: static HierarchyComponent.Item fullIndexItem(
192: HierarchyComponent root, String value) {
193: if (value.equals(""))
194: return null;
195: HierarchyComponent.Item ti = (HierarchyComponent.Item) root
196: .getRootItem();
197: if (value.equals("rootItem"))
198: return ti;
199: String ary[] = value.split("\\.");
200:
201: for (int i = 0; i < ary.length; i++) {
202: int index = Integer.parseInt(ary[i]);
203: ti = (HierarchyComponent.Item) ti.getChildren().get(index);
204: }
205:
206: if (ary.length == 0)
207: ti = (HierarchyComponent.Item) ti.getChildren().get(
208: Integer.parseInt(value));
209: return ti;
210: }
211:
212: static String fullIndex(HierarchyComponent.Item item) {
213: if (item == item.getHierarchy().getRootItem())
214: return "rootItem";
215: StringBuilder sb = new StringBuilder(String.valueOf(item
216: .getIndex()));
217: Object root = item.getHierarchy().getRootItem();
218:
219: while (item.getParent() != root) {
220: item = (HierarchyComponent.Item) item.getParent();
221: sb.insert(0, '.').insert(0, item.getIndex());
222: }
223:
224: return sb.toString();
225: }
226:
227: private void prepareInitData(StringBuilder sb) {
228: Tree.Item ri = (Tree.Item) tree.getRootItem();
229: sb.append("{ch:").append(tree.getHeight());
230: sb.append(",cw:").append(tree.getWidth());
231: sb.append(",fo:").append(tree.isFocus());
232: sb.append(",rt:");
233: GridBoxRenderer.getValue(this , ri.getText(), null, sb);
234: sb.append(",ri:\"").append(getQualifiedURL(ri.getImage()))
235: .append('"');
236: sb.append(",re:").append(ri.isExpanded());
237: sb.append(",rv:").append(tree.isRootItemVisible());
238: sb.append(',');
239: prepareInitData(sb, ri);
240: sb.append('}');
241: }
242:
243: private void prepareInitData(StringBuilder sb, Tree.Item tn) {
244: List<Tree.Item> l = tn.getChildren();
245: sb.append("ti:[");
246:
247: for (int i = 0; i < l.size(); i++) {
248: Tree.Item ti = l.get(i);
249:
250: if (ti != null) {
251: sb.append("{ix:").append(i);
252: sb.append(",tm:\"").append(
253: getQualifiedURL(ti.getImage())).append('"');
254: sb.append(",tt:");
255: GridBoxRenderer.getValue(this , ti.getText(), null, sb);
256: sb.append(",te:").append(ti.isExpanded());
257: sb.append(',');
258: prepareInitData(sb, ti);
259: sb.append("},");
260: }
261: }
262:
263: if (sb.charAt(sb.length() - 1) != '[')
264: sb.setCharAt(sb.length() - 1, ']');
265: else
266: sb.append(']');
267: }
268: }
|