001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: * Darrell Meyer <darrell@mygwt.net> - derived implementation
011: * Tom Schindl <tom.schindl@bestsolution.at> - bugfix in issues: 40
012: *******************************************************************************/package net.mygwt.ui.client.viewer;
013:
014: import java.util.ArrayList;
015: import java.util.HashMap;
016: import java.util.Iterator;
017: import java.util.List;
018: import java.util.Map;
019:
020: import net.mygwt.ui.client.Events;
021: import net.mygwt.ui.client.event.BaseEvent;
022: import net.mygwt.ui.client.event.Listener;
023: import net.mygwt.ui.client.widget.Component;
024: import net.mygwt.ui.client.widget.tree.Tree;
025: import net.mygwt.ui.client.widget.tree.TreeItem;
026:
027: import com.google.gwt.user.client.DOM;
028: import com.google.gwt.user.client.Element;
029: import com.google.gwt.user.client.ui.Widget;
030:
031: /**
032: * An interface to content providers for tree-structure-oriented viewers.
033: *
034: * Content providers for tree viewers must implement either the
035: * <code>ITreeContentProvider</code> interface or the
036: * <code>IAsyncTreeContentProvider</code> interface.
037: *
038: * @see TreeViewer
039: */
040: public class TreeViewer extends Viewer implements ICheckable,
041: ITreeViewer {
042:
043: protected boolean checkable;
044: private Tree tree;
045: private List checkChangeListener;
046: private boolean caching = true;
047: private boolean force;
048: private Map nodeMap = new HashMap();
049:
050: /**
051: * Creates a new tree viewer instance.
052: *
053: * @param tree the underlying tree widget
054: */
055: public TreeViewer(Tree tree) {
056: this .tree = tree;
057: // force render
058: tree.getElement();
059: hookWidget(tree);
060:
061: Listener l = new Listener() {
062: public void handleEvent(BaseEvent be) {
063: switch (be.type) {
064: case Events.BeforeExpand: {
065: TreeItem item = (TreeItem) be.widget;
066: Object loaded = item.getData("loaded");
067: if (loaded == null) {
068: be.doit = false;
069: loadChildren(item, true);
070: }
071: break;
072: }
073: case Events.Collapse: {
074: if (!caching) {
075: TreeItem item = (TreeItem) be.widget;
076: int count = item.getItemCount();
077: for (int i = 0; i < count; i++) {
078: item.remove(item.getItem(0));
079: }
080: item.setData("loaded", null);
081: }
082: break;
083: }
084:
085: }
086: }
087: };
088:
089: tree.addListener(Events.BeforeExpand, l);
090: tree.addListener(Events.Collapse, l);
091: }
092:
093: public void add(Object parent, Object child) {
094: TreeItem p = (TreeItem) findItem(parent);
095: internalAdd(p, child, p.getItemCount());
096: }
097:
098: public void addCheckStateListener(ICheckStateListener listener) {
099: if (checkChangeListener == null) {
100: checkChangeListener = new ArrayList();
101: }
102: if (!checkChangeListener.contains(listener)) {
103: checkChangeListener.add(listener);
104: }
105: }
106:
107: /**
108: * Applies the viewer's filters to the current elements.
109: */
110: public void applyFilters() {
111: filterItems(tree.getRootItem());
112: }
113:
114: public Widget findItem(Object elem) {
115: TreeItem[] items = tree.getAllItems();
116: for (int i = 0; i < items.length; i++) {
117: TreeItem item = items[i];
118: Object data = item.getData();
119:
120: if (getComparer() != null) {
121: if (getComparer().equals(elem, data)) {
122: return item;
123: } else {
124: continue;
125: }
126: }
127:
128: if (elem == data || (elem != null && elem.equals(data))) {
129: return item;
130: }
131: }
132: return null;
133: }
134:
135: /**
136: * Returns <code>true</code> if the viewer is caching.
137: *
138: * @return the caching state
139: */
140: public boolean getCaching() {
141: return caching;
142: }
143:
144: public boolean getChecked(Object element) {
145: TreeItem item = (TreeItem) findItem(element);
146:
147: if (item != null) {
148: return item.isChecked();
149: }
150:
151: return false;
152: }
153:
154: /**
155: * Returns the current checked selection.
156: *
157: * @return the checked elements
158: */
159: public ISelection getCheckedSelection() {
160: TreeItem[] items = tree.getChecked();
161: if (items.length == 0) {
162: return DefaultSelection.EMPTY;
163: }
164: ArrayList checked = new ArrayList();
165: for (int i = 0; i < items.length; i++) {
166: checked.add(items[i].getData());
167: }
168: return new DefaultSelection(checked);
169: }
170:
171: /**
172: * Returns the viewer's tree widget.
173: *
174: * @return the tree
175: */
176: public Tree getTree() {
177: return tree;
178: }
179:
180: public Widget getWidget() {
181: return tree;
182: }
183:
184: public void insert(Object parent, Object child, int position) {
185: TreeItem p = (TreeItem) findItem(parent);
186: internalAdd(p, child, position);
187: }
188:
189: /**
190: * Refreshes this viewer starting with the given element.
191: *
192: * @param elem the element
193: */
194: public void refresh(Object elem) {
195: TreeItem item = (TreeItem) findItem(elem);
196: if (item != null) {
197: int count = item.getItemCount();
198: for (int i = 0; i < count; i++) {
199: item.remove(item.getItem(0));
200: }
201: item.setData("loaded", null);
202: loadChildren(item, item.isExpanded());
203: }
204: }
205:
206: public void remove(Object element) {
207: TreeItem item = (TreeItem) findItem(element);
208: if (item != null) {
209: TreeItem parent = item.getParentItem();
210: parent.remove(item);
211: removeElement(item.getData());
212:
213: nodeMap.remove(element);
214: item.setData(null);
215: }
216: }
217:
218: public void removeCheckStateListener(ICheckStateListener listener) {
219: if (checkChangeListener != null) {
220: checkChangeListener.remove(listener);
221: }
222: }
223:
224: /**
225: * Selects the elements.
226: *
227: * @param elem the element to be selected
228: */
229: public void select(Object elem) {
230: TreeItem item = (TreeItem) findItem(elem);
231: tree.getSelectionModel().select(item);
232: }
233:
234: /**
235: * Sets whether the children should be cached after first being retrieved from
236: * the content provider. When <code>false</code>, the tree items will be
237: * removed when collapsed.Default value is <code>true</code>.
238: *
239: * @param caching
240: */
241: public void setCaching(boolean caching) {
242: this .caching = caching;
243: }
244:
245: public boolean setChecked(Object element, boolean state) {
246: TreeItem item = (TreeItem) findItem(element);
247:
248: if (item != null) {
249: item.setChecked(state);
250: return true;
251: }
252:
253: return false;
254: }
255:
256: public void setContentProvider(IContentProvider contentProvider) {
257: super .setContentProvider(contentProvider);
258: if (contentProvider instanceof ICheckable) {
259: checkable = true;
260: }
261: }
262:
263: /**
264: * Sets the expanded state for the element.
265: *
266: * @param element the element
267: * @param expanded the expand state
268: */
269: public void setExpanded(Object element, boolean expanded) {
270: TreeItem item = (TreeItem) findItem(element);
271: if (item != null) {
272: item.setExpanded(expanded);
273: }
274: }
275:
276: public void setInput(final Object input) {
277: ITreeContentProvider cp = (ITreeContentProvider) getContentProvider();
278: cp.inputChanged(this , this .input, input);
279: this .input = input;
280: cp.getChildren(input, new IAsyncContentCallback() {
281: public void setElements(Object[] elems) {
282: elements = elems;
283: onInputReceived(input);
284: }
285: });
286: }
287:
288: public void setInput(Object input, boolean force) {
289: this .force = force;
290: setInput(input);
291: }
292:
293: public void setSelection(ISelection selection, boolean reveal) {
294: List selected = selection.toList();
295: tree.getSelectionModel().deselectAll();
296: TreeItem[] items = tree.getAllItems();
297: for (int i = 0; i < items.length; i++) {
298: TreeItem item = items[i];
299: Object elem = item.getData();
300: if (selected.contains(elem)) {
301: tree.getSelectionModel().select(item);
302: }
303: }
304: }
305:
306: public void update() {
307: TreeItem root = tree.getRootItem();
308: TreeItem[] items = tree.getAllItems();
309: for (int i = 0; i < items.length; i++) {
310: TreeItem item = items[i];
311: if (item != root) {
312: updateInternal(item);
313: }
314: }
315: }
316:
317: public void update(Object elem) {
318: TreeItem item = (TreeItem) findItem(elem);
319: if (item != null) {
320: item.setData(elem);
321: updateInternal(item);
322: }
323: }
324:
325: protected void add(Object elem) {
326:
327: }
328:
329: protected Object[] filter(Object[] elements) {
330: filterItems(tree.getRootItem());
331: return null;
332: }
333:
334: protected List getSelectedFromWidget() {
335: ArrayList elems = new ArrayList();
336: TreeItem[] items = tree.getSelection();
337: for (int i = 0; i < items.length; i++) {
338: TreeItem item = items[i];
339: elems.add(item.getData());
340: }
341: return elems;
342: }
343:
344: protected void hookWidget(Component widget) {
345: super .hookWidget(widget);
346: widget.addListener(Events.CheckChange, new Listener() {
347:
348: public void handleEvent(BaseEvent be) {
349: fireCheckStateChanged(be);
350: }
351: });
352: }
353:
354: protected void insert(Object elem, int index) {
355: // do nothing
356: }
357:
358: protected void onInputReceived(Object input) {
359: renderTree();
360: }
361:
362: protected TreeItem renderItem(TreeItem parent, Object elem,
363: int position) {
364: boolean hasChildren = false;
365:
366: hasChildren = ((ITreeContentProvider) getContentProvider())
367: .hasChildren(elem);
368:
369: LabelProvider lp = (LabelProvider) getLabelProvider();
370:
371: TreeItem item = new TreeItem();
372: item.setData(elem);
373: item.setText(lp.getText(elem));
374: item.setIconStyle(lp.getIconStyle(elem));
375: item.setTextStyle(lp.getTextStyle(elem));
376: item.setLeaf(!hasChildren);
377:
378: nodeMap.put(elem, item);
379:
380: if (checkable) {
381: item.setChecked(((ICheckable) getContentProvider())
382: .getChecked(elem));
383: }
384:
385: if (position == -1) {
386: parent.add(item);
387: } else {
388: parent.insert(item, position);
389: }
390:
391: return item;
392: }
393:
394: private void filterItems(TreeItem item) {
395: if (item.isRoot() || isOrDecendantSelected(item.getData())) {
396: item.setVisible(true);
397: int count = item.getItemCount();
398: for (int i = 0; i < count; i++) {
399: filterItems(item.getItem(i));
400: }
401: } else {
402: item.setVisible(false);
403: }
404: }
405:
406: private void fireCheckStateChanged(BaseEvent be) {
407: if (checkChangeListener != null) {
408: TreeItem item = (TreeItem) be.widget;
409:
410: CheckStateChangedEvent evt = new CheckStateChangedEvent(
411: this , item.getData(), item.isChecked());
412: Iterator it = checkChangeListener.iterator();
413:
414: while (it.hasNext()) {
415: ((ICheckStateListener) it.next())
416: .checkStateChanged(evt);
417: }
418: }
419: }
420:
421: private void internalAdd(TreeItem parent, Object elem, int position) {
422: parent.setLeaf(false);
423: // if the children have not been loaded then do nothing
424: if (parent.getData("loaded") != null) {
425: renderItem(parent, elem, position);
426: if (getSorter() != null) {
427: sortChildren(parent);
428: }
429: filterItems(parent);
430: }
431: }
432:
433: private boolean isOrDecendantSelected(Object elem) {
434: if (!isFiltered(null, elem)) {
435: return true;
436: }
437:
438: TreeItem item = (TreeItem) nodeMap.get(elem);
439: if (item != null) {
440: int count = item.getItemCount();
441: for (int i = 0; i < count; i++) {
442: TreeItem child = item.getItem(i);
443: boolean result = isOrDecendantSelected(child.getData());
444: if (result) {
445: return true;
446: }
447: }
448: }
449: return false;
450: }
451:
452: private void loadChildren(final TreeItem item, final boolean expand) {
453:
454: ITreeContentProvider acp = (ITreeContentProvider) getContentProvider();
455:
456: // if there is an async call out for my children already, I want to make
457: // sure that I don't make another call and load the same items twice
458: if (!item.isEnabled()) {
459: return;
460: }
461: item.setEnabled(false);
462:
463: if (expand) {
464: item.getUI().onLoadingChange(true);
465: }
466:
467: acp.getChildren(item.getData(), new IAsyncContentCallback() {
468:
469: public void setElements(Object[] children) {
470: boolean rendered = item.isRendered();
471: if (rendered) {
472: item.getUI().onLoadingChange(false);
473: }
474: if (children == null) {
475: item.setData("state", null);
476: return;
477: }
478: item.setEnabled(true);
479: // children = filterChildren(item, children);
480: sortElements(children);
481:
482: boolean f = item.getData("force") != null;
483: item.setData("force", null);
484:
485: for (int i = 0; i < children.length; i++) {
486: TreeItem child = renderItem(item, children[i], -1);
487: if (f) {
488: child.setData("force", "true");
489: loadChildren(child, false);
490: }
491: }
492: item.setData("loaded", "true");
493: if (item.hasChildren()) {
494: item.setExpanded(expand);
495: } else {
496: item.setLeaf(true);
497: if (rendered) {
498: item.getUI().updateJoint();
499: }
500: }
501:
502: }
503: });
504:
505: }
506:
507: private void renderTree() {
508: TreeItem root = tree.getRootItem();
509: root.setData(input);
510:
511: int count = root.getItemCount();
512: for (int i = 0; i < count; i++) {
513: root.remove(root.getItem(0));
514: }
515:
516: nodeMap.clear();
517:
518: Object[] elems = elements;
519: elems = sortElements(elems);
520: for (int i = 0; i < elems.length; i++) {
521: Object elem = elems[i];
522: TreeItem item = null;
523: // if (getFilters().length > 0) {
524: // if (isOrDecendantSelected(elem)) {
525: // item = renderItem(root, elem, -1);
526: // }
527: // } else {
528: item = renderItem(root, elem, -1);
529: // }
530: if (force && item != null) {
531: item.setData("force", "true");
532: loadChildren(item, false);
533: }
534: }
535: force = false;
536: }
537:
538: private void sortChildren(TreeItem parent) {
539: Object[] elems = new Object[parent.getItemCount()];
540: Element p = parent.getContainer();
541: for (int i = 0; i < elems.length; i++) {
542: TreeItem item = parent.getItem(i);
543: DOM.removeChild(p, item.getElement());
544: elems[i] = item.getData();
545: }
546: sortElements(elems);
547:
548: for (int i = 0; i < elems.length; i++) {
549: TreeItem item = (TreeItem) findItem(elems[i]);
550: Element elem = item.getElement();
551: DOM.appendChild(p, elem);
552: }
553:
554: }
555:
556: private void updateInternal(TreeItem item) {
557: LabelProvider lp = (LabelProvider) getLabelProvider();
558: Object elem = item.getData();
559:
560: item.setText(lp.getText(elem));
561: item.setIconStyle(lp.getIconStyle(elem));
562: item.setTextStyle(lp.getTextStyle(elem));
563: }
564: }
|