org.openelis.gwt.widget.tree.TreeWidget.java Source code

Java tutorial

Introduction

Here is the source code for org.openelis.gwt.widget.tree.TreeWidget.java

Source

/** Exhibit A - UIRF Open-source Based Public Software License.
* 
* The contents of this file are subject to the UIRF Open-source Based
* Public Software License(the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
* openelis.uhl.uiowa.edu
* 
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
* 
* The Original Code is OpenELIS code.
* 
* The Initial Developer of the Original Code is The University of Iowa.
* Portions created by The University of Iowa are Copyright 2006-2008. All
* Rights Reserved.
* 
* Contributor(s): ______________________________________.
* 
* Alternatively, the contents of this file marked
* "Separately-Licensed" may be used under the terms of a UIRF Software
* license ("UIRF Software License"), in which case the provisions of a
* UIRF Software License are applicable instead of those above. 
*/
package org.openelis.gwt.widget.tree;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.openelis.ui.common.Util;
import org.openelis.gwt.event.BeforeDragStartEvent;
import org.openelis.gwt.event.BeforeDragStartHandler;
import org.openelis.gwt.event.BeforeDropHandler;
import org.openelis.gwt.event.DragStartHandler;
import org.openelis.gwt.event.DropEnterHandler;
import org.openelis.gwt.event.DropHandler;
import org.openelis.gwt.event.HasDropController;
import org.openelis.gwt.event.NavigationSelectionEvent;
import org.openelis.gwt.event.NavigationSelectionHandler;
import org.openelis.gwt.screen.ScreenPanel;
import org.openelis.gwt.screen.TabHandler;
import org.openelis.gwt.widget.CalendarLookUp;
import org.openelis.gwt.widget.CheckBox;
import org.openelis.gwt.widget.Dropdown;
import org.openelis.gwt.widget.Field;
import org.openelis.gwt.widget.HasField;
import org.openelis.gwt.widget.Label;
import org.openelis.gwt.widget.NavigationWidget;
import org.openelis.gwt.widget.table.ColumnComparator;
import org.openelis.gwt.widget.table.TableColumn;
import org.openelis.gwt.widget.table.TableDataCell;
import org.openelis.gwt.widget.table.TableDataRow;
import org.openelis.gwt.widget.table.event.BeforeCellEditedEvent;
import org.openelis.gwt.widget.table.event.BeforeCellEditedHandler;
import org.openelis.gwt.widget.table.event.BeforeRowAddedEvent;
import org.openelis.gwt.widget.table.event.BeforeRowAddedHandler;
import org.openelis.gwt.widget.table.event.BeforeRowDeletedEvent;
import org.openelis.gwt.widget.table.event.BeforeRowDeletedHandler;
import org.openelis.gwt.widget.table.event.BeforeRowMovedEvent;
import org.openelis.gwt.widget.table.event.BeforeRowMovedHandler;
import org.openelis.gwt.widget.table.event.BeforeSortEvent;
import org.openelis.gwt.widget.table.event.BeforeSortHandler;
import org.openelis.gwt.widget.table.event.CellEditedEvent;
import org.openelis.gwt.widget.table.event.CellEditedHandler;
import org.openelis.gwt.widget.table.event.HasBeforeCellEditedHandlers;
import org.openelis.gwt.widget.table.event.HasBeforeRowAddedHandlers;
import org.openelis.gwt.widget.table.event.HasBeforeRowDeletedHandlers;
import org.openelis.gwt.widget.table.event.HasBeforeRowMovedHandlers;
import org.openelis.gwt.widget.table.event.HasBeforeSortHandlers;
import org.openelis.gwt.widget.table.event.HasCellEditedHandlers;
import org.openelis.gwt.widget.table.event.HasRowAddedHandlers;
import org.openelis.gwt.widget.table.event.HasRowDeletedHandlers;
import org.openelis.gwt.widget.table.event.HasRowMovedHandlers;
import org.openelis.gwt.widget.table.event.HasSortHandlers;
import org.openelis.gwt.widget.table.event.HasUnselectionHandlers;
import org.openelis.gwt.widget.table.event.RowAddedEvent;
import org.openelis.gwt.widget.table.event.RowAddedHandler;
import org.openelis.gwt.widget.table.event.RowDeletedEvent;
import org.openelis.gwt.widget.table.event.RowDeletedHandler;
import org.openelis.gwt.widget.table.event.RowMovedEvent;
import org.openelis.gwt.widget.table.event.RowMovedHandler;
import org.openelis.gwt.widget.table.event.SortEvent;
import org.openelis.gwt.widget.table.event.SortHandler;
import org.openelis.gwt.widget.table.event.UnselectionEvent;
import org.openelis.gwt.widget.table.event.UnselectionHandler;
import org.openelis.gwt.widget.table.event.SortEvent.SortDirection;
import org.openelis.gwt.widget.tree.TreeView.VerticalScroll;
import org.openelis.gwt.widget.tree.event.BeforeLeafCloseEvent;
import org.openelis.gwt.widget.tree.event.BeforeLeafCloseHandler;
import org.openelis.gwt.widget.tree.event.BeforeLeafOpenEvent;
import org.openelis.gwt.widget.tree.event.BeforeLeafOpenHandler;
import org.openelis.gwt.widget.tree.event.HasBeforeLeafCloseHandlers;
import org.openelis.gwt.widget.tree.event.HasBeforeLeafOpenHandlers;
import org.openelis.gwt.widget.tree.event.HasLeafClosedHandlers;
import org.openelis.gwt.widget.tree.event.HasLeafOpenedHandlers;
import org.openelis.gwt.widget.tree.event.LeafClosedEvent;
import org.openelis.gwt.widget.tree.event.LeafClosedHandler;
import org.openelis.gwt.widget.tree.event.LeafOpenedEvent;
import org.openelis.gwt.widget.tree.event.LeafOpenedHandler;

import com.allen_sauer.gwt.dnd.client.DragController;
import com.allen_sauer.gwt.dnd.client.DragHandler;
import com.allen_sauer.gwt.dnd.client.drop.DropController;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.logical.shared.BeforeSelectionEvent;
import com.google.gwt.event.logical.shared.BeforeSelectionHandler;
import com.google.gwt.event.logical.shared.HasBeforeSelectionHandlers;
import com.google.gwt.event.logical.shared.HasSelectionHandlers;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.HTMLTable.Cell;

public class TreeWidget extends FocusPanel
        implements FocusHandler, BlurHandler, ClickHandler, HasField, MouseOverHandler, MouseOutHandler,
        HasBeforeSelectionHandlers<TreeDataItem>, HasSelectionHandlers<TreeDataItem>,
        HasUnselectionHandlers<TreeDataItem>, HasBeforeCellEditedHandlers, HasCellEditedHandlers,
        HasBeforeRowAddedHandlers, HasRowAddedHandlers, HasBeforeRowDeletedHandlers, HasRowDeletedHandlers,
        HasBeforeLeafOpenHandlers, HasBeforeLeafCloseHandlers, HasLeafOpenedHandlers, HasLeafClosedHandlers,
        HasDropController, HasBeforeSortHandlers, HasSortHandlers, HasBeforeRowMovedHandlers, HasRowMovedHandlers {

    protected HashMap<String, ArrayList<TreeColumn>> columns;
    protected boolean enabled;
    protected boolean focused;
    protected int selectedRow = -1;
    protected int selectedCol = -1;
    protected TreeView view;
    protected TreeRenderer renderer;
    protected TreeKeyboardHandler keyboardHandler;
    protected boolean shiftKey;
    protected boolean ctrlKey;
    protected int maxRows;
    protected int cellHeight = 21;
    protected int cellSpacing = 0;
    protected Widget activeWidget = null;
    protected int[] rowIndexList;
    protected boolean showRows;
    protected String title;
    protected boolean showHeader;
    protected TreeDragController dragController;
    protected TreeIndexDropController dropController;
    protected boolean selectedByClick;
    protected ArrayList<TreeDataItem> data;
    protected ArrayList<TreeDataItem> rows = new ArrayList<TreeDataItem>();
    protected int shownRows;
    protected boolean multiSelect;
    protected VerticalScroll showScroll;
    // public ArrayList<Integer> selectedRows = new ArrayList<Integer>();
    protected HashMap<String, TreeDataItem> leaves;
    protected String width;
    protected ArrayList<TreeDataItem> deleted;// = new ArrayList<DataSet<Key>>();
    protected ArrayList<Integer> selections = new ArrayList<Integer>(1);
    protected int selected = -1;
    protected ArrayList<TreeColumn> headers;
    protected boolean fireEvents = true;
    protected boolean queryMode;
    ArrayList<Exception> exceptions;

    public TreeWidget() {

    }

    public void init() {
        renderer = new TreeRenderer(this);
        keyboardHandler = new TreeKeyboardHandler(this);
        view = new TreeView(this, showScroll);
        view.setWidth(width);
        view.setHeight(maxRows * cellHeight);
        setWidget(view);
        addDomHandler(keyboardHandler, KeyUpEvent.getType());
        addDomHandler(keyboardHandler, KeyDownEvent.getType());
    }

    public void setTreeWidth(String width) {
        this.width = width;
    }

    public void setMaxRows(int maxRows) {
        this.maxRows = maxRows;
    }

    public int getMaxRows() {
        return maxRows;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    public void setShowScroll(VerticalScroll scroll) {
        this.showScroll = scroll;
    }

    public void showHeader(boolean showHeader) {
        this.showHeader = showHeader;
    }

    public boolean showHeader() {
        return showHeader;
    }

    public void multiSelect(boolean multiSelect) {
        this.multiSelect = multiSelect;
    }

    public boolean multiSelect() {
        return multiSelect;
    }

    public HashMap<String, ArrayList<TreeColumn>> getColumns() {
        return columns;
    }

    public void setHeaders(ArrayList<TreeColumn> headers) {
        this.headers = headers;
    }

    public ArrayList<TreeColumn> getHeaders() {
        return headers;
    }

    public void setColumns(HashMap<String, ArrayList<TreeColumn>> columns) {
        this.columns = columns;
    }

    /**
     * This method is called be TreeViewHeader for accurate resizing
     * @return
     */
    protected int getTreeWidth() {
        int tw = 0;
        if (width.equals("auto")) {
            for (TreeColumn column : headers)
                tw += column.getCurrentWidth();
        } else
            tw = Util.stripUnits(width, "px");

        return tw;
    }

    /**
     * This method handles all click events on the body of the table
     */
    public void onClick(ClickEvent event) {
        if (event.getNativeEvent().getCtrlKey())
            ctrlKey = true;
        else
            ctrlKey = false;
        if (event.getNativeEvent().getShiftKey())
            shiftKey = true;
        else
            shiftKey = false;
        if (event.getSource() == view.table) {
            Cell cell = ((FlexTable) event.getSource()).getCellForEvent(event);

            if (treeIndex(selectedRow) == cell.getRowIndex() && selectedCol == cell.getCellIndex())
                return;
            select(rowIndexList[cell.getRowIndex()], cell.getCellIndex(), true);
        }
        ctrlKey = false;
        shiftKey = false;
    }

    /**
     * Pass -1 to this method to unselect all rows in the tree.  This method will fire an UnselectionEvent
     * if a current item is selected and an UnselectionHandler is registered to the tree. UnselectEvent will be
     * passed a null candidate row when -1 is passed.
     */
    public void unselect(int row) {
        finishEditing();
        if (row == -1) {
            if (selections.size() > 0) {
                if (fireEvents && getHandlerCount(UnselectionEvent.getType()) > 0) {
                    UnselectionEvent event = UnselectionEvent.fire(this, rows.get(selections.get(0)), null);
                    if (event != null && event.isCanceled())
                        return;
                }
            }
            for (int i : selections) {
                rows.get(i).selected = false;
            }
            selections.clear();
            renderer.rowUnselected(-1);
        } else {
            selections.remove(new Integer(row));
        }
        if (selectedRow > -1)
            rows.get(selectedRow).selected = false;
        selectedRow = -1;
        if (isRowDrawn(row))
            renderer.rowUnselected(row);
    }

    /**
     * This method will determine if a specific TreeDataItem by its rowIndex[0..lastReachableItem] is currently visible 
     * in the tree view.
     * @param row
     * @return
     */
    protected boolean isRowDrawn(int row) {
        return row >= rowIndexList[0] && row <= rowIndexList[view.table.getRowCount() - 1];
    }

    /**
     * This method will determine the physical tree index[0..maxRow] of a TreeDataItem by its
     * rowIndex[0..lastReachableItem].  If the the row is currently not drawn on the screen this method will return a -1 
     * @param modelIndex
     * @return
     */
    protected int treeIndex(int rowIndex) {
        for (int i = 0; i < view.table.getRowCount(); i++) {
            if (rowIndexList[i] == rowIndex)
                return i;
        }
        return -1;
    }

    /**
     * This method will return the rowIndex[0..lastReachableItem] of the given TreeDataItem.  
     * Will return -1 if the TreeDataItem is not currently in the visible rows.
     * 
     * @param item
     * @return
     */
    public int getRowIndex(TreeDataItem item) {
        return rows.indexOf(item);
    }

    /**
     * Method used by lib classes to start editing in a cell.
     * @param row
     * @param col
     */
    protected void select(int row, int col) {
        select(row, col, false);
    }

    /**
     * This method will cause the table row passed to be selected. If the row is
     * already selected, the column clicked will be opened for editing if the
     * cell is editable and the user has the correct permissions.
     * 
     * @param row
     * @param col
     */
    protected void select(final int row, final int col, boolean byClick) {
        if (selectedRow != row) {
            if (!multiSelect || (multiSelect && !shiftKey && !ctrlKey)) {
                while (selections.size() > 0) {
                    int index = selections.get(0);
                    if (fireEvents) {
                        UnselectionEvent event = UnselectionEvent.fire(this, rows.get(index), rows.get(row));
                        if (event != null && event.isCanceled())
                            return;
                    }
                    unselect(index);
                }
            }

            if (getHandlerCount(BeforeSelectionEvent.getType()) > 0 && fireEvents) {
                BeforeSelectionEvent<TreeDataItem> event = BeforeSelectionEvent.fire(this, rows.get(row));
                if (event.isCanceled())
                    return;
            } else if (!isEnabled())
                return;
        }
        finishEditing();
        if (multiSelect && ctrlKey && isSelected(row)) {
            unselect(row);
            selectedCol = -1;
            sinkEvents(Event.ONKEYPRESS);
            return;
        }
        if (selectedRow != row) {
            selectRow(row);
            if (fireEvents)
                SelectionEvent.fire(this, rows.get(row));
        }
        if (isEnabled() && canEditCell(row, col)) {
            if (byClick && columns.get(rows.get(row).leafType).get(col).getColumnWidget() instanceof CheckBox
                    && !shiftKey && !ctrlKey) {
                clearCellExceptions(row, col);
                if (CheckBox.CHECKED.equals(getCell(row, col).getValue())) {
                    setCell(row, col, CheckBox.UNCHECKED);
                    if (fireEvents)
                        CellEditedEvent.fire(this, row, col, CheckBox.UNCHECKED);
                } else if (queryMode && CheckBox.UNCHECKED.equals(getCell(row, col).getValue())) {
                    setCell(row, col, CheckBox.UNKNOWN);
                    if (fireEvents)
                        CellEditedEvent.fire(this, row, col, CheckBox.UNKNOWN);
                } else {
                    setCell(row, col, CheckBox.CHECKED);
                    if (fireEvents)
                        CellEditedEvent.fire(this, row, col, CheckBox.CHECKED);
                }
                selectedCol = -1;
                sinkEvents(Event.ONKEYPRESS);
            } else {
                selectedCol = col;
                renderer.setCellEditor(row, col);
                unsinkEvents(Event.ONKEYPRESS);
            }
        } else {
            selectedCol = -1;
            sinkEvents(Event.ONKEYPRESS);
        }

    }

    /**
     *  This method will select all rows in the model, but only if the table is in 
     *  MultiSelect mode.
     */
    public void selectAll() {
        if (multiSelect) {
            selections = new ArrayList<Integer>();
            for (int i = 0; i < rows.size(); i++)
                selections.add(i);
            renderer.dataChanged(true);
        }
    }

    /** 
     * This method will select a TreeDataItem based on its rowIndex[0..lastReachableItem].  The row does not need
     * to be currently drawn in the TreeView. 
     * @param index
     */
    public void select(int rowIndex) {
        if (selectedRow != rowIndex) {
            while (selections.size() > 0) {
                int index = selections.get(0);
                if (fireEvents) {
                    UnselectionEvent event = UnselectionEvent.fire(this, rows.get(index), rows.get(rowIndex));
                    if (event != null && event.isCanceled())
                        return;
                }
                unselect(index);
            }

            if (getHandlerCount(BeforeSelectionEvent.getType()) > 0 && fireEvents) {
                BeforeSelectionEvent<TreeDataItem> event = BeforeSelectionEvent.fire(this, rows.get(rowIndex));
                if (event.isCanceled())
                    return;
            } else if (!isEnabled())
                return;
        }
        selectRow(rowIndex);
        if (fireEvents)
            SelectionEvent.fire(this, getSelection());
    }

    /**
     * This method will select the TreeDataItem passed.  If the passed item is not currently visible
     * becuase its parent nodes are closed, the method will will recursively open all parent nodes until the 
     * item is added to the visible list of rows.  if you need to make sure this item is also drawn on the
     * screen, call scrollToSelection() after this method.
     * @param item
     */
    public void select(TreeDataItem item) {
        if (item.parent != null && !item.parent.open) {
            TreeDataItem parent = item.parent;
            while (!parent.open) {
                parent.open = true;
                if (parent.parent != null)
                    parent = parent.parent;
            }
            refreshRow(parent);
        }
        select(rows.indexOf(item));
    }

    /**
     * This method will stop editing the active cell and flip it to it display state and fire any valueChangeEvents
     * if necessary.
     */
    public void finishEditing() {
        if (activeWidget != null) {
            if (renderer.stopEditing() && fireEvents)
                CellEditedEvent.fire(this, selectedRow, selectedCol,
                        getRow(selectedRow).cells.get(selectedCol).value);
            selectedCol = -1;
            sinkEvents(Event.KEYEVENTS);
        }
    }

    /**
     * This method can be called to flip a cell to its active editing state.  Pass the rowIndex[0..lastReachableItem], if the 
     * row is not currently displayed on the screen this method will do nothing.  
     * @param row
     * @param col
     */
    public void startEditing(int row, int col) {
        if (isRowDrawn(row))
            select(row, col, false);
    }

    /**
     * This method can be called to flip a cell to its active editing state.  if the item passed is currently not 
     * displayed on the screen this method will do nothing.
     * @param item
     * @param col
     */
    public void startEditing(TreeDataItem item, int col) {
        startEditing(getRowIndex(item), col);
    }

    /**
     * Enables the tree and columns to be edited.  Make sure column definitions are set before calling
     * this method.
     */
    public void enable(boolean enabled) {
        this.enabled = enabled;
        for (ArrayList<TreeColumn> leaf : columns.values()) {
            for (TreeColumn column : leaf) {
                column.enable(enabled);
            }
        }
    }

    public boolean isEnabled() {
        return enabled;
    }

    /**
     * This method is coded to determine if the user has clicked outside the tree and will call finishEditing.
     */
    public void onFocus(FocusEvent event) {
        if (((ScreenPanel) event.getSource()).focused == null
                || !DOM.isOrHasChild(getElement(), ((ScreenPanel) event.getSource()).focused.getElement())) {
            finishEditing();
        }
    }

    public void onBlur(BlurEvent event) {

    }

    /**
     * Scrolls the tree to the first selected item in the tree.  Selection will be at the top of the tree regardless 
     * of its current position in the tree. Will finishEditng an activeCell before scrolling.
     */
    public void scrollToSelection() {
        if (isRowDrawn(selectedRow))
            return;
        finishEditing();
        if (numRows() == shownRows()) {
            view.scrollBar.setScrollPosition(cellHeight * selectedRow);
        } else {
            int shownIndex = 0;
            for (int i = 0; i < selectedRow; i++) {
                if (getRow(i).shown)
                    shownIndex++;
            }
            view.scrollBar.setScrollPosition(cellHeight * shownIndex);
        }
    }

    /**
     * Scrolls the tree to the first selected item in the tree.  If the selection is off the top the selected Item will 
     * be at the top of the tree.  If the item is off the bottom the selection will the last item in the tree view. 
     * WillfinishEditng an activeCell before scrolling.
     */
    public void scrollToVisible() {
        if (isRowDrawn(selectedRow))
            return;
        finishEditing();
        if (numRows() == shownRows()) {
            if (selectedRow > rowIndexList[maxRows - 1])
                view.scrollBar.setScrollPosition((cellHeight * selectedRow) - (cellHeight * (maxRows - 1)));
            else
                view.scrollBar.setScrollPosition(cellHeight * selectedRow);
        } else {
            int shownIndex = 0;
            for (int i = 0; i < selectedRow; i++) {
                if (getRow(i).shown)
                    shownIndex++;
            }
            if (selectedRow > rowIndexList[maxRows - 1])
                view.scrollBar.setScrollPosition((cellHeight * shownIndex) - (cellHeight * (maxRows - 1)));
            else
                view.scrollBar.setScrollPosition(cellHeight * shownIndex);
        }
    }

    /**
     * Moves a TreeDataItem from its currentRowIndex[0..lastReachableItem] to the newRowIndex[0..lastReachableItem]
     * @param curIndex
     * @param newIndex
     */
    public void moveRow(int curIndex, int newIndex) {
        if (fireEvents) {
            if (getHandlerCount(BeforeRowMovedEvent.getType()) > 0) {
                BeforeRowMovedEvent<TreeDataItem> event = BeforeRowMovedEvent.fire(this, curIndex, newIndex,
                        rows.get(curIndex));
                if (event != null && event.isCancelled())
                    return;
            }
        }
        TreeDataItem item = rows.get(curIndex);
        if (item.parent != null) {
            item.parent.removeItem(item.childIndex);
        } else {
            data.remove(rows.get(curIndex).childIndex);
        }
        int index = rows.indexOf(item);
        while (index + 1 < rows.size() && item.isDecendant(rows.get(index + 1))) {
            TreeDataItem itemc = rows.get(index + 1);
            rows.remove(index + 1);
            if (itemc.shown)
                shownRows--;
        }
        if (item.shown)
            shownRows--;
        rows.remove(index);
        int insert = newIndex;
        if (newIndex > curIndex)
            insert--;
        if (insert >= rows.size())
            insert = rows.size();
        item.depth = 0;
        item.parent = null;
        int rowindex = rows.indexOf(data.get(insert));
        data.add(insert, item);
        ArrayList<TreeDataItem> added = new ArrayList<TreeDataItem>();
        checkChildItems(item, added);
        rows.addAll(rowindex, added);

        for (int i = insert; i < data.size(); i++)
            data.get(i).childIndex = i;
        if (fireEvents)
            RowMovedEvent.fire(this, curIndex, newIndex, item);
        renderer.dataChanged(true);
    }

    /**
     * Adds the passed item to end of the data model as a TopLevel tree node.
     * @param row
     */
    public void addRow(TreeDataItem item) {
        finishEditing();
        if (fireEvents) {
            BeforeRowAddedEvent event = BeforeRowAddedEvent.fire(this, data.size(), item);
            if (event != null && event.isCancelled())
                return;
        }
        data.add(item);
        checkChildItems(item, rows);
        renderer.dataChanged(true);
        item.childIndex = data.size() - 1;
        if (fireEvents)
            RowAddedEvent.fire(this, data.size() - 1, item);
    }

    /**
     * Inserts the passed item to the data model as a TopLevel tree node at the passed index
     * @param index
     * @param item
     */
    public void addRow(int index, TreeDataItem item) {
        finishEditing();
        if (fireEvents) {
            BeforeRowAddedEvent event = BeforeRowAddedEvent.fire(this, index, item);
            if (event != null && event.isCancelled())
                return;
        }
        int rowindex = rows.indexOf(data.get(index));
        data.add(index, item);
        ArrayList<TreeDataItem> added = new ArrayList<TreeDataItem>();
        checkChildItems(item, added);
        rows.addAll(rowindex, added);
        for (int i = index; i < data.size(); i++) {
            data.get(i).childIndex = i;
        }
        if (fireEvents)
            RowAddedEvent.fire(this, index, item);
        renderer.dataChanged(true);
    }

    /**
     * Adds the passed Child Item to the passed Parent item at the end of its children list and will refresh and render the tree 
     * for the cahnges.
     * @param parent
     * @param child
     */
    public void addChildItem(TreeDataItem parent, TreeDataItem child) {
        parent.addItem(child);
        if (parent.open)
            refreshRow(parent);
        if (fireEvents)
            RowAddedEvent.fire(this, -1, child);
    }

    /**
     * Inserts the passed Child Item to the passed Parent item at the index supplied and will refresh and render the tree 
     * for the cahnges.
     * @param parent
     * @param child
     */
    public void addChildItem(TreeDataItem parent, TreeDataItem child, int index) {
        parent.addItem(index, child);
        if (parent.open)
            refreshRow(parent);
        if (fireEvents)
            RowAddedEvent.fire(this, -1, child);
    }

    /**
     * Removes the passed Child Item from the passed Parent Item and will refersh and render the tree for the changes.
     * @param parent
     * @param child
     */
    public void removeChild(TreeDataItem parent, TreeDataItem child) {
        unselect(-1);
        parent.removeItem(parent.getItems().indexOf(child));
        if (parent.open)
            refreshRow(parent);
    }

    /**
     * Clears the tree and and its data model.
     */
    public void clear() {
        if (data != null)
            data.clear();
        if (rows != null)
            rows.clear();
        if (selections != null)
            selections.clear();
        selectedRow = -1;
        selectedCol = -1;
        activeWidget = null;
        shownRows = 0;
        rows = new ArrayList<TreeDataItem>();
        renderer.dataChanged(false);
    }

    /**
     * Clears all selections without firing UnselectEvents.
     */
    protected void clearSelections() {
        for (int i : selections) {
            rows.get(i).selected = false;
        }
        selections.clear();
        selectedRow = -1;
        selectedCol = -1;
    }

    /**
     * Deletes the passed item from the tree and will refresh and render the tree for the change.
     * @param item
     */
    public void deleteRow(TreeDataItem item) {
        deleteRow(rows.indexOf(item));
    }

    /**
     * Deletes the passed item from the tree by its rowIndex[0..lastReachableItem] and will refresh
     * and render the tree for the change
     * @param row
     */
    public void deleteRow(int row) {
        unselect(-1);
        if (fireEvents) {
            BeforeRowDeletedEvent event = BeforeRowDeletedEvent.fire(this, row, getRow(row));
            if (event != null && event.isCancelled())
                return;
        }
        TreeDataItem item = rows.get(row);
        if (item.parent != null) {
            item.parent.removeItem(item.childIndex);
        } else {
            data.remove(rows.get(row).childIndex);
        }
        int index = rows.indexOf(item);
        while (index + 1 < rows.size() && item.isDecendant(rows.get(index + 1))) {
            TreeDataItem itemc = rows.get(index + 1);
            rows.remove(index + 1);
            if (itemc.shown)
                shownRows--;
        }
        if (item.shown)
            shownRows--;
        rows.remove(index);
        for (int i = 0; i < data.size(); i++) {
            data.get(i).childIndex = i;
        }
        renderer.dataChanged(true);
        if (fireEvents)
            RowDeletedEvent.fire(this, row, item);
    }

    /**
     * Deletes the passed list of rowIndexes[0..lastReachableItem] from the tree. Will refresh and 
     * render the tree for the changes.
     * @param rowIndexes
     */
    public void deleteRows(List<Integer> rowIndexes) {
        unselect(-1);
        Collections.sort(rowIndexes);
        Collections.reverse(rowIndexes);
        for (int row : rowIndexes) {
            TreeDataItem item = rows.get(row);
            if (item.parent != null) {
                item.parent.removeItem(item.childIndex);
            } else {
                data.remove(rows.get(row).childIndex);
            }
            int index = rows.indexOf(item) + 1;
            while (item.isDecendant(rows.get(index))) {
                TreeDataItem itemc = rows.get(index + 1);
                rows.remove(index + 1);
                if (itemc.shown)
                    shownRows--;
            }
            if (item.shown)
                shownRows--;
            rows.remove(index);
        }
        renderer.dataChanged(true);
    }

    public void enableMultiSelect(boolean multi) {
        this.multiSelect = multi;
    }

    /**
     * Returns the model of the TopLevel tree nodes.
     * @return
     */
    public ArrayList<TreeDataItem> getData() {
        return data;
    }

    /**
     * Returns the value of of a given cell of TreeDataItem.  row is the rowIndex[0..lastReachableItem] of the item.
     * @param row
     * @param col
     * @return
     */
    public Object getObject(int row, int col) {
        Object obj = rows.get(row).cells.get(col).getValue();
        if (obj instanceof ArrayList) {
            if (((ArrayList) obj).size() == 1)
                return ((ArrayList) obj).get(0);
            else if (((ArrayList) obj).size() == 0)
                return null;
        }
        return obj;
    }

    /**
     * Returns the TreeDataItem at the passed rowIndex[0..lastReachableItem]
     * @param row
     * @return
     */
    public TreeDataItem getRow(int row) {
        return rows.get(row);
    }

    /**
     * Returns the currently selcted TreeDataItem
     * @return
     */
    public TreeDataItem getSelection() {
        if (selections == null || selections.size() == 0)
            return null;
        return rows.get(selections.get(0));
    }

    /**
     * Returns a list of currently selected TreeDataItems
     * @return
     */
    public ArrayList<TreeDataItem> getSelections() {
        ArrayList<TreeDataItem> selected = new ArrayList<TreeDataItem>();
        for (int i : selections) {
            selected.add(rows.get(i));
        }
        return selected;
    }

    /**
     * Returns the rowIndex[0..lastReachableItem] of the currently selected item
     * @return
     */
    public int getSelectedRow() {
        return selectedRow;
    }

    /**
     * Will hide the TreeDataItem at the passed rowIndex[0..lastReachableItem]
     * @param row
     */
    public void hideRow(int row) {
        rows.get(row).shown = false;
        shownRows--;
        renderer.dataChanged(true);
    }

    /**
     * Determines if the TreeDataItem is enabled for editing at the passed
     * rowIndex[0..lastReachableItem] 
     * @param index
     * @return
     */
    public boolean isEnabled(int index) {
        if (index < numRows())
            return rows.get(index).enabled;
        return false;
    }

    /**
     * Determines if the TreeDataItem is selected at the passed rowIndex[0..lastReachableItem]
     * @param index
     * @return
     */
    public boolean isSelected(int index) {
        return selections.contains(index);
    }

    /**
     * Loads a new model of TopLevel tree nodes into the tree completly replacing any existing model
     */
    public void load(ArrayList<TreeDataItem> data) {
        finishEditing();
        this.data = data;
        shownRows = 0;
        getReachableRows();
        selections.clear();
        selectedRow = -1;
        selectedCol = -1;
        renderer.dataChanged(false);
    }

    /**
     * returns current number of ReachableItems in the tree. 
     * @return
     */
    public int numRows() {
        return rows.size();
    }

    /**
     * private method to handle the selection with multiSelection and rendering of selected rows
     * @param index
     */
    private void selectRow(int index) {
        if (index > rows.size())
            throw new IndexOutOfBoundsException();
        selectedRow = index;
        if (multiSelect && shiftKey) {
            if (selections.size() == 0)
                selections.add(index);
            else {
                Integer max = Collections.max(selections);
                Integer min = Collections.min(selections);
                if (index < min) {
                    selections.clear();
                    for (int i = index; i <= min; i++) {
                        selections.add(i);
                    }
                } else if (index > max) {
                    selections.clear();
                    for (int i = max; i <= index; i++) {
                        selections.add(i);
                    }
                }
            }
        } else {
            if (!multiSelect || (multiSelect && !ctrlKey))
                selections.clear();
            selections.add(index);
            selectedRow = index;
        }
        if (isRowDrawn(index) && view.isVisible())
            renderer.dataChanged(true);
    }

    /**
     * MultiSelection method that will select all items in the passed parameters of rowIndexes[0..lastReachableItem]
     * Throws assertion error if tree is not in multiSelect mode;
     * @param selections
     */
    public void selectRows(Integer... selections) {
        assert multiSelect == true;
        selectRows(Arrays.asList(selections));
    }

    /**
      * MultiSelection method that will select all items in the passed parameters of items
      * Throws assertion error if tree is not in multiSelect mode;
     * @param selections
     */
    public void selectRows(TreeDataItem... selections) {
        assert multiSelect == true;
        List<Integer> indexes = new ArrayList<Integer>();
        for (int i = 0; i < selections.length; i++) {
            indexes.add(rows.indexOf(selections[i]));
        }
        selectRows(indexes);
    }

    /**
      * MultiSelection method that will select all items in the passed list of items
      * Throws assertion error if tree is not in multiSelect mode;
     * @param selections
     */
    public void selectRows(ArrayList<TreeDataItem> selections) {
        assert multiSelect == true;
        selectRows((TreeDataItem[]) selections.toArray());
    }

    /**
      * MultiSelection method that will select all items in the passed list of rowIndexes[0..lastReachableItem] 
      * Throws assertion error if tree is not in multiSelect mode;
     * @param selections
     */
    public void selectRows(List<Integer> selections) {
        assert multiSelect == true;
        ctrlKey = true;
        for (int i : selections)
            select(i);
        ctrlKey = false;
    }

    /*
     public void setModel(ArrayList<TreeDataItem> data) {
    this.data = data;  
     }
     */

    /**
     * Will make an item visible if currently hidden 
     */
    public void showRow(int row) {
        if (!rows.get(row).shown) {
            rows.get(row).shown = true;
            shownRows++;
            renderer.dataChanged(true);
        }
    }

    /**
     * Returns the number of shownRows which is a subset of reachable rows in the model
     * @return
     */
    public int shownRows() {
        return shownRows;
    }

    /**
     * This method will finishEditing the active cell and return the model of top level nodes
     * @return
     */
    public ArrayList<TreeDataItem> unload() {
        finishEditing();
        return data;
    }

    /*
     public void unselectRow(int index){
    if(index < 0) {
        clearSelections();
    }else if(index < numRows()){
        rows.get(index).selected = false;
        selections.remove(new Integer(index));
    }
    renderer.rowUnselected(-1);
     }
     */

    /**
     * Sets the value of a cell in the item at the passed rowIndex[0..lastReachableItem] and column.  
     */
    public void setCell(int row, int col, Object value) {
        rows.get(row).cells.get(col).setValue(value);
        if (isRowDrawn(row))
            renderer.cellUpdated(row, col);
    }

    /**
     * Returns the Cell at the passed rowIndex[0..lastReachableItem] and column
     * @param row
     * @param col
     * @return
     */
    public TableDataCell getCell(int row, int col) {
        return rows.get(row).cells.get(col);
    }

    /**
     * Replaces the item at the passesd rowIndex[0..lastReachableItem] with the passed item
     * @param index
     * @param newItem
     */
    public void setRow(int index, TreeDataItem newItem) {
        int rowindex = rows.indexOf(data.get(index));
        data.set(index, newItem);
        TreeDataItem item = rows.get(rowindex);
        while (item.isDecendant(rows.get(rowindex + 1))) {
            TreeDataItem itemc = rows.get(rowindex + 1);
            rows.remove(index + 1);
            if (itemc.shown)
                shownRows--;
        }
        if (item.shown)
            shownRows--;
        rows.remove(rowindex);
        ArrayList<TreeDataItem> added = new ArrayList<TreeDataItem>();
        checkChildItems(newItem, added);
        rows.addAll(rowindex, added);
        newItem.childIndex = index;
        renderer.dataChanged(true);
    }

    /**
     * Will toggle the passed TreeDataItem.  Will also select that item if not currently selected.
     * @param item
     */
    public void toggle(TreeDataItem item) {
        toggle(rows.indexOf(item));
    }

    /**
     * Will toggle the treeDataItem at the passed in rowIndex[0..lastReachableItem].  Will also select the item if not
     * currently selected.
     * @param row
     */
    public void toggle(int row) {
        finishEditing();
        if (selectedRow != row) {
            if (!multiSelect || (multiSelect && !shiftKey && !ctrlKey)) {
                while (selections.size() > 0) {
                    int index = selections.get(0);
                    if (fireEvents) {
                        UnselectionEvent event = UnselectionEvent.fire(this, rows.get(index), rows.get(row));
                        if (event != null && event.isCanceled())
                            return;
                    }
                    unselect(index);
                }
            }

            if (getHandlerCount(BeforeSelectionEvent.getType()) > 0 && fireEvents) {
                BeforeSelectionEvent<TreeDataItem> event = BeforeSelectionEvent.fire(this, rows.get(row));
                if (event.isCanceled())
                    return;
            } else if (!isEnabled())
                return;
            selections.add(row);
            rows.get(row).selected = true;
            renderer.rowSelected(row);
            selectedRow = row;
        }

        if (fireEvents)
            SelectionEvent.fire(this, rows.get(row));

        if (!rows.get(row).open) {
            if (fireEvents) {
                BeforeLeafOpenEvent event = BeforeLeafOpenEvent.fire(this, row, rows.get(row));
                if (event != null && event.isCancelled())
                    return;
            }
            rows.get(row).open = true;
            refreshRow(row);
            if (fireEvents)
                LeafOpenedEvent.fire(this, row, rows.get(row));
        } else {
            if (fireEvents) {
                BeforeLeafCloseEvent event = BeforeLeafCloseEvent.fire(this, row, rows.get(row));
                if (event != null && event.isCancelled())
                    return;
            }
            rows.get(row).close();
            refreshRow(row);
            if (fireEvents)
                LeafClosedEvent.fire(this, row, rows.get(row));
        }
    }

    /** 
     * Returns the rowIndex[0..lastReachableItem] of the first selcted row in the tree
     * @return
     */
    public int getSelectedRowIndex() {
        if (selections.size() == 0)
            return -1;
        return selections.get(0);
    }

    /**
     * Returns an int[] of selected rowIndexes[0..lastReachableItem] in the tree.
     * @return
     */
    public int[] getSelectedRowIndexes() {
        int[] ret = new int[selections.size()];
        for (int i = 0; i < selections.size(); i++)
            ret[i] = selections.get(i);
        return ret;
    }

    /**
     * Returns an ArrayLis<Integer> of the selected rowIndexes[0..lastReachableItem]
     * @return
     */
    public ArrayList<Integer> getSelectedRowList() {
        return selections;
    }

    /**
     * Clears exceptions for the cell at the passed rowIndex[0..lastReachableItem]  and column
     * @param row
     * @param col
     */
    public void clearCellExceptions(int row, int col) {
        rows.get(row).cells.get(col).clearExceptions();
        renderer.cellUpdated(row, col);
    }

    /**
     * Adds the passed exception to the cell at the passed rowIndex[0..lastReachableItem] and column
     * @param row
     * @param col
     * @param ex
     */
    public void setCellException(int row, int col, Exception ex) {
        rows.get(row).cells.get(col).addException(ex);
        if (isRowDrawn(row))
            renderer.cellUpdated(row, col);

    }

    /**
     * Clears exceptions for the cell at the passed rowIndex[0..lastReachableItem] and column key
     * @param row
     * @param col
     */
    public void clearCellExceptions(int row, String col) {
        int index = -1;
        for (TreeColumn column : columns.get(getRow(row).leafType)) {
            if (column.key.equals(col)) {
                index = columns.get(getRow(row).leafType).indexOf(column);
                rows.get(row).cells.get(index).clearExceptions();
                break;
            }
        }
        renderer.cellUpdated(row, index);
    }

    /**
     * Adds the passed exception to the cell at the passed rowIndex[0..lastReachableItem] and column key
     * @param row
     * @param col
     * @param ex
     */
    public void setCellException(int row, String col, Exception ex) {
        int index = -1;
        for (TreeColumn column : columns.get(getRow(row).leafType)) {
            if (column.key.equals(col)) {
                index = columns.get(getRow(row).leafType).indexOf(column);
                rows.get(row).cells.get(index).addException(ex);
                break;
            }
        }
        if (isRowDrawn(row))
            renderer.cellUpdated(row, index);

    }

    /**
     * Creates and returns a TreeDataItem of the passed leaf type
     * @param leafType
     * @return
     */
    public TreeDataItem createTreeItem(String leafType) {
        TreeDataItem td = new TreeDataItem(columns.get(leafType).size());
        td.leafType = leafType;
        return td;
    }

    /**
     * Sets the defined Hash of leaf types that defines the TreeColumns to the tree.
     * @param leaves
     */
    public void setLeaves(HashMap<String, TreeDataItem> leaves) {
        this.leaves = leaves;
    }

    /**
     * This method will open all top level nodes in the model and also recurse through all children to open them and make
     * every item in the tree reachable and be displayed in the tree.  It will also cause all current selections to be lost
     * and the tree scrolled to the top.
     */
    public void expand() {
        unselect(-1);
        rows = new ArrayList<TreeDataItem>();
        shownRows = 0;
        if (data == null)
            return;
        for (int i = 0; i < data.size(); i++) {
            TreeDataItem item = data.get(i);
            item.childIndex = i;
            openChildItems(item, rows);
        }
        refresh(false);
    }

    /**
     * This method will close all top level nodes in the model and also recurse through all children to close them. 
     * Only top level nodes will be reachable. It will also cause all current selections to be lost
     * and the tree scrolled to the top.
     */
    public void collapse() {
        unselect(-1);
        rows = new ArrayList<TreeDataItem>();
        shownRows = 0;
        if (data == null)
            return;
        for (int i = 0; i < data.size(); i++) {
            TreeDataItem item = data.get(i);
            item.childIndex = i;
            rows.add(item);
            shownRows++;
            closeChildItems(item, rows);
        }
        refresh(false);
    }

    /**
     * Method called for recursively opening child items
     * @param item
     * @param rows
     */
    private void openChildItems(TreeDataItem item, ArrayList<TreeDataItem> rows) {
        item.open = true;
        rows.add(item);
        if (item.shown)
            shownRows++;
        if (item.shownItems() > 0) {
            Iterator<TreeDataItem> it = item.getItems().iterator();
            while (it.hasNext())
                openChildItems(it.next(), rows);
        }
    }

    /**
     * Method called for recursively closing child items
     * @param item
     * @param rows
     */
    private void closeChildItems(TreeDataItem item, ArrayList<TreeDataItem> rows) {
        item.open = false;
        //if(item.shown)
        //   shownRows++;
        Iterator<TreeDataItem> it = item.getItems().iterator();
        while (it.hasNext())
            closeChildItems(it.next(), rows);
    }

    /*
    public void collapse(TreeDataItem item) {
       unselect(-1);
       collapse(rows.indexOf(item));
    }
        
    public void collapse(int index) {
       unselect(-1);
       closeChildItems(rows.get(index),rows);
      refreshRow(index);
    }
    */

    /**
     * This method will walk the tree creating the initial list of reachable rows.  This method is called once when
     * the data is initally loaded.
     */
    private void getReachableRows() {
        rows = new ArrayList<TreeDataItem>();
        if (data == null)
            return;
        for (int i = 0; i < data.size(); i++) {
            TreeDataItem item = data.get(i);
            item.childIndex = i;
            checkChildItems(item, rows);
        }
    }

    /**
     * Method recursively called for walking the tree to find reachable items 
     * @param item
     * @param rows
     */
    private void checkChildItems(TreeDataItem item, ArrayList<TreeDataItem> rows) {
        rows.add(item);
        if (item.shown)
            shownRows++;
        if (item.open && item.shownItems() > 0) {
            Iterator<TreeDataItem> it = item.getItems().iterator();
            while (it.hasNext())
                checkChildItems(it.next(), rows);
        }
    }

    /**
     * Refreshes a the row at the rowIndex[0..lastReachableItem] making any changes to the tree and 
     * rendering them in the display
     * @param index
     */
    private void refreshRow(int index) {
        TreeDataItem row = rows.get(index);
        while (index + 1 < rows.size() && row.isDecendant(rows.get(index + 1))) {
            TreeDataItem item = rows.get(index + 1);
            rows.remove(index + 1);
            if (item.shown)
                shownRows--;
        }
        rows.remove(index);
        if (row.shown)
            shownRows--;
        ArrayList<TreeDataItem> middle = new ArrayList<TreeDataItem>();
        checkChildItems(row, middle);
        rows.addAll(index, middle);
        renderer.dataChanged(true);
    }

    /**
     * Refreshes the passed item into the reachable rows making any changes to the tree and 
     * rendering them in the display
     * @param index
     */
    public void refreshRow(TreeDataItem item) {
        refreshRow(rows.indexOf(item));
    }

    /**
     * Method called to determine if a cell can be edited by its passed rowIndex[0..lastReachableItem] and column.  Fires BeforeCellEdited event if
     * registered.
     * @param row
     * @param col
     * @return
     */
    protected boolean canEditCell(int row, int col) {
        if (getHandlerCount(BeforeCellEditedEvent.getType()) > 0) {
            if (fireEvents) {
                BeforeCellEditedEvent bce = BeforeCellEditedEvent.fire(this, row, col,
                        getRow(row).cells.get(col).value);
                if (bce.isCancelled()) {
                    return false;
                }
            }
        } else {
            if (columns.get(rows.get(row).leafType).get(col).colWidget instanceof Label)
                return false;
        }
        return isEnabled();
    }

    /**
     * Stub method inherited from HasField interface
     */
    public void addException(Exception exception) {
        // TODO Auto-generated method stub
    }

    /**
     * This method is called on commit of a screen to determine if the Tree contains any 
     * errors  
     */
    public void checkValue() {
        finishEditing();
        exceptions = null;
        if (data == null)
            return;
        for (int i = 0; i < rows.size(); i++) {
            for (int j = 0; j < rows.get(i).cells.size(); j++) {
                Widget wid = columns.get(rows.get(i).leafType).get(j).getWidgetEditor(rows.get(i));
                if (wid instanceof HasField) {
                    ((HasField) wid).checkValue();
                    if (rows.get(i).cells.get(j).exceptions != null)
                        exceptions = rows.get(i).cells.get(j).exceptions;
                    if (((HasField) wid).getExceptions() != null) {
                        exceptions = ((HasField) wid).getExceptions();
                        ArrayList<Exception> exceps = new ArrayList<Exception>();
                        for (Exception exc : (ArrayList<Exception>) ((HasField) wid).getExceptions()) {
                            exceps.add(new Exception(exc.getMessage()));
                            if (rows.get(i).cells.get(j).exceptions != null) {
                                if (!rows.get(i).cells.get(j).getExceptions().contains(exc))
                                    rows.get(i).cells.get(j).exceptions.add(exc);
                            }
                        }
                        if (rows.get(i).cells.get(j).exceptions != null)
                            exceptions = rows.get(i).cells.get(j).exceptions;
                    }
                }
            }
        }
        if (exceptions != null)
            renderer.dataChanged(false);
    }

    /**
     * Stub method inherited from Hasfield Interface
     */
    public void clearExceptions() {
        // TODO Auto-generated method stub

    }

    /**
     * Returns any exceptions that may have been found in checkValue so the screen
     * knows the tree has errors and will cancel commit
     */
    public ArrayList<Exception> getExceptions() {
        // TODO Auto-generated method stub
        return exceptions;
    }

    /**
     * Stub method inherited from HasField interface
     */
    public Field getField() {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * Stub method inherited from HasField interface
     */
    public void getQuery(ArrayList list, String key) {
        /*
        ArrayList<TreeColumn> cols;
            
        if(queryMode){
           cols = columns.get(rows.get(0).leafType);
           for(TreeColumn col : cols) {
        if(rows != null && rows.size() > 0 && rows.get(0).cells.get(cols.indexOf(col)).value != null){
           if(col.colWidget instanceof Dropdown) {
              ((Dropdown)col.colWidget).setSelectionKeys((ArrayList<Object>)rows.get(0).cells.get(cols.indexOf(col)).value);
           }else if(!(col.colWidget instanceof CalendarLookUp)){
              ((HasField)col.getColumnWidget()).setFieldValue(rows.get(0).cells.get(cols.indexOf(col)).value);
           }
           ((HasField)col.getColumnWidget()).getQuery(list, col.key);
               
        }
           }
        }
        */
    }

    /**
     * Stub method inherited from HasField interface
     */
    public void setField(Field field) {
        // TODO Auto-generated method stub

    }

    /**
     * Stub method inherited from HasField interface
     */
    public Object getFieldValue() {
        return null;
    }

    /**
     * Stub method inherited from HasField interface
     */
    public void setQueryMode(boolean query) {
        if (query == queryMode)
            return;
        if (query)
            fireEvents = false;
        else
            fireEvents = true;
        queryMode = query;
        /*
        for(ArrayList<TreeColumn> cols : columns.values())
        for(TreeColumn col : cols) {
           ((HasField)col.getColumnWidget()).setQueryMode(query);
        }
        */
    }

    public void onMouseOver(MouseOverEvent event) {
        ((Widget) event.getSource()).addStyleName("TreeHighlighted");
    }

    public void onMouseOut(MouseOutEvent event) {
        ((Widget) event.getSource()).removeStyleName("TreeHighlighted");
    }

    public HandlerRegistration addCellEditedHandler(CellEditedHandler handler) {
        return addHandler(handler, CellEditedEvent.getType());
    }

    public HandlerRegistration addRowAddedHandler(RowAddedHandler handler) {
        return addHandler(handler, RowAddedEvent.getType());
    }

    public HandlerRegistration addRowDeletedHandler(RowDeletedHandler handler) {
        return addHandler(handler, RowDeletedEvent.getType());
    }

    public HandlerRegistration addBeforeSelectionHandler(BeforeSelectionHandler<TreeDataItem> handler) {
        return addHandler(handler, BeforeSelectionEvent.getType());
    }

    public HandlerRegistration addSelectionHandler(SelectionHandler<TreeDataItem> handler) {
        return addHandler(handler, SelectionEvent.getType());
    }

    public HandlerRegistration addBeforeCellEditedHandler(BeforeCellEditedHandler handler) {
        return addHandler(handler, BeforeCellEditedEvent.getType());
    }

    public HandlerRegistration addBeforeRowAddedHandler(BeforeRowAddedHandler handler) {
        return addHandler(handler, BeforeRowAddedEvent.getType());
    }

    public HandlerRegistration addBeforeRowDeletedHandler(BeforeRowDeletedHandler handler) {
        return addHandler(handler, BeforeRowDeletedEvent.getType());
    }

    public HandlerRegistration addBeforeLeafOpenHandler(BeforeLeafOpenHandler handler) {
        return addHandler(handler, BeforeLeafOpenEvent.getType());
    }

    public HandlerRegistration addBeforeLeafCloseHandler(BeforeLeafCloseHandler handler) {
        return addHandler(handler, BeforeLeafCloseEvent.getType());
    }

    public HandlerRegistration addLeafOpenedHandler(LeafOpenedHandler handler) {
        return addHandler(handler, LeafOpenedEvent.getType());
    }

    public HandlerRegistration addLeafClosedHandler(LeafClosedHandler handler) {
        return addHandler(handler, LeafClosedEvent.getType());
    }

    /**
     * Stub method inherited from HasField interface
     */
    public void setFieldValue(Object value) {
        // TODO Auto-generated method stub

    }

    /**
     * Sorts the tree model by the selected column in the headers list.
     * This will cause the tree to be walked agian to determine the reachable rows
     * @param col
     * @param direction
     */
    public void sort(int col, SortDirection direction) {
        if (fireEvents) {
            if (getHandlerCount(BeforeSortEvent.getType()) > 0) {
                BeforeSortEvent event = BeforeSortEvent.fire(this, col, headers.get(col).key, direction);
                if (event != null && event.isCancelled())
                    return;
            }
        }
        unselect(-1);
        if (fireEvents && getHandlerCount(SortEvent.getType()) > 0) {
            SortEvent.fire(this, col, headers.get(col).key, direction);
        } else {
            for (String leaf : headers.get(col).sortLeaves) {
                if (leaf.equals("model")) {
                    Collections.sort(data, new ColumnComparator(col, direction));
                } else {
                    Iterator<TreeDataItem> it = data.iterator();
                    while (it.hasNext())
                        sortChildItems(it.next(), leaf, col, direction);
                }

            }
        }
        getReachableRows();
        renderer.dataChanged(false);
    }

    /**
     * Method called recursively for sorting child items
     * @param item
     * @param leaf
     * @param col
     * @param dir
     */
    private void sortChildItems(TreeDataItem item, String leaf, int col, SortDirection dir) {
        if (item.leafType.equals(leaf)) {
            Collections.sort(item.getItems(), new ColumnComparator(col, dir));
        }
        Iterator<TreeDataItem> it = item.getItems().iterator();
        while (it.hasNext())
            sortChildItems(it.next(), leaf, col, dir);
    }

    /**
     * Enables or disables the tree items to be dragged
     * @param drag
     */
    public void enableDrag(boolean drag) {
        if (drag) {
            if (dragController == null) {
                dragController = new TreeDragController(RootPanel.get());
                dragController.addBeforeStartHandler(new BeforeDragStartHandler<TreeRow>() {
                    public void onBeforeDragStart(BeforeDragStartEvent<TreeRow> event) {
                        finishEditing();
                    }
                });
                for (TreeRow row : renderer.rows)
                    dragController.makeDraggable(row);
            }
            dragController.setEnable(true);
        } else {
            dragController.setEnable(false);
        }
    }

    /**
     * Enables or disables the Tree from accepting drop events
     */
    public void enableDrop(boolean drop) {
        if (drop) {
            dropController = new TreeIndexDropController(this);
        } else
            dropController = null;
    }

    /**
     * Adds a target where items from this tree can be dragged to.
     * @param drop
     */
    public void addTarget(HasDropController drop) {
        assert dragController != null;
        dragController.registerDropController(drop.getDropController());
    }

    /**
     * Removes a target that this tree can no longer be dragged to.
     * @param drop
     */
    public void removeTarget(HasDropController drop) {
        assert dragController != null;
        dragController.unregisterDropController(drop.getDropController());
    }

    public DropController getDropController() {
        return dropController;
    }

    public DragController getDragController() {
        return dragController;
    }

    public void addDragHandler(DragHandler handler) {
        dragController.addDragHandler(handler);
    }

    public void setDropController(DropController controller) {
        // TODO Auto-generated method stub
    }

    /**
     * Will redraw the displayed tree to pull in changes made to TreeDataItems directly.  
     * if true is passed in the the tree will keep its current scroll postion, false will 
     * cause the tree to be scrolled to the top
     * @param keepPosition
     */
    public void refresh(boolean keepPosition) {
        renderer.dataChanged(keepPosition);
    }

    /**
     * Redraws a cell based on its rowIndex[0..lastReachableItem] and column.  Used if a TreeDataItem 
     * cell was changed directly through code
     * @param row
     * @param col
     */
    public void refresh(int row, int col) {
        renderer.cellUpdated(row, col);
    }

    /**
     * Sets the flag usesd to determine if the tree should fire registered events or not
     * @param fire
     */
    public void fireEvents(boolean fire) {
        fireEvents = fire;
    }

    /**
     * Stub method inherited from HasField interface
     */
    public HandlerRegistration addFieldValueChangeHandler(ValueChangeHandler handler) {
        return null;
    }

    public HandlerRegistration addUnselectionHandler(UnselectionHandler<TreeDataItem> handler) {
        return addHandler(handler, UnselectionEvent.getType());
    }

    /**
     * Stub method inherited from HasField interface
     */
    public void addExceptionStyle(String style) {
        // TODO Auto-generated method stub

    }

    /**
     * Stub method inherited from HasField interface
     */
    public Object getWidgetValue() {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * Stub method inherited from HasField interface
     */
    public void removeExceptionStyle(String style) {
        // TODO Auto-generated method stub

    }

    public HandlerRegistration addBeforeSortHandler(BeforeSortHandler handler) {
        return addHandler(handler, BeforeSortEvent.getType());
    }

    public HandlerRegistration addSortHandler(SortHandler handler) {
        return addHandler(handler, SortEvent.getType());
    }

    public void addTabHandler(TabHandler handler) {
        addDomHandler(handler, KeyPressEvent.getType());
    }

    public HandlerRegistration addBeforeDragStartHandler(BeforeDragStartHandler<TreeRow> handler) {
        assert (dragController != null) : new Exception("Enable Dragging first before registering handlers");
        return dragController.addBeforeStartHandler(handler);
    }

    public HandlerRegistration addDagStartHandler(DragStartHandler<TreeRow> handler) {
        assert (dragController != null) : new Exception("Enable Dragging first before registerning handlers");
        return dragController.addStartHandler(handler);
    }

    public HandlerRegistration addBeforeDropHandler(BeforeDropHandler<TreeRow> handler) {
        assert (dropController != null) : new Exception("Enable Dropping first before registering handlers");
        return dropController.addBeforeDropHandler(handler);
    }

    public HandlerRegistration addDropHandler(DropHandler<TreeRow> handler) {
        assert (dropController != null) : new Exception("Enable Dropping first before registering handlers");
        return dropController.addDropHandler(handler);
    }

    public HandlerRegistration addDropEnterHandler(DropEnterHandler<TreeRow> handler) {
        assert (dropController != null) : new Exception("Enable Dropping first before registering handlers");
        return dropController.addDropEnterHandler(handler);
    }

    public HandlerRegistration addNavigationSelectionHandler(NavigationSelectionHandler handler) {
        return addHandler(handler, NavigationSelectionEvent.getType());
    }

    public HandlerRegistration addBeforeRowMovedHandler(BeforeRowMovedHandler handler) {
        // TODO Auto-generated method stub
        return null;
    }

    public HandlerRegistration addRowMovedHandler(RowMovedHandler handler) {
        // TODO Auto-generated method stub
        return null;
    }

    public boolean getCtrlKey() {
        return ctrlKey;
    }

    public boolean getShiftKey() {
        return shiftKey;
    }

}