JavaFX custom ListCell - javafx-8

I'm trying to animate ListCell when they appear.
Specially I try to animate a new cell when it was just added to the list.
For now it's working pretty OK except when I scroll the ListView, then indexes get messed up and the wrong cell is animated.
I use a boolean flag (entering) in my item model to detect when a cell is used for a brand new item.
public class TimeListCell extends ListCell<MarkItem> {
private static final String BUTTON_GOTO_MARK_CLASS = "but-markgoto";
private static final String LABEL_TIME_MARK_CLASS = "track-time";
private static final String BUTTON_DELETE_MARK_CLASS = "but-markdel";
private static final String MARK_HIGHLIGHT_CURRENT_CLASS = "highlighted";
private Instant time;
private MarkItem markItem;
protected ListCellAnimation anim;
private HBox root = new HBox();
private Button go = new Button();
private Label track = new Label();;
private Button del = new Button();
private ChangeListener<? super Boolean> highlightChange = (e, o, n) -> { setHighlighted(n); };
public TimeListCell (Consumer<MarkItem> onGoto, Consumer<MarkItem> onDelete) {
root.setAlignment(Pos.CENTER);
go.getStyleClass().add(BUTTON_GOTO_MARK_CLASS);
go.setOnAction( e -> {
if (onGoto != null) {
// Trigger GOTO consumer function
onGoto.accept(markItem);
}
});
track.getStyleClass().add(LABEL_TIME_MARK_CLASS);
del.getStyleClass().add(BUTTON_DELETE_MARK_CLASS);
del.setOnAction( e -> {
// First trigger exit animation then delete item
this.animateExit(onDelete);
});
root.getChildren().add(go);
root.getChildren().add(track);
root.getChildren().add(del);
}
#Override
protected void updateItem (final MarkItem item, boolean empty) {
super.updateItem(item, empty);
if (markItem != null) {
markItem.highlightedProperty().removeListener(highlightChange);
}
if (!empty && item != null) {
markItem = item;
time = item.getTime();
track.setText(DateUtil.format(time, DateUtil.Pattern.TIME));
setGraphic(root);
item.highlightedProperty().addListener(highlightChange);
setHighlighted(item.isHighlighted());
if (anim == null) {
//Adding Animation to the ListCell
anim = new ListCellAnimation(this);
//KeyFrame[] f = getKeyFrames(types);
KeyFrame[] frames = null;
if (anim.getKeyFrames().size() == 0) {
KeyFrame[] f = anim.getPopIn(frames);
if (f != null) {
anim.getKeyFrames().addAll(f);
}
}
}
if (item.isEntering()) {
//Checking when to play Animation
animateEnter();
item.setEntering(false);
}
} else {
setGraphic(null);
}
}
/**
* Set/unset cell highlighted style for display.
*
* #param highlighted
* Whether or not to highlight the cell
*/
public void setHighlighted (boolean highlighted) {
track.getStyleClass().remove(MARK_HIGHLIGHT_CURRENT_CLASS);
if (highlighted)
track.getStyleClass().add(MARK_HIGHLIGHT_CURRENT_CLASS);
}
/**
* Animate entering cell.
*/
private void animateEnter() {
if (anim != null && anim.getKeyFrames().size() >= 0
&& (anim.getTimeline().getStatus() == Timeline.Status.STOPPED
|| anim.getTimeline().getStatus() == Timeline.Status.PAUSED)) {
anim.getTimeline().playFromStart();
}
}
/**
* Animate exiting cell.
* Trigger DELETE consumer function when animation is complete.
*/
private void animateExit (Consumer<MarkItem> onDelete) {
anim.getReversedTimeline().setOnFinished( t -> {
// Remove item from list
if (onDelete != null) {
onDelete.accept(markItem);
}
// Prepare cell for next item to use it
scaleXProperty().set(1);
scaleYProperty().set(1);
});
anim.getReversedTimeline().playFromStart();
}
public Instant getTime () {
return time;
}
}
Has anyone any idea of what could mess up the cell indexing ?
Thanks.

If a cell which is animating is reused to display an item that is not "entering", then you need to stop the current animation:
if (item.isEntering()) {
//Checking when to play Animation
animateEnter();
item.setEntering(false);
} else {
anim.getTimeline().stop();
}
In general, you seem to be assuming that any given cell is only ever used for a single item, which is certainly not the case. There may be other bugs in your code that are consequences of this assumption, but this is the main one I can see.

Related

Customize the Xamarin.Forms Picker Popup List

I know how to create a custom renderer to customize the actual text of the Xamarin forms picker, but how do you customize, say, the background color or text of the list that pops up when you click on the picker text box?
You can refer to the following code :
in iOS
using System;
using Xamarin.Forms;
using xxx;
using xxx.iOS;
using UIKit;
using Xamarin.Forms.Platform.iOS;
using Foundation;
[assembly:ExportRenderer(typeof(MyPicker), typeof(MyiOSPicker))]
namespace xxx.iOS
{
public class MyiOSPicker:PickerRenderer,IUIPickerViewDelegate
{
IElementController ElementController => Element as IElementController;
public MyiOSPicker()
{
}
[Export("pickerView:viewForRow:forComponent:reusingView:")]
public UIView GetView(UIPickerView pickerView, nint row, nint component, UIView view)
{
UILabel label = new UILabel
{
//here you can set the style of item!!!
TextColor = UIColor.Blue,
Text = Element.Items[(int)row].ToString(),
TextAlignment = UITextAlignment.Center,
};
return label;
}
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if(Control!=null)
{
UIPickerView pickerView = (UIPickerView)Control.InputView;
pickerView.WeakDelegate = this;
pickerView.BackgroundColor = UIColor.Yellow; //set the background color of pickerview
}
}
}
}
in Android
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using xxx;
using xxx.Droid;
using Android.Widget;
using Android.App;
using System.Linq;
[assembly: ExportRenderer(typeof(MyPicker), typeof(MyAndroidPicker))]
namespace xxx.Droid
{
public class MyAndroidPicker:PickerRenderer
{
IElementController ElementController => Element as IElementController;
public MyAndroidPicker()
{
}
private AlertDialog _dialog;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || e.OldElement != null)
return;
Control.Click += Control_Click;
}
protected override void Dispose(bool disposing)
{
Control.Click -= Control_Click;
base.Dispose(disposing);
}
private void Control_Click(object sender, EventArgs e)
{
Picker model = Element;
var picker = new NumberPicker(Context);
if (model.Items != null && model.Items.Any())
{
// set style here
picker.MaxValue = model.Items.Count - 1;
picker.MinValue = 0;
picker.SetBackgroundColor(Android.Graphics.Color.Yellow);
picker.SetDisplayedValues(model.Items.ToArray());
picker.WrapSelectorWheel = false;
picker.Value = model.SelectedIndex;
}
var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical };
layout.AddView(picker);
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, true);
var builder = new AlertDialog.Builder(Context);
builder.SetView(layout);
builder.SetTitle(model.Title ?? "");
builder.SetNegativeButton("Cancel ", (s, a) =>
{
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
// It is possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
_dialog = null;
});
builder.SetPositiveButton("Ok ", (s, a) =>
{
ElementController.SetValueFromRenderer(Picker.SelectedIndexProperty, picker.Value);
// It is possible for the Content of the Page to be changed on SelectedIndexChanged.
// In this case, the Element & Control will no longer exist.
if (Element != null)
{
if (model.Items.Count > 0 && Element.SelectedIndex >= 0)
Control.Text = model.Items[Element.SelectedIndex];
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
// It is also possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
}
_dialog = null;
});
_dialog = builder.Create();
_dialog.DismissEvent += (ssender, args) =>
{
ElementController?.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
};
_dialog.Show();
}
}
}

Only 1 button enable in ListView, custom adapter

I run into a problem where I need to enable my button in the ListView. The weird thing is :
public class CookingStepAdapter extends ArrayAdapter<CookingStep> {
...
private void addButtonToList(Button clock, Button skip){
if (list_clock_button == null) {
list_clock_button = new ArrayList<Button>();
iterate = 0;
}
if (list_skip_button == null)
list_skip_button = new ArrayList<Button>();
list_clock_button.add(clock);
list_skip_button.add(skip);
clock.setEnabled(true);
skip.setEnabled(true);
list_clock_button.get(0).setEnabled(true);
list_clock_button.get(0).setFocusable(true);
list_clock_button.get(0).setFocusableInTouchMode(true);
list_clock_button.get(0).invalidate();
list_skip_button.get(0).setEnabled(true);
list_skip_button.get(0).setFocusable(true);
list_skip_button.get(0).setFocusableInTouchMode(true);
list_skip_button.get(0).postInvalidate();
}
}
When I set enable with the list_clock_button.get(0), it's not working at all. But clock.setEnabled(true); actually worked.
But then I only want the first button of the ListView enabled, that makes the first option more fit in this situation. The second option works, but it made all the buttons enabled, that's not what I want. I did recheck the first button address, and it matched list_clock_button.get(0), why it's not working.
EDIT :
Here's my function getView :
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.cooking_steps_and_timer, parent, false);
final Button button = (Button) rowView.findViewById(R.id.button_timer);
final TextView timer = (TextView) rowView.findViewById(R.id.cooking_timer);
final Button skipButton = (Button) rowView.findViewById(R.id.button_skip);
TextView stepContent = (TextView) rowView.findViewById(R.id.cooking_step_content);
final CookingStep step = list.get(position);
String stepOrder = (String) context.getResources().getText(R.string.step_order) + " " + step.getOrder();
String content = "<b>" + stepOrder + ":</b>" + " " + step.getContent() + "\n";
stepContent.setText(Html.fromHtml(content));
if (step.getTimer() == null || step.getTimer() == 0){
timer.setVisibility(View.GONE);
button.setVisibility(View.GONE);
skipButton.setVisibility(View.GONE);
} else {
//myTimer = new CookingTimer(step.getTimer());
timer.setText(step.getMyTimer().toString());
step.setCountDown(new CountDownTimer(step.getTimer() * 60000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
step.getMyTimer().tick();
timer.setText(step.getMyTimer().toString());
}
#Override
public void onFinish() {
nextButtonEnable();
}
});
button.setText(R.string.button_available);
skipButton.setText(R.string.skip_button_content);
addButtonToList(button, skipButton);
//button.setEnabled(true);
list_clock_button.get(0).setEnabled(true);
button.requestFocusFromTouch();
button.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
if (!button.isPressed()) {
button.setPressed(true);
button.setText(R.string.button_available);
step.getCountDown().start();
} else {
button.setPressed(false);
button.setText(R.string.button_pressed);
step.getCountDown().cancel();
}
}
return true;
}
});
skipButton.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
step.getCountDown().cancel();
nextButtonEnable();
}
return true;
}
});
}
return rowView;
}
The reason of all buttons getting enabled is you are setting all of them enable yourself. To set any particular button enable you have to check the id or the item position to set that particular button to enable.
Like this for example:-
if(clock.getID == R.id.some_id_in_xml){
clock.setEnabled(true);
}
But before that you have to check the position of the item to select the desired item in the list.

How to apply like search on GWT cell table?

I am using GWT 2.3.I which I am using GWT cell table.
Here below is the code for my cell table:
public class FormGrid extends SuperGrid {
List<Form> formList;
#Override
public void setColumns(CellTable table) {
TextColumn<Form> nameColumn = new TextColumn<Form>() {
#Override
public String getValue(Form object) {
return object.getName();
}
};
table.addColumn(nameColumn, "Name");
}
#Override
public void setData() {
if (formList != null && formList.size() > 0) {
AsyncDataProvider<Form> provider = new AsyncDataProvider<Form>() {
#Override
protected void onRangeChanged(HasData<Form> display) {
int start = display.getVisibleRange().getStart();
int end = start + display.getVisibleRange().getLength();
end = end >= formList.size() ? formList.size() : end;
List<Form> sub = formList.subList(start, end);
updateRowData(start, sub);
}
};
provider.addDataDisplay(getTable());
provider.updateRowCount(formList.size(), true);
}
}
public List<Form> getFormList() {
return formList;
}
public void setFormList(List<Form> formList) {
this.formList = formList;
}
}
In this my set column and set data will be called fro super class flow.This cell table is working fine.
Now I want to put a filter type facility (like search) in this cell table.It should be like, there is a texbox above the cell table and what ever written in that text box, it should fire a like query to all form name for that text box value.
for example I have 1000 form in the grid.Now if user writes 'app' in some filter textbox above the cell table the all the form which have 'app' in there name will be filtered and grid has only those forms only.
This is the first case:
Another case is I am only render one column in grid name.I have two more properties in form (description,tag).But I am not rendering them.now for filter if user writes 'app' in filter box then it should make a query to all three (name, description, and tag) and should return if 'app' matched to any of three.
I am not getting how to apply filter in cell table.
Please help me out.Thanks in advance.
You can find an implementation in the expenses sample.
Here is a short summary of the steps
1.) Create a Textbox and a SearchButton.
2.) add a clickHandler to the SearchButton (You can also add KeyUpHandler to the Textbox alternatively)
searchButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
search();
}
});
3.) In the search function retrieve the searchString and store it.
private void search() {
searchString = searchBox.getText();
setData();
}
4.) modify your setdata() function to take searchString into account
#Override
public void setData() {
if (formList != null && formList.size() > 0) {
AsyncDataProvider<Form> provider = new AsyncDataProvider<Form>() {
#Override
protected void onRangeChanged(HasData<Form> display) {
int start = display.getVisibleRange().getStart();
int end = start + display.getVisibleRange().getLength();
//new function if searchString is specified take into account
List<Form> sub = getSubList(start,end);
end = end >= sub.size() ? sub.size() : end;
updateRowData(sub.subList(start, end);, sub);
}
};
provider.addDataDisplay(getTable());
provider.updateRowCount(formList.size(), true);
}
}
private List<Form> getSubList(int start, int end) {
List<Form> filtered_list = null;
if (searchString != null) {
filtered_list= new ArrayList<Form>();
for (Form form : formList) {
if (form.getName().equals(searchString) || form.getTag().equals(searchString) || form.getDescription().equals(searchString))
filtered_list.add(form);
}
}
else
filtered_list = formList;
return filtered_list;
}
can propose another solution what can be used quite easy multiple times.
Idea is to create custom provider for your celltable.
GWT celltable filtering
Video in this post shows it in action.
Here is the part of code of custom list data provider which u have to implement.
#Override
protected void updateRowData(HasData display, int start, List values) {
if (!hasFilter() || filter == null) { // we don't need to filter, so call base class
super.updateRowData(display, start, values);
} else {
int end = start + values.size();
Range range = display.getVisibleRange();
int curStart = range.getStart();
int curLength = range.getLength();
int curEnd = curStart + curLength;
if (start == curStart || (curStart < end && curEnd > start)) {
int realStart = curStart < start ? start : curStart;
int realEnd = curEnd > end ? end : curEnd;
int realLength = realEnd - realStart;
List<t> resulted = new ArrayList<t>(realLength);
for (int i = realStart - start; i < realStart - start + realLength; i++) {
if (filter.isValid((T) values.get(i), getFilter())) {
resulted.add((T) values.get(i));
}
}
display.setRowData(realStart, resulted);
display.setRowCount(resulted.size());
}
}
}

GWT CEll Browser Real Time Update

has someone been able to correctly to update a cell browser at runtime, i.e. when u remove a node or add a node, the change is reflected immediately in the CEll Browser, because I am using a List and when i am making a change it is not being updated on the spot
You can use ListDataProvider setList(...) method for dynamic updates. Here is an example how I update cell browser via RPC:
private void loadAllData(final ListDataProvider<Data> dataProvider) {
dBservice.getAllData(new AsyncCallback<List<Data>>() {
public void onSuccess(List<Data> result) {
dataProvider.setList(result);
}
public void onFailure(Throwable caught) {
caught.printStackTrace();
}
});
}
to refresh a cellBrowser you have to close all the child on the root node.
anyway something like this
for (int i = 0; i < cellBrowser.getRootTreeNode().getChildCount(); i++) {
cellBrowser.getRootTreeNode().setChildOpen(i, false);
}
the AsyncDataProvider calls refreshes data
private final class Model implements TreeViewModel{
private List<ZonaProxy> zonaList = null;
private List<CategoriaProxy> categoriaList = null;
public void setCategoriaList(List<CategoriaProxy> categoriaList) {
this.categoriaList = categoriaList;
}
public void setListZona(List<ZonaProxy> zonaList) {
this.zonaList = zonaList;
}
#SuppressWarnings({ "unchecked", "rawtypes" })
public <T> NodeInfo<?> getNodeInfo(T value) {
CategoryDataProvider dataProvider1 = new CategoryDataProvider();
return new DefaultNodeInfo(dataProvider1, new CategoriaCell());
}
/**
* Check if the specified value represents a leaf node. Leaf nodes cannot be
* opened.
*/
public boolean isLeaf(Object value) {
if (value instanceof CategoriaProxy){
if (((CategoriaProxy) value).getLivello() == 3) {
return true;
}
}
return false;
}
}
private class CategoryDataProvider extends AsyncDataProvider<CategoriaProxy>
{
#Override
protected void onRangeChanged(HasData<CategoriaProxy> display)
{
requests.categoriaRequest().findAllCategorias(0, 8).with().fire(new Receiver<List<CategoriaProxy>>() {
#Override
public void onSuccess(List<CategoriaProxy> values) {
updateRowCount(values.size(), true);
updateRowData(0, values);
}
});
}
}
it Works.
Apparently it is not enough to change the data provider and refresh it.
You need also to force the affected cell to close and reopen it, as in this example
public void updateCellBrowser(String id) {
TreeNode node = getNode(cellBrowser.getRootTreeNode(),id);
if(node != null && ! node.isDestroyed()) {
TreeNode parent = node.getParent();
int index = node.getIndex();
parent.setChildOpen(index, false,true);
parent.setChildOpen(index, true, true);
}
}
In my particular example the cell ids are pathnames hence the following
implementation of getNode().
private TreeNode getNode(TreeNode node, String id) {
for(int i=0; i < node.getChildCount(); i++)
if(node.isChildOpen(i)) {
Object value = node.getChildValue(i);
if(value instanceof String) {
String nodeId = ((String) value);
if(id.equals(nodeId))
return node.setChildOpen(i, true);
if(id.startsWith(nodeId))
getNode(node.setChildOpen(i, true),id);
}
}
return null;
}

GWT: Select a TreeItem with right click

I'm capturing a right click event to show a context menu. What I haven't been able to figure out, is how to make the right click actually select the TreeItem, prior to showing of context menu.
All help is appreciated.
private Tree tree = new Tree() {
#Override
public void onBrowserEvent(Event event) {
if (event.getTypeInt() == Event.ONCONTEXTMENU) {
DOM.eventPreventDefault(event);
showContextMenu(event);
}
super.onBrowserEvent(event);
}
#Override
protected void setElement(Element elem) {
super.setElement(elem);
sinkEvents(Event.ONCONTEXTMENU);
}
};
ONMOUSEDOWN event gets fired before ONCONTEXTMENU. Have you tried to listen for onMouseDown events, and set the selected item? Something along these lines:
#Override
public void onBrowserEvent(Event event) {
switch (DOM.eventGetType(event)) {
case Event.ONMOUSEDOWN:
if (DOM.eventGetButton(event) == Event.BUTTON_RIGHT) {
TreeItem selectedItem = findSelectedItem(event);
if (selectedItem != null) {
selectedItem.setSelected(true);
}
} else {
super.onBrowserEvent(event);
}
break;
case Event.ONCONTEXTMENU:
showContextMenu(event);
break;
default:
super.onBrowserEvent(event);
break;
}
and findSelectedItem traverses the tree looking for the selected item:
TreeItem findSelectedItem(Event e) {
return findSelectedItemRecursive(event.getClientX(), event.getClientY());
}
TreeItem findSelectedTreeItemRecursive(TreeItem root, int x, int y) {
if (null == root) {
int count = getItemCount();
for (int i = 0; i < count; i++) {
TreeItem selected = findSelectedTreeItemRecursive(getItem(i), x, y);
if (selected != null) {
return selected;
}
}
return null;
}
int count = item.getChildCount();
for (int i = 0; i < count; i++) {
TreeItem selected = findSelectedTreeItem(item.getChild(i), x, y);
if (selected != null) {
return selected;
}
}
if (x >= item.getAbsoluteLeft()
&& x <= item.getAbsoluteLeft() + item.getOffsetWidth()
&& y >= item.getAbsoluteTop()
&& y <= item.getAbsoluteTop() + item.getOffsetHeight()) {
return item;
}
return null;
}
You can use dedicated overloaded TreeItem :
public class MyTreeItem extends TreeItem implements ContextMenuHandler {
public SBTreeItem(SBItemTree tree, String name) {
super();
Label w = new Label(name);
w.addDomHandler(this, ContextMenuEvent.getType());
setWidget(w);
}
public void onContextMenu(ContextMenuEvent event) {
Window.alert(getSBItem().getName());
event.getNativeEvent().stopPropagation();
}
}
I'd just like to add a couple of links leading to issues about this:
http://code.google.com/p/google-web-toolkit/issues/detail?id=4529&q=right%20click%20selection
http://code.google.com/p/google-web-toolkit/issues/detail?id=4604&q=right%20click%20selection
I know this is an old question, but hopefully here's an answer that will save time for the masses hitting this page from a Google search. IMO, the best way is to use Google's own internal tree searching code -- it's a solution that scales very well with the number of elements in the tree. I
am using GWT 2.5.1.
private void initTree() {
tree = new Tree() {
#Override
public void onBrowserEvent(Event event) {
/*
* If the event is a context menu event, we want the tree item
* to also be selected.
*
* This logic must occur before the call to the superclass
* method so the selection is updated before the context menu
* logic executes. This is useful when we want to make items in
* the context menu invisible/disabled based on the selection.
*/
if (DOM.eventGetType(event) == Event.ONCONTEXTMENU) {
if (getItemCount() > 0) {
// In my use case there is only 1 top-level tree item
TreeItem root = getItem(0);
// Taken from com.google.gwt.user.client.ui.Tree.elementClicked(Element):
ArrayList<Element> chain = new ArrayList<Element>();
collectElementChain(chain, getElement(), DOM.eventGetTarget(event));
TreeItem selection = findItemByChain(chain, 0, root);
/*
* For some reason SelectionEvent will only fire if
* selection is non-null; I am firing the selection
* event manually because I want to know when there has
* been a deselection of an item in the tree.
*/
if (selection != null) {
this.setSelectedItem(selection);
} else {
SelectionEvent.fire(this, null);
}
}
}
super.onBrowserEvent(event);
}
};
tree.setAnimationEnabled(true);
}
//// BEGIN code copied from com.google.gwt.user.client.ui.Tree:
/**
* Collects parents going up the element tree, terminated at the tree root.
*/
private void collectElementChain(ArrayList<Element> chain, Element hRoot,
Element hElem) {
if ((hElem == null) || (hElem == hRoot)) {
return;
}
collectElementChain(chain, hRoot, DOM.getParent(hElem));
chain.add(hElem);
}
private TreeItem findItemByChain(ArrayList<Element> chain, int idx,
TreeItem root) {
if (idx == chain.size()) {
return root;
}
Element hCurElem = chain.get(idx);
for (int i = 0, n = root.getChildCount(); i < n; ++i) {
TreeItem child = root.getChild(i);
if (child.getElement() == hCurElem) {
TreeItem retItem = findItemByChain(chain, idx + 1,
root.getChild(i));
if (retItem == null) {
return child;
}
return retItem;
}
}
return findItemByChain(chain, idx + 1, root);
}
//// END