Package com.totsp.gwittir.client.ui.table

Source Code of com.totsp.gwittir.client.ui.table.BoundTable

/*
* BoundTable.java
*
* Created on July 24, 2007, 5:30 PM
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
package com.totsp.gwittir.client.ui.table;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FocusListener;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTMLTable;
import com.google.gwt.user.client.ui.HasFocus;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.ScrollListener;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.SourcesClickEvents;
import com.google.gwt.user.client.ui.SourcesTableEvents;
import com.google.gwt.user.client.ui.TableListener;
import com.google.gwt.user.client.ui.Widget;

import com.totsp.gwittir.client.action.Action;
import com.totsp.gwittir.client.beans.Binding;
import com.totsp.gwittir.client.beans.Introspector;
import com.totsp.gwittir.client.beans.Property;
import com.totsp.gwittir.client.beans.SourcesPropertyChangeEvents;
import com.totsp.gwittir.client.keyboard.KeyBinding;
import com.totsp.gwittir.client.keyboard.KeyBindingException;
import com.totsp.gwittir.client.keyboard.KeyboardController;
import com.totsp.gwittir.client.keyboard.SuggestedKeyBinding;
import com.totsp.gwittir.client.keyboard.Task;
import com.totsp.gwittir.client.log.Level;
import com.totsp.gwittir.client.ui.BoundWidget;
import com.totsp.gwittir.client.ui.Button;
import com.totsp.gwittir.client.ui.Label;
import com.totsp.gwittir.client.ui.util.BoundWidgetTypeFactory;
import com.totsp.gwittir.client.util.ListSorter;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
* This is an option-rich table for use with objects implementing the Bindable interfaces.
* @author <a href="mailto:cooper@screaming-penguin.com">Robert "kebernet" Cooper</a>
* @see com.totsp.gwittir.client.beans.Bindable
*/
public class BoundTable extends AbstractTableWidget implements HasChunks {

    private static BoundTable activeTable = null;
    /**
     * A placholder for no mask options (0)
     */
    public static final int NONE_MASK = 0;
    /**
     * Renderer the table inside a scroll panel.
     *
     * <p>If the table has a DataProvider, it will use the "Google Reader"
     * get-next-chunk-on-max-scroll operation.</p>
     */
    public static final int SCROLL_MASK = 1;
    /**
     * Renderers a heading row on the table using the labels on the Column objects.
     */
    public static final int HEADER_MASK = 2;
    /**
     * Lets the user have multiple rows in the "selected" state at a time.
     */
    public static final int MULTIROWSELECT_MASK = 4;
    /**
     * Turns off row selection and styling.
     */
    public static final int NO_SELECT_ROW_MASK = 8;
    /**
     * Turns off selected column stying.
     */
    public static final int NO_SELECT_COL_MASK = 16;
    /**
     * Turns off cell selection stying.
     */
    public static final int NO_SELECT_CELL_MASK = 32;
    /**
     * Tells the table to render a spacing row in between bound rows.
     */
    public static final int SPACER_ROW_MASK = 64;
    /**
     * If this table has a DataProvider AND it is not scrolling, this supresses the
     * first, previous, next and last buttons at the bottom of the table.
     */
    public static final int NO_NAV_ROW_MASK = 128;
    /**
     * Enables sorting on the table when a header row is clicked.
     *
     * If this table has a DataProvider, it must be a SortableDataProvider for this to work.
     */
    public static final int SORT_MASK = 256;
    /**
     * Enables the click in widget insertion. Note: This will use the default widget type
     * for the model object from the BoundWidgetTypeFactory
     * @see com.totsp.gwittir.client.ui.util.BoundWidgetTypeFactory
     */
    public static final int INSERT_WIDGET_MASK = 512;
    /**
     * Determines whether buttons for row handles/selection should be present.
     */
    public static final int ROW_HANDLE_MASK = 1024;
    /**
     * Determines whether multi-selection requires shift-clicking
     */
    public static final int MULTI_REQUIRES_SHIFT = 2048;
    private static final String DEFAULT_STYLE = "default";
    private static final String NAV_STYLE = "nav";
    private Binding topBinding;
    private Button allRowsHandle;
    private Collection value;
    private DataProvider provider;
    private FlexTable table;
    private HashMap<SourcesClickEvents, ClickListener> clickListeners = new HashMap<SourcesClickEvents, ClickListener>();
    private HashMap<HasFocus, FocusListener> focusListeners = new HashMap<HasFocus, FocusListener>();
    private HashMap<Integer, String> selectedRowStyles;
    private List rowHandles; /*<Button>*/

    private Map bindingCache = new HashMap();
    private Map externalKeyBindings = new HashMap();
    private Map keyBindings = new HashMap();
    private Map widgetCache = new HashMap();
    private ScrollPanel scroll;
    private String selectedCellLastStyle;
    private String selectedColLastStyle = BoundTable.DEFAULT_STYLE;
    private String selectedRowLastStyle = BoundTable.DEFAULT_STYLE;
    private Timer cleanUpCaches = new Timer() {

        public void run() {
            if (value != null) {
                for (Iterator it = new ArrayList(widgetCache.keySet()).iterator();
                        it.hasNext();) {
                    Object o = it.next();

                    if (!value.contains(o)) {
                        widgetCache.remove(o);
                    }
                }

                for (Iterator it = new ArrayList(bindingCache.keySet()).iterator();
                        it.hasNext();) {
                    Object o = it.next();

                    if (!value.contains(o)) {
                        bindingCache.remove(o);
                    }
                }
            }
        }
    };
    private Widget base;
    private boolean[] ascending;
    private Field[] columns;
    private boolean inChunk = false;
    private boolean shiftDown = false;
    private int currentChunk = -1;
    private int lastScrollPosition;
    private int masks;
    private int numberOfChunks;
    private int selectedCellRowLastIndex = -1;
    private int selectedColLastIndex = -1;
    private int selectedRowLastIndex = -1;

    /** Creates a new instance of BoundTable */
    public BoundTable() {
        super();
        this.init(0);
    }

    /**
     * Creates a new instance of Bound table with the indicated options value.
     * @param masks int value containing the sum of the *_MASK options for the table.
     */
    public BoundTable(int masks) {
        super();
        this.init(masks);
    }

    /**
     * Creates a new instance of Bound table with the indicated options value.
     * @param typeFactory A BoundWidget type factory used to create the widgets that appear in the table.
     * @param masks int value containing the sum of the *_MASK options for the table.
     */
    public BoundTable(int masks, BoundWidgetTypeFactory typeFactory) {
        super();
        this.factory = typeFactory;
        this.init(masks);
    }

    /**
     * Creates a new instance of a table using a Collection as a data set.
     * @param masks int value containing the sum of the *_MASK options for the table.
     * @param cols The Column objects for the table.
     * @param value A collection containing Bindable objects to render in the table.
     */
    public BoundTable(int masks, Field[] cols, Collection value) {
        super();
        this.setColumns(cols);
        this.value = value;
        this.init(masks);
    }

    /**
     * Creates a new instance of a table using a Collection as a data set.
     * @param typeFactory A BoundWidget type factory used to create the widgets that appear in the table.
     * @param masks int value containing the sum of the *_MASK options for the table.
     * @param cols The Column objects for the table.
     * @param value A collection containing Bindable objects to render in the table.
     */
    public BoundTable(int masks, BoundWidgetTypeFactory typeFactory,
            Field[] cols, Collection value) {
        super();
        this.setColumns(cols);
        this.value = value;
        this.factory = typeFactory;
        this.init(masks);
    }

    /**
     * Creates a new instance of a table using a Collection as a data set.
     *
     * @param masks int value containing the sum of the *_MASK options for the table.
     * @param cols The Column objects for the table.
     */
    public BoundTable(int masks, Field[] cols) {
        super();
        this.setColumns(cols);
        this.init(masks);
    }

    /**
     * Creates a new instance of a table using a Collection as a data set.
     * @param typeFactory A BoundWidget type factory used to create the widgets that appear in the table.
     * @param masks int value containing the sum of the *_MASK options for the table.
     * @param cols The Column objects for the table.
     */
    public BoundTable(int masks, BoundWidgetTypeFactory typeFactory,
            Field[] cols) {
        super();
        this.setColumns(cols);
        this.factory = typeFactory;
        this.init(masks);
    }

    /**
     * Creates a new instance of BoundTable
     * @param masks int value containing the sum of the *_MASK options for the table.
     * @param cols The Column objects for the table.
     * @param provider Instance of DataProvider to get chunked data from.
     */
    public BoundTable(int masks, Field[] cols, DataProvider provider) {
        super();
        this.setColumns(cols);
        this.provider = provider;
        this.init(masks);
    }

    /**
     * Creates a new instance of BoundTable
     * @param typeFactory A BoundWidget type factory used to create the widgets that appear in the table.
     * @param masks int value containing the sum of the *_MASK options for the table.
     * @param cols The Column objects for the table.
     * @param provider Instance of DataProvider to get chunked data from.
     */
    public BoundTable(int masks, BoundWidgetTypeFactory typeFactory,
            Field[] cols, DataProvider provider) {
        super();
        this.setColumns(cols);
        this.provider = provider;
        this.factory = typeFactory;
        this.init(masks);
    }

    /**
     * Adds a new SourcesPropertyChangeEvents object to the table.
     * @param o An object of type SourcesPropertyChangeEvents.
     */
    public void add(SourcesPropertyChangeEvents o) {
        if (this.value.add(o)) {
            this.addRow(o);
        }
    }

    /**
     * Adds a colleciton of SourcesPropertyChangeEvents to the table
     * @param c A collection containing SourcesPropertyChangeEvents objects.
     */
    public void add(Collection c) {
        for (Iterator it = c.iterator(); it.hasNext();) {
            this.add((SourcesPropertyChangeEvents) it.next());
        }
    }

    private void addKeyBinding(KeyBinding binding, Object object) {
        this.externalKeyBindings.put(binding, object);
    }

    public void addKeyBinding(KeyBinding binding, BoundWidget widget)
            throws KeyBindingException {
        if (this.getActive()) {
            KeyboardController.INSTANCE.register(binding, widget);
        }

        this.addKeyBinding(binding, (Object) widget);
    }

    public void addKeyBinding(KeyBinding binding, Task task)
            throws KeyBindingException {
        if (this.getActive()) {
            KeyboardController.INSTANCE.register(binding, task);
        }

        this.addKeyBinding(binding, (Object) task);
    }

    public void addKeyBinding(KeyBinding binding, Action action)
            throws KeyBindingException {
        if (this.getActive()) {
            KeyboardController.INSTANCE.register(binding, action);
        }

        this.addKeyBinding(binding, (Object) action);
    }

    public void addKeyBinding(KeyBinding binding) throws KeyBindingException {
        if (this.getActive()) {
            KeyboardController.INSTANCE.register(binding, this);
        }

        this.addKeyBinding(binding, (Object) this);
    }

    private void addRow(final SourcesPropertyChangeEvents o) {
        int row = table.getRowCount();

        if (((((masks & BoundTable.HEADER_MASK) > 0) && (row >= 2))
                || (((masks & BoundTable.HEADER_MASK) == 0) && (row >= 1)))
                && ((masks & BoundTable.SPACER_ROW_MASK) > 0)) {
            table.setWidget(row, 0, new Label(""));
            table.getFlexCellFormatter().setColSpan(row, 0, this.columns.length);
            table.getRowFormatter().setStyleName(row, "spacer");
            row++;
        }

        Binding bindingRow = new Binding();
        topBinding.getChildren().add(bindingRow);

        int count = topBinding.getChildren().size();
        final Button handle;
        int startColumn = 0;

        if ((this.masks & BoundTable.ROW_HANDLE_MASK) > 0) {
            handle = new Button((this.getActive() && (rowHandles.size() < 9))
                    ? Integer.toString(this.rowHandles.size() + 1) : " ");
            handle.setStyleName("rowHandle");
            handle.addFocusListener(new FocusListener() {

                public void onFocus(Widget sender) {
                    if (shiftDown) {
                        return;
                    }

                    setActive(true);

                    List newSelected = null;

                    if ((masks & BoundTable.MULTIROWSELECT_MASK) > 0) {
                        newSelected = new ArrayList(getSelected());

                        if (newSelected.contains(o)) {
                            newSelected.remove(o);
                        } else {
                            newSelected.add(o);
                        }
                    } else {
                        newSelected = new ArrayList();
                        newSelected.add(o);
                    }

                    setSelected(newSelected);
                }

                public void onLostFocus(Widget sender) {
                }
            });
            handle.addClickListener(new ClickListener() {

                public void onClick(Widget sender) {
                    setActive(true);

                    List newSelected = null;

                    if ((masks & BoundTable.MULTIROWSELECT_MASK) > 0) {
                        newSelected = new ArrayList(getSelected());

                        if (newSelected.contains(o)) {
                            newSelected.remove(o);
                        } else {
                            newSelected.add(o);
                        }
                    } else {
                        newSelected = new ArrayList();
                        newSelected.add(o);
                    }

                    if (handle != null) {
                        handle.setFocus(true);
                    }

                    if (handle != null) {
                        handle.setFocus(true);
                    }

                    setSelected(newSelected);
                }
            });
            startColumn++;
            this.rowHandles.add(handle);
            this.table.setWidget(row, 0, handle);
        } else {
            handle = null;
        }

        if (count < 10) {
            SuggestedKeyBinding kb = new SuggestedKeyBinding(Integer.toString(
                    count).charAt(0), false, true, false);
            Task task = new Task() {

                public void run() {
                    List newSelected = new ArrayList(getSelected());

                    if (newSelected.contains(o)) {
                        newSelected.remove(o);
                    } else {
                        newSelected.add(o);
                    }

                    setSelected(newSelected);

                    if (handle != null) {
                        handle.setFocus(true);
                    }
                }
            };

            this.keyBindings.put(kb, task);

            if (this.getActive()) {
                try {
                    KeyboardController.INSTANCE.register(kb, task);
                } catch (KeyBindingException kbe) {
                    BoundTable.LOG.log(Level.DEBUG, "Unable to register" + kb,
                            kbe);
                }
            }
        }

        for (int col = 0; col < this.columns.length; col++) {
            Widget widget = (Widget) createCellWidget(bindingRow, col, o);

            try {
                table.setWidget(row, col + startColumn, widget);

                if (widget instanceof HasFocus) {
                    addSelectedFocusListener((HasFocus) widget,
                            topBinding.getChildren().size() - 1, col + startColumn);
                }

                if (widget instanceof SourcesClickEvents) {
                    addSelectedClickListener((SourcesClickEvents) widget,
                            topBinding.getChildren().size() - 1, col + startColumn);
                }

                if (this.columns[col].getStyleName() != null) {
                    table.getCellFormatter().setStyleName(row, col + startColumn,
                            this.columns[col].getStyleName());
                }
            } catch (RuntimeException e) {
                BoundTable.LOG.log(Level.ERROR, widget + "", e);
            }
        }

        boolean odd = (this.calculateRowToObjectOffset(new Integer(row)).intValue() % 2) != 0;
        this.table.getRowFormatter().setStyleName(row, odd ? "odd" : "even");

        if (this.isAttached()) {
            bindingRow.setLeft();
            bindingRow.bind();
        }
    }

    private void addSelectedClickListener(final SourcesClickEvents widget,
            final int objectNumber, final int col) {
        ClickListener l = new ClickListener() {

            public void onClick(Widget sender) {
                setActive(true);

                int row = calculateObjectToRowOffset(objectNumber);
                handleSelect(true, row, col);
            }
        };

        widget.addClickListener(l);
        clickListeners.put(widget, l);
    }

    private void addSelectedFocusListener(final HasFocus widget,
            final int objectNumber, final int col) {
        FocusListener l = new FocusListener() {

            public void onLostFocus(Widget sender) {
            }

            public void onFocus(Widget sender) {
                setActive(true);

                int row = calculateObjectToRowOffset(objectNumber);

                //GWT.log("Focus row: " + row + " object: " + objectNumber
                //        + " col: " + col, null);
                //GWT.log("SelectedRowLastIndex " + selectedRowLastIndex, null);
                handleSelect(row != selectedRowLastIndex, row, col);
            }
        };

        widget.addFocusListener(l);
        focusListeners.put(widget, l);
    }

    @Override
    public void addStyleName(String style) {
        this.base.addStyleName(style);
    }

    public void addTableListener(TableListener listener) {
        this.table.addTableListener(listener);
    }

    protected int calculateObjectToRowOffset(int row) {
        if ((masks & BoundTable.SPACER_ROW_MASK) > 0) {
            row += row;
        }

        if ((masks & BoundTable.HEADER_MASK) > 0) {
            row++;
        }

        //GWT.log( "Row before: "+ row, null);
        if ((masks & BoundTable.INSERT_WIDGET_MASK) > 0) {
            //if( (masks & BoundTable.MULTIROWSELECT_MASK) > 0){
            //   row += this.selectedRowsBeforeObjectRow(row);
            //} else {
            row += this.selectedRowsBeforeRow(row);

            //}
        }

        //GWT.log( "Row after "+ row, null);
        return row;
    }

    protected Integer calculateRowToObjectOffset(Integer rowNumber) {
        int row = rowNumber.intValue();

        if ((masks & BoundTable.HEADER_MASK) > 0) {
            row -= 1;
        }

        if ((masks & BoundTable.SPACER_ROW_MASK) > 0) {
            row -= (row / 2);
        }

        //GWT.log( "Selected rows before row "+row+" "+ this.selectedRowsBeforeRow( row ), null );
        if (((masks & BoundTable.INSERT_WIDGET_MASK) > 0)
                && ((masks & BoundTable.MULTIROWSELECT_MASK) > 0)) {
            //GWT.log( "At"+ row+ " Removing: "+this.selectedRowsBeforeRow(row), null );
            row -= this.selectedRowsBeforeRow(row);
        }

        //GWT.log( "Returning object instance index: "+row, null);
        return new Integer(row);
    }

    /**
     * Clears the table and cleans up all bindings and listeners.
     */
    public void clear() {
        this.topBinding.unbind();
        this.topBinding.getChildren().clear();

        if (this.rowHandles != null) {
            this.rowHandles.clear();
        }

        if (this.keyBindings != null) {
            this.keyBindings.clear();
        }

        for (Iterator it = this.focusListeners.entrySet().iterator();
                it.hasNext();) {
            Entry entry = (Entry) it.next();
            ((HasFocus) entry.getKey()).removeFocusListener((FocusListener) entry.getValue());
        }

        for (Iterator it = this.clickListeners.entrySet().iterator();
                it.hasNext();) {
            Entry entry = (Entry) it.next();
            ((SourcesClickEvents) entry.getKey()).removeClickListener((ClickListener) entry.getValue());
        }

        this.table.clear();

        while (table.getRowCount() > 0) {
            this.table.removeRow(0);
        }

        if (this.selectedRowStyles != null) {
            this.selectedRowStyles.clear();
        }

        this.clearSelectedCol();
        this.selectedCellLastStyle = BoundTable.DEFAULT_STYLE;
        this.selectedColLastIndex = -1;
        this.selectedColLastStyle = BoundTable.DEFAULT_STYLE;
        this.selectedRowLastIndex = -1;
        this.selectedRowLastStyle = BoundTable.DEFAULT_STYLE;
        this.selectedCellRowLastIndex = -1;
        this.cleanUpCaches.schedule(50);
    }

    private void clearSelectedCell() {
        if ((this.selectedColLastIndex != -1)
                && (this.selectedCellRowLastIndex != -1)) {
            this.getCellFormatter().setStyleName(this.selectedCellRowLastIndex,
                    this.selectedColLastIndex, this.selectedCellLastStyle);
        }

        this.selectedColLastIndex = -1;
        this.selectedCellRowLastIndex = -1;
    }

    private void clearSelectedCol() {
        if (this.selectedColLastIndex != -1) {
            this.getColumnFormatter().setStyleName(this.selectedColLastIndex,
                    this.selectedColLastStyle);
        }
    }

    private void clearSelectedRows() {
        this.clearSelectedCol();
        this.clearSelectedCell();

        List old = this.getSelected();

        if ((this.masks & BoundTable.INSERT_WIDGET_MASK) > 0) {
            List removeRows = new ArrayList();

            if (this.selectedRowStyles != null) {
                removeRows.addAll(this.selectedRowStyles.keySet());
            } else {
                removeRows.add(new Integer(this.selectedRowLastIndex));
            }

            for (int i = removeRows.size() - 1; i >= 0; i--) {
                //GWT.log("Removing nested: " + removeRows.get(i), null);
                this.removeNestedWidget(((Integer) removeRows.get(i)).intValue());
            }
        }

        if ((this.masks & BoundTable.MULTIROWSELECT_MASK) > 0) {
            for (Iterator it = this.selectedRowStyles.entrySet().iterator();
                    it.hasNext();) {
                Entry entry = (Entry) it.next();
                int row = ((Integer) entry.getKey()).intValue();
                this.table.getRowFormatter().setStyleName(row, (String) entry.getValue());
            }

            this.selectedRowStyles.clear();
        } else if ((this.masks & BoundTable.NO_SELECT_ROW_MASK) == 0) {
            if (this.selectedRowLastIndex != -1) {
                this.getRowFormatter().setStyleName(this.selectedRowLastIndex,
                        this.selectedRowLastStyle);
                this.selectedRowLastStyle = BoundTable.DEFAULT_STYLE;
            }
        }

        this.selectedRowLastIndex = -1;
        this.selectedCellRowLastIndex = -1;

        this.changes.firePropertyChange("selected", old, this.getSelected());
    }

    private BoundWidget createCellWidget(Binding rowBinding, int colIndex,
            SourcesPropertyChangeEvents target) {
        try {
            final BoundWidget widget;
            Field col = this.columns[colIndex];
            BoundWidget[] rowWidgets = (BoundWidget[]) widgetCache.get(target);

            if (rowWidgets == null) {
                rowWidgets = new BoundWidget[this.columns.length];
                widgetCache.put(target, rowWidgets);
            }

            if (rowWidgets[colIndex] != null) {
                widget = rowWidgets[colIndex];
                BoundTable.LOG.log(Level.SPAM,
                        "Using cache widget for " + target + "."
                        + col.getPropertyName(), null);
            } else {
                if (col.getCellProvider() != null) {
                    widget = col.getCellProvider().get();
                } else {
                    final Property p = Introspector.INSTANCE.getDescriptor(target).getProperty(col.getPropertyName());
                    widget = this.factory.getWidgetProvider(col.getPropertyName(),
                            p.getType()).get();

                    // TODO Figure out some way to make this read only.
                }
                assert widget != null : target + "." + col.getPropertyName() + " did not get a widget.";
                rowWidgets[colIndex] = widget;
                BoundTable.LOG.log(Level.SPAM,
                        "Creating widget for " + target + "." + col.getPropertyName(),
                        null);
            }

            Binding[] bindings = (Binding[]) this.bindingCache.get(target);

            if (bindings == null) {
                bindings = new Binding[this.columns.length];
                this.bindingCache.put(target, bindings);
            }

            if (bindings[colIndex] == null) {
                try {
                    bindings[colIndex] = new Binding(widget, "value",
                            col.getValidator(), col.getFeedback(), target,
                            col.getPropertyName(), null, null);
                    BoundTable.LOG.log(Level.SPAM,
                            "Created binding " + bindings[colIndex], null);
                } catch (Throwable e) {
                    throw new RuntimeException("Error creating " + col.getPropertyName(), e);
                }
            }

            widget.setModel(target);
            rowBinding.getChildren().add(bindings[colIndex]);

            return widget;
        } catch (Exception e) {
            GWT.log(null, e);
            return null;
        }
    }

    private Widget createNavWidget() {
        Grid p = new Grid(1, 5);
        p.setStyleName(BoundTable.NAV_STYLE);

        Button b = new Button("<<",
                new ClickListener() {

                    public void onClick(Widget sender) {
                        first();
                    }
                });
        b.setStyleName(BoundTable.NAV_STYLE);

        if (this.getCurrentChunk() == 0) {
            b.setEnabled(false);
        }

        p.setWidget(0, 0, b);
        b = new Button("<",
                new ClickListener() {

                    public void onClick(Widget sender) {
                        previous();
                    }
                });
        b.setStyleName(BoundTable.NAV_STYLE);

        if (this.getCurrentChunk() == 0) {
            b.setEnabled(false);
        }

        p.setWidget(0, 1, b);

        b = new Button(">",
                new ClickListener() {

                    public void onClick(Widget sender) {
                        next();
                    }
                });
        b.setStyleName(BoundTable.NAV_STYLE);

        if (this.getCurrentChunk() == (this.getNumberOfChunks() - 1)) {
            b.setEnabled(false);
        }

        Label l = new Label((this.getCurrentChunk() + 1) + " / "
                + this.getNumberOfChunks());
        p.setWidget(0, 2, l);
        p.getCellFormatter().setHorizontalAlignment(0, 2, HasHorizontalAlignment.ALIGN_CENTER);

        p.setWidget(0, 3, b);
        b = new Button(">>",
                new ClickListener() {

                    public void onClick(Widget sender) {
                        last();
                    }
                });
        b.setStyleName(BoundTable.NAV_STYLE);

        if (this.getCurrentChunk() == (this.getNumberOfChunks() - 1)) {
            b.setEnabled(false);
        }

        p.setWidget(0, 4, b);

        return p;
    }

    protected FlexTable createTableImpl() {
        return new FlexTable();
    }

    /**
     * Causes the table to go to the first chunk of data, if a data provider is used.
     */
    public void first() {
        this.currentChunk = 0;
        this.provider.getChunk(this, this.getCurrentChunk());
        this.inChunk = true;
    }

    public boolean getActive() {
        return BoundTable.activeTable == this;
    }

    /**
     * Returns the Binding object used by this table.
     * @return The Binding object for this table.
     */
    public Binding getBinding() {
        return this.topBinding;
    }

    public int getCellCount(int row) {
        return this.table.getCellCount(row);
    }

    public HTMLTable.CellFormatter getCellFormatter() {
        return this.table.getCellFormatter();
    }

    public int getCellPadding() {
        return this.table.getCellPadding();
    }

    public int getCellSpacing() {
        return this.table.getCellSpacing();
    }

    public HTMLTable.ColumnFormatter getColumnFormatter() {
        return this.table.getColumnFormatter();
    }

    /**
     * Returns the Columns used in this table.
     * @return Column[] used for rendering this table.
     */
    public Field[] getColumns() {
        Field[] ret = new Field[this.columns.length];

        for (int i = 0; i < ret.length; i++) {
            ret[i] = this.columns[i];
        }

        return columns;
    }

    /**
     * Returns the current fetched chunk from the data provider.
     * @return int index of the current chunk.
     */
    public int getCurrentChunk() {
        return currentChunk;
    }

    public FlexTable.FlexCellFormatter getFlexCellFormatter() {
        return this.table.getFlexCellFormatter();
    }

    public String getHTML(int row, int column) {
        return this.table.getHTML(row, column);
    }

    /**
     * Returns the number of available chunks (passed in from the DataProvider)
     * @return int number of chunks available from the DataProvider
     */
    public int getNumberOfChunks() {
        return numberOfChunks;
    }

    public int getRowCount() {
        return this.table.getRowCount();
    }

    public HTMLTable.RowFormatter getRowFormatter() {
        return this.table.getRowFormatter();
    }

    /**
     * Returns a List containing the current selected row objects.
     * @return List of Bindables from the selected rows.
     */
    public List getSelected() {
        ArrayList selected = new ArrayList();
        HashSet realIndexes = new HashSet();

        if (this.selectedRowStyles != null) {
            for (Iterator it = this.selectedRowStyles.keySet().iterator();
                    it.hasNext();) {
                realIndexes.add(calculateRowToObjectOffset((Integer) it.next()));
            }
        } else if (this.selectedRowLastIndex != -1) {
            realIndexes.add(calculateRowToObjectOffset(
                    new Integer(this.selectedRowLastIndex)));
        }

        int i = 0;

        for (Iterator it = this.topBinding.getChildren().iterator();
                it.hasNext(); i++) {
            if (realIndexes.contains(new Integer(i))) {
                selected.add(((Binding) ((Binding) it.next()).getChildren().get(0)).getRight().object);
            } else {
                it.next();
            }
        }

        return selected;
    }

    @Override
    public String getStyleName() {
        return this.base.getStyleName();
    }

    @Override
    public String getTitle() {
        return this.table.getTitle();
    }

    public Object getValue() {
        return value;
    }

    public BoundWidget getWidget(int row, int col) {
        return (BoundWidget) this.table.getWidget(row, col);
    }

    private void handleSelect(boolean toggleRow, int row, int col) {
        int calcRow = row;

        //GWT.log( "Toggle row "+ toggleRow, null );
        //GWT.log( " ON "+row+", "+col, new RuntimeException() );
        if ((this.masks & BoundTable.INSERT_WIDGET_MASK) > 0) {
            if (((this.selectedRowStyles == null)
                    && (this.selectedRowLastIndex != -1)
                    && (this.selectedRowLastIndex == (row - 1)))
                    || ((this.selectedRowStyles != null)
                    && this.selectedRowStyles.containsKey(new Integer(row - 1)))) {
                return;
            }

            calcRow = row - this.selectedRowsBeforeRow(row);
        }

        if (calcRow < 0) {
            throw new RuntimeException("Row base is negative!");
        }

        if ((((masks & BoundTable.SPACER_ROW_MASK) == 0)
                && ((((masks & BoundTable.HEADER_MASK) > 0) && (calcRow > 0))
                || ((masks & BoundTable.HEADER_MASK) == 0)))
                || (((masks & BoundTable.HEADER_MASK) > 0) & ((calcRow % 2) != 0))
                || (((masks & BoundTable.HEADER_MASK) == 0)
                && ((calcRow % 2) != 1))) {
            if (!this.columns[col].isClickable()) {
                System.out.println("Not clickable");
                return;
            }
            if ((toggleRow
                    && (((masks & BoundTable.MULTIROWSELECT_MASK) == 0)
                    && (row != this.selectedCellRowLastIndex)))
                    || (((masks & BoundTable.MULTIROWSELECT_MASK) > 0)
                    && toggleRow)) {
                //if( toggleRow || (masks & BoundTable.MULTIROWSELECT_MASK) == 0){
                row = setSelectedRow(row);
            }

            setSelectedCell(row, col);

            setSelectedCol(col);

            this.selectedCellRowLastIndex = row;
        }
    }

    /**
     * Method called by the DataProvider to initialize the first chunk and pass
     * in the to total number of chunks available.
     * @param c Data for Chunk index 0
     * @param numberOfChunks The total number of available chunks of data.
     */
    public void init(Collection c, int numberOfChunks) {
        this.numberOfChunks = numberOfChunks;
        this.currentChunk = 0;
        this.setValue(c);
        this.inChunk = false;
    }

    private void init(int masksValue) {
        //GWT.log( "Init "+ +masksValue + " :: "+((masksValue & BoundTable.MULTI_REQUIRES_SHIFT) > 0), null);
        final BoundTable instance = this;
        this.topBinding = new Binding();
        this.masks = masksValue;
        this.factory = (this.factory == null)
                ? new BoundWidgetTypeFactory(true) : this.factory;

        if (((this.masks & BoundTable.SORT_MASK) > 0)
                && (this.columns != null)) {
            this.ascending = new boolean[this.columns.length];
        }

        if ((this.masks & BoundTable.MULTIROWSELECT_MASK) > 0) {
            this.selectedRowStyles = new HashMap();
        }

        if (((this.masks & BoundTable.ROW_HANDLE_MASK) > 0)
                && ((this.masks & BoundTable.MULTIROWSELECT_MASK) > 0)) {
            this.allRowsHandle = new Button("  ",
                    new ClickListener() {

                        public void onClick(Widget sender) {
                            if ((getSelected() != null)
                                    && (getSelected().size() == 0)) {
                                setSelected(new ArrayList(
                                        (Collection) getValue()));
                            } else {
                                setSelected(new ArrayList());
                            }
                        }
                    });
            this.allRowsHandle.setStyleName("rowHandle");
            this.allRowsHandle.setHeight("100%");
            this.allRowsHandle.setWidth("100%");

            if ((this.masks & BoundTable.MULTIROWSELECT_MASK) == 0) {
                this.allRowsHandle.setEnabled(false);
            }
        }

        if ((this.masks & BoundTable.ROW_HANDLE_MASK) > 0) {
            this.rowHandles = new ArrayList();
        }

        this.table = createTableImpl();
        this.table.setCellPadding(0);
        this.table.setCellSpacing(0);

        EventingSimplePanel esp = new EventingSimplePanel();
        esp.setWidget(table);

        if ((masks & BoundTable.SCROLL_MASK) > 0) {
            this.scroll = new ScrollPanel();
            this.scroll.setWidget(table);
            super.initWidget(esp);

            scroll.addScrollListener(new ScrollListener() {

                public void onScroll(Widget widget, int scrollLeft,
                        int scrollTop) {
                    //GWT.log("HasProvider: " + (provider != null), null);
                    if ((provider != null) && (inChunk == false)
                            && (scrollTop >= (table.getOffsetHeight()
                            - scroll.getOffsetHeight()))) {
                        //GWT.log("Scroll Event fired. ", null);
                        lastScrollPosition = scrollTop - 1;
                        next();
                    }
                }
            });
        } else {
            super.initWidget(esp);
        }

        this.table.setCellSpacing(0);
        table.addTableListener(new TableListener() {

            public void onCellClicked(SourcesTableEvents sender, int row,
                    int cell) {
                setActive(true);

                int startColumn = ((masks & BoundTable.ROW_HANDLE_MASK) > 0)
                        ? 1 : 0;

                if (startColumn == 0) {
                    handleSelect(true, row, cell);
                }

                if (((masks & BoundTable.SORT_MASK) > 0)
                        && ((masks & BoundTable.HEADER_MASK) > 0)
                        && (row == 0)) {
                    sortColumn(cell - startColumn);
                }
            }
        });
        this.base = ((this.scroll == null) ? (Widget) this.table
                : (Widget) this.scroll);
        this.value = (this.value == null) ? new ArrayList() : this.value;
        this.columns = (this.columns == null) ? new Field[0] : this.columns;
        this.setStyleName("gwittir-BoundTable");

        if ((this.provider != null) && (this.getCurrentChunk() == -1)) {
            this.provider.init(this);
            this.inChunk = true;
        }

        this.addPropertyChangeListener("selected",
                new PropertyChangeListener() {

                    public void propertyChange(
                            PropertyChangeEvent propertyChangeEvent) {
                        if (getAction() != null) {
                            getAction().execute(instance);
                        }
                    }
                });
        this.addPropertyChangeListener("active",
                new PropertyChangeListener() {

                    public void propertyChange(
                            PropertyChangeEvent propertyChangeEvent) {
                        boolean newActive = ((Boolean) propertyChangeEvent.getNewValue()).booleanValue();

                        if ((masks & BoundTable.ROW_HANDLE_MASK) > 0) {
                            for (int i = 0; i < rowHandles.size(); i++) {
                                ((Button) rowHandles.get(i)).setText((newActive
                                        && (i <= 8)) ? Integer.toString(i + 1) : " ");
                            }
                        }

                        for (Iterator it = keyBindings.entrySet().iterator();
                                it.hasNext();) {
                            Entry entry = (Entry) it.next();
                            handleBinding(newActive, entry);
                        }

                        for (Iterator it = externalKeyBindings.entrySet().iterator();
                                it.hasNext();) {
                            Entry entry = (Entry) it.next();
                            handleBinding(newActive, entry);
                        }
                    }

                    private void handleBinding(boolean newActive, Entry entry) {
                        KeyBinding kb = (KeyBinding) entry.getKey();
                        Object execute = entry.getValue();

                        if (newActive) {
                            BoundTable.LOG.log(Level.SPAM, "Registering " + kb, null);

                            try {
                                if (execute instanceof Task) {
                                    KeyboardController.INSTANCE.register(kb,
                                            (Task) execute);
                                } else if (execute instanceof Action) {
                                    KeyboardController.INSTANCE.register(kb,
                                            (Action) execute);
                                } else if (execute instanceof BoundWidget) {
                                    KeyboardController.INSTANCE.register(kb,
                                            (BoundWidget) execute);
                                }
                            } catch (KeyBindingException kbe) {
                                BoundTable.LOG.log(Level.DEBUG,
                                        "Unable to register" + kb, kbe);
                            }
                        } else {
                            boolean result = KeyboardController.INSTANCE.unregister(kb);
                            BoundTable.LOG.log(Level.SPAM,
                                    "Unregistering " + kb + " " + result, null);
                        }
                    }
                });
    }

    private void insertNestedWidget(int row) {
        //GWT.log( "Inserting nested for row "+row, null);
        Integer realIndex = this.calculateRowToObjectOffset(new Integer(row));

        //GWT.log( "RealIndex: "+ realIndex, null );
        int i = 0;
        SourcesPropertyChangeEvents o = null;

        for (Iterator it = this.topBinding.getChildren().iterator();
                it.hasNext(); i++) {
            if (realIndex.intValue() == i) {
                o = ((Binding) ((Binding) it.next()).getChildren().get(0)).getRight().object;

                break;
            } else {
                it.next();
            }
        }

        BoundWidget widget = this.factory.getWidgetProvider(Introspector.INSTANCE.resolveClass(
                o)).get();
        widget.setModel(o);
        this.table.insertRow(row + 1);
        this.table.setWidget(row + 1, 0, (Widget) widget);
        this.table.getFlexCellFormatter().setColSpan(row + 1, 0, this.columns.length + 1);
        this.table.getCellFormatter().setStyleName(row + 1, 0, "expanded");
        this.modifySelectedIndexes(row, +1);
    }

    /**
     * Causes the table to render the last chunk of data.
     */
    public void last() {
        if ((this.numberOfChunks - 1) >= 0) {
            this.currentChunk = this.numberOfChunks - 1;
            this.provider.getChunk(this, currentChunk);
            this.inChunk = true;
        }
    }

    private void modifySelectedIndexes(int fromRow, int modifier) {
        if (this.selectedRowLastIndex > fromRow) {
            this.selectedRowLastIndex += modifier;
        }

        if (this.selectedCellRowLastIndex > fromRow) {
            this.selectedCellRowLastIndex += modifier;
        }

        if (this.selectedRowStyles == null) {
            return;
        }

        HashMap newSelectedRowStyles = new HashMap();

        for (Iterator it = this.selectedRowStyles.entrySet().iterator();
                it.hasNext();) {
            Entry entry = (Entry) it.next();
            Integer entryRow = (Integer) entry.getKey();

            if (entryRow.intValue() > fromRow) {
                newSelectedRowStyles.put(new Integer(entryRow.intValue()
                        + modifier), entry.getValue());
            } else {
                newSelectedRowStyles.put(entryRow, entry.getValue());
            }
        }

        this.selectedRowStyles = newSelectedRowStyles;
    }

    /**
     * Causes the table to render the next chunk of data.
     */
    public void next() {
        //GWT.log("Next invoked. NOC: " + this.numberOfChunks, null);
        if ((this.currentChunk + 1) < this.numberOfChunks) {
            this.provider.getChunk(this, ++currentChunk);
            this.inChunk = true;
        }
    }

    @Override
    protected void onAttach() {
        super.onAttach();
        this.renderAll();
    }

    @Override
    protected void onDetach() {
        super.onDetach();
        this.clear();
        this.setActive(false);
    }

    /**
     * Causes teh table to render the previous chunk of data.
     */
    public void previous() {
        if ((this.getCurrentChunk() - 1) >= 0) {
            this.provider.getChunk(this, --currentChunk);
            inChunk = true;
        }
    }

    private void removeNestedWidget(int row) {
        this.modifySelectedIndexes(row, -1);
        this.table.removeRow(row + 1);
    }

    private void renderAll() {
        this.clear();

        int startColumn = 0;

        if ((this.masks & BoundTable.ROW_HANDLE_MASK) > 0) {
            this.table.setWidget(0, 0, this.allRowsHandle);
            startColumn = 1;
        }

        if ((this.masks & BoundTable.HEADER_MASK) > 0) {
            for (int i = 0; i < this.columns.length; i++) {
                this.table.setWidget(0, i + startColumn,
                        new Label(this.columns[i].getLabel()));
            }

            this.table.getRowFormatter().setStyleName(0, "header");
        }
        GWT.log("Table size: " + (this.value == null ? "null" : Integer.toString(this.value.size())));
        for (Iterator it = (this.value == null) ? null : this.value.iterator();
                (it != null) && it.hasNext();) {
            this.addRow((SourcesPropertyChangeEvents) it.next());
        }

        if ((this.provider != null)
                && ((this.masks & BoundTable.SCROLL_MASK) == 0)
                && ((this.masks & BoundTable.NO_NAV_ROW_MASK) == 0)) {
            int row = this.table.getRowCount();
            this.table.setWidget(row, 0, this.createNavWidget());
            this.table.getFlexCellFormatter().setColSpan(row, 0, this.columns.length);
            table.getCellFormatter().setHorizontalAlignment(row, 0,
                    HasHorizontalAlignment.ALIGN_CENTER);
        }

        //GWT.log(this.toString(), null);
    }

    private int selectedRowsBeforeRow(int row) {
        //GWT.log( "=======Selected rows before "+row, null);
        //GWT.log( "=======lastRow "+this.selectedRowLastIndex, null );
        if (this.selectedRowStyles == null) {
            return ((this.selectedRowLastIndex == -1)
                    || (this.selectedRowLastIndex >= row)) ? 0 : 1;
        }

        int count = 0;

        for (Iterator it = this.selectedRowStyles.keySet().iterator();
                it.hasNext();) {
            if (((Integer) it.next()).intValue() < row) {
                count++;
            }
        }

        return count;
    }

    public void setActive(boolean active) {
        if ((BoundTable.activeTable == this) & !active) {
            BoundTable.activeTable = null;
            this.changes.firePropertyChange("active", true, false);
        } else if (BoundTable.activeTable != this) {
            if (BoundTable.activeTable != null) {
                BoundTable.activeTable.setActive(false);
            }

            BoundTable.activeTable = this;
            this.changes.firePropertyChange("active", false, true);
        }
    }

    public void setBorderWidth(int width) {
        this.table.setBorderWidth(width);
    }

    public void setCellPadding(int padding) {
        this.table.setCellPadding(padding);
    }

    public void setCellSpacing(int spacing) {
        this.table.setCellSpacing(spacing);
    }

    /**
     * Called by the DataProvider to pass in a requested chunk of data.
     * THIS METHOD MUST BE CALLED ASYNCRONOUSLY.
     * @param c The next requested chunk of Bindable objects.
     */
    public void setChunk(Collection c) {
        if (!this.inChunk) {
            throw new RuntimeException(
                    "This method MUST becalled asyncronously!");
        }

        if ((masks & BoundTable.SCROLL_MASK) > 0) {
            this.add(c);
        } else {
            this.setValue(c);
        }

        if (((masks & BoundTable.SCROLL_MASK) > 0)
                && (this.scroll.getScrollPosition() >= this.lastScrollPosition)) {
            this.scroll.setScrollPosition(this.lastScrollPosition);
        }

        this.inChunk = false;
    }

    /**
     * Sets Column[] object for use on the table.
     * Note, this will foce a re-init of the table.
     * @param columns Column[] to use to render the table.
     */
    public void setColumns(Field[] columns) {
        this.columns = new Field[columns.length];

        for (int i = 0; i < columns.length; i++) {
            this.columns[i] = columns[i];
        }

        if ((this.masks & BoundTable.SORT_MASK) > 0) {
            this.ascending = new boolean[this.columns.length];
        }

        if (this.topBinding != null) { // Used to check that init() has fired.
            this.renderAll();
        }
    }

    public void setDataProvider(DataProvider provider) {
        this.provider = provider;
        this.provider.init(this);
        this.inChunk = true;
    }

    @Override
    public void setHeight(String height) {
        this.base.setHeight(height);
    }

    @Override
    public void setPixelSize(int width, int height) {
        this.table.setPixelSize(width, height);
    }

    /**
     * Sets the indicated items in the list to "selected" state.
     * @param selected A List of Bindables to set as the Selected value.
     */
    public void setSelected(List selected) {
        int i = 0;
        this.clearSelectedRows();

        for (Iterator it = this.topBinding.getChildren().iterator();
                it.hasNext(); i++) {
            SourcesPropertyChangeEvents b = ((Binding) ((Binding) it.next()).getChildren().get(0)).getRight().object;

            if (selected.contains(b)) {
                this.setSelectedRow(calculateObjectToRowOffset(i));

                if (this.table.getWidget(calculateObjectToRowOffset(i), 0) instanceof HasFocus) {
                    ((HasFocus) this.table.getWidget(calculateObjectToRowOffset(
                            i), 0)).setFocus(true);
                }
            }
        }
    }

    private void setSelectedCell(int row, int col) {
        if ((((this.masks & BoundTable.HEADER_MASK) > 0) && (row == 0))
                || ((this.masks & BoundTable.NO_SELECT_CELL_MASK) > 0)) {
            return;
        }

        if ((this.selectedColLastIndex != -1)
                && (this.selectedCellRowLastIndex != -1)) {
            this.getCellFormatter().setStyleName(this.selectedCellRowLastIndex,
                    this.selectedColLastIndex, this.selectedCellLastStyle);
        }

        this.selectedCellLastStyle = table.getCellFormatter().getStyleName(row, col);

        if ((this.selectedCellLastStyle == null)
                || (this.selectedCellLastStyle.length() == 0)) {
            this.selectedCellLastStyle = BoundTable.DEFAULT_STYLE;
        }

        table.getCellFormatter().setStyleName(row, col, "selected");
    }

    private void setSelectedCol(int col) {
        clearSelectedCol();
        this.selectedColLastIndex = col;

        if ((this.masks & BoundTable.NO_SELECT_COL_MASK) == 0) {
            this.selectedColLastStyle = table.getColumnFormatter().getStyleName(col);

            if ((this.selectedColLastStyle == null)
                    || (this.selectedColLastStyle.length() == 0)) {
                this.selectedColLastStyle = BoundTable.DEFAULT_STYLE;
            }

            table.getColumnFormatter().setStyleName(col, "selected");
        }
    }

    private int setSelectedRow(int row) {
        if (((this.masks & BoundTable.HEADER_MASK) > 0) && (row == 0)) {
            return row;
        }

        List old = this.getSelected();

        //GWT.log( "Selected row:"+row, null);
        //GWT.log( "Needs Shift:" + ((this.masks & BoundTable.MULTI_REQUIRES_SHIFT)  > 0), null);
        //GWT.log( "Has Shift:" + shiftDown, null );
        //GWT.log(( "Shift OK: "+((masks & BoundTable.MULTI_REQUIRES_SHIFT)  > 0 == shiftDown ) ) , null );
        if ((this.masks & BoundTable.MULTIROWSELECT_MASK) > 0) {
            if ((((masks & BoundTable.MULTI_REQUIRES_SHIFT) > 0) == shiftDown)) {
                //TOGGLE ROW.
                if (this.selectedRowStyles.containsKey(new Integer(row))) {
                    //Handle Widget remove on Multirow
                    this.getRowFormatter().setStyleName(row,
                            (String) this.selectedRowStyles.remove(new Integer(row)));

                    if ((this.masks & BoundTable.INSERT_WIDGET_MASK) > 0) {
                        this.removeNestedWidget(row);
                    }
                } else {
                    String lastStyle = table.getRowFormatter().getStyleName(row);
                    lastStyle = ((lastStyle == null)
                            || (lastStyle.length() == 0)) ? BoundTable.DEFAULT_STYLE
                            : lastStyle;
                    this.selectedRowStyles.put(new Integer(row), lastStyle);
                    this.getRowFormatter().setStyleName(row, "selected");

                    if ((this.masks & BoundTable.INSERT_WIDGET_MASK) > 0) {
                        this.insertNestedWidget(row);
                    }
                }
            } else {
                // Set selected and toggle all others
                GWT.log("clearing all rows", null);

                for (Integer i : (Integer[]) this.selectedRowStyles.keySet().toArray(new Integer[this.selectedRowStyles.keySet().size()])) {
                    if (i == row) {
                        continue;
                    }

                    GWT.log("Clearing " + i, null);
                    //Handle Widget remove on Multirow
                    this.getRowFormatter().setStyleName(i, this.selectedRowStyles.remove(i));

                    if ((this.masks & BoundTable.INSERT_WIDGET_MASK) > 0) {
                        this.removeNestedWidget(row);
                    }
                }

                if (!this.selectedRowStyles.containsKey(row)) {
                    String lastStyle = table.getRowFormatter().getStyleName(row);
                    lastStyle = ((lastStyle == null)
                            || (lastStyle.length() == 0)) ? BoundTable.DEFAULT_STYLE
                            : lastStyle;
                    this.selectedRowStyles.put(row, lastStyle);
                    this.getRowFormatter().setStyleName(row, "selected");

                    if ((this.masks & BoundTable.INSERT_WIDGET_MASK) > 0) {
                        this.insertNestedWidget(row);
                    }
                }
            }
        } else {
            if ((this.masks & BoundTable.NO_SELECT_ROW_MASK) == 0) {

                if (this.selectedRowLastIndex != -1) {
                    this.getRowFormatter().setStyleName(this.selectedRowLastIndex,
                            this.selectedRowLastStyle);

                    if ((this.masks & BoundTable.INSERT_WIDGET_MASK) > 0) {
                        this.removeNestedWidget(this.selectedRowLastIndex);

                        if (this.selectedRowLastIndex < row) {
                            row--;
                        }
                    }
                }

                String currentStyle = table.getRowFormatter().getStyleName(row);

                if ((currentStyle == null) || !currentStyle.equals("selected")) {
                    this.selectedRowLastStyle = currentStyle;
                }

                if ((this.selectedRowLastStyle == null)
                        || (this.selectedRowLastStyle.length() == 0)) {
                    this.selectedRowLastStyle = BoundTable.DEFAULT_STYLE;
                }

                table.getRowFormatter().setStyleName(row, "selected");

                if ((this.masks & BoundTable.INSERT_WIDGET_MASK) > 0) {
                    this.insertNestedWidget(row);
                }
            }
        }

        this.selectedRowLastIndex = (this.selectedRowLastIndex == row) ? (-1)
                : row;
        this.changes.firePropertyChange("selected", old, this.getSelected());

        return row;
    }

    @Override
    public void setSize(String width, String height) {
        this.base.setSize(width, height);
    }

    @Override
    public void setStyleName(String style) {
        this.base.setStyleName(style);
    }

    public void setValue(Object value) {
        try {
            if (value == this.value) {
                return;
            }
            Collection old = this.value;
            this.value = (Collection) value;
            this.changes.firePropertyChange("value", old, this.value);

            if (this.isAttached()) {
                boolean active = this.getActive();
                this.setActive(false);
                this.renderAll();
                this.setActive(active);
            }
        } catch (Exception e) {
            GWT.log(null, e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void setWidth(String width) {
        this.base.setWidth(width);
    }

    /**
     * Sorts the table based on the value of the property in the specified column
     * index.
     *
     * If using a SortableDataProvider, this will throw a runtime exception if the
     * column denoted by the index is not a supported sortable column.
     * @param index index of the column to sort the table on.
     */
    public void sortColumn(int index) {
        ascending[index] = !ascending[index];

        if (this.provider == null) {
            ArrayList sort = new ArrayList();
            sort.addAll(value);

            try {
                ListSorter.sortOnProperty(sort,
                        columns[index].getPropertyName(), ascending[index]);
            } catch (Exception e) {
                LOG.log(Level.INFO, "Exception during sort", e);
            }

            value.clear();

            for (Iterator it = sort.iterator(); it.hasNext();) {
                value.add(it.next());
            }

            setValue(value);
        } else if (this.provider instanceof SortableDataProvider) {
            SortableDataProvider sdp = (SortableDataProvider) this.provider;
            boolean canSort = false;
            String[] sortableProperties = sdp.getSortableProperties();

            for (int i = 0; (i < sortableProperties.length) && !canSort; i++) {
                if (sortableProperties[i].equals(
                        this.columns[index].getPropertyName())) {
                    canSort = true;
                }
            }

            if (!canSort) {
                throw new RuntimeException(
                        "That is not a sortable field from this data provider.");
            }

            sdp.sortOnProperty(this, this.columns[index].getPropertyName(),
                    this.ascending[index]);
        }

        int startColumn = ((masks & BoundTable.ROW_HANDLE_MASK) > 0) ? 1 : 0;

        if ((this.masks & BoundTable.HEADER_MASK) > 0) {
            table.getCellFormatter().setStyleName(0, index + startColumn,
                    this.ascending[index] ? "ascending" : "descending");
        }
    }

    private class EventingSimplePanel extends SimplePanel {

        EventingSimplePanel() {
            super();
            sinkEvents(Event.MOUSEEVENTS);
            sinkEvents(Event.FOCUSEVENTS);
        }

        @Override
        public void onBrowserEvent(Event evt) {
            if (DOM.eventGetShiftKey(evt)) {
                shiftDown = true;
            } else {
                shiftDown = false;
            }

            super.onBrowserEvent(evt);
        }
    }
}
TOP

Related Classes of com.totsp.gwittir.client.ui.table.BoundTable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.