/*******************************************************************************
* Copyright (c) 2007 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributor:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.hibernate.eclipse.console.viewers.xpl;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
/**
* TreeViewer has the next lack design to work with the objects as TreeItem id:
* internalFindItems always return array of size <= 1
* cause doFindItems & internalFindItem returns FIRST proper item which it find
* but in common case this is not the TRUTH - so TreeViewer add method doesn't work
* as we expected.
*
* So MTreeViewer fix the problem.
* It redefine add method taking into account what for one parentElementOrTreePath as id
* could exist several ( >= 0 ) widgets.
* doFindItems & internalFindItem returns ALL proper items which it find.
*
* We expect what TreeViewer developers fix the bug in future versions,
* but we can't wait it.
*
* more info is here http://jira.jboss.com/jira/browse/JBIDE-1482
*
* @author Vitali
*/
public class MTreeViewer extends TreeViewer {
public MTreeViewer(Composite parent, int style) {
super(parent, style);
}
public void clearChildren(Object node) {
Widget widget = null;
if (node instanceof Widget) {
widget = (Widget)node;
} else if (node == null) {
widget = getTree();
}
Widget[] items = null;
if (widget == null && node != null) {
items = internalFindItems(node);
} else {
items = getChildren(widget);
}
for (int j = 0; j < items.length; j++) {
clearChildren(items[j]);
if (items[j] instanceof TreeItem) {
((TreeItem)items[j]).setExpanded(false);
((TreeItem)items[j]).clearAll(true);
}
}
}
/**
* Adds the given child elements to this viewer as children of the given
* parent element. If this viewer does not have a sorter, the elements are
* added at the end of the parent's list of children in the order given;
* otherwise, the elements are inserted at the appropriate positions.
* <p>
* This method should be called (by the content provider) when elements have
* been added to the model, in order to cause the viewer to accurately
* reflect the model. This method only affects the viewer, not the model.
* </p>
*
* @param parentElementOrTreePath
* the parent element
* @param childElements
* the child elements to add
*/
public void add(Object parentElementOrTreePath, Object[] childElements) {
Assert.isNotNull(parentElementOrTreePath);
assertElementsNotNull(childElements);
if (isBusy())
return;
Widget[] widgets = internalFindItems2(parentElementOrTreePath);
// If parent hasn't been realized yet, just ignore the add.
if (widgets.length == 0) {
return;
}
for (int i = 0; i < widgets.length; i++) {
internalAdd(widgets[i], parentElementOrTreePath, childElements);
// call this to refresh icon of parent item
updateItem(widgets[i], parentElementOrTreePath);
}
}
/**
* Find the items for the given element of tree path
*
* @param parentElementOrTreePath
* the element or tree path
* @return the items for that element
*/
protected Widget[] internalFindItems2(Object parentElementOrTreePath) {
Widget[] widgets;
if (parentElementOrTreePath instanceof TreePath) {
widgets = internalFindItems(parentElementOrTreePath);
} else {
widgets = findItems2(parentElementOrTreePath);
}
return widgets;
}
/**
* Finds the widgets which represent the given element. The returned array
* must not be changed by clients; it might change upon calling other
* methods on this viewer.
* <p>
* This method was introduced to support multiple equal elements in a viewer
* (@see {@link AbstractTreeViewer}). Multiple equal elements are only
* supported if the element map is enabled by calling
* {@link #setUseHashlookup(boolean)} and passing <code>true</code>.
* </p>
* <p>
* The default implementation of this method tries first to find the widget
* for the given element assuming that it is the viewer's input; this is
* done by calling <code>doFindInputItem</code>. If it is not found
* there, the widgets are looked up in the internal element map provided
* that this feature has been enabled. If the element map is disabled, the
* widget is found via <code>doFindInputItem</code>.
* </p>
*
* @param element
* the element
* @return the corresponding widgets
*/
protected Widget[] findItems2(Object element) {
Widget result = doFindInputItem(element);
if (result != null) {
return new Widget[] { result };
}
// if we have an element map use it, otherwise search for the item.
if (usingElementMap()) {
return findItems2(element);
}
return doFindItems(element);
}
/**
* Recursively tries to find the given element.
*
* @param parent
* the parent item
* @param element
* the element
* @return Widget
*/
protected List<Item> internalFindItem(Item parent, Object element) {
List<Item> ret = new ArrayList<Item>();
// compare with node
Object data = parent.getData();
if (data != null) {
if (equals(data, element)) {
ret.add(parent);
return ret;
}
}
// recurse over children
Item[] items = getChildren(parent);
for (int i = 0; i < items.length; i++) {
Item item = items[i];
List<Item> o = internalFindItem(item, element);
if (null != o) {
ret.addAll(o);
}
}
return ret;
}
protected Widget[] doFindItems(Object element) {
Widget[] ret = new Widget[0];
// compare with root
Object root = getRoot();
if (null == root) {
return ret;
}
List<Widget> res = new ArrayList<Widget>();
Item[] items = getChildren(getControl());
if (items != null) {
for (int i = 0; i < items.length; i++) {
List<Item> o = internalFindItem(items[i], element);
if (null != o) {
res.addAll(o);
}
}
}
ret = res.toArray(new Widget[0]);
return ret;
}
}