DataTable.java revision 8b0ea2285c1327a686ff0b6ab245915e7fd20094
1e3f6868dac3b4c4714637d12b93d97823011a35cshowardpackage autotest.common.table;
2e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
3e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
48b0ea2285c1327a686ff0b6ab245915e7fd20094showardimport autotest.common.Utils;
5ef37eccb66c415fdb6a8fe63244fe8545463719fshowardimport autotest.common.ui.RightClickTable;
6ef37eccb66c415fdb6a8fe63244fe8545463719fshoward
74cd4763a855d8eb7d25fd4963babc432eb4d25e6showardimport com.google.gwt.event.dom.client.ClickEvent;
84cd4763a855d8eb7d25fd4963babc432eb4d25e6showardimport com.google.gwt.event.dom.client.ClickHandler;
94cd4763a855d8eb7d25fd4963babc432eb4d25e6showardimport com.google.gwt.event.dom.client.ContextMenuEvent;
104cd4763a855d8eb7d25fd4963babc432eb4d25e6showardimport com.google.gwt.event.dom.client.ContextMenuHandler;
114cd4763a855d8eb7d25fd4963babc432eb4d25e6showardimport com.google.gwt.event.dom.client.DomEvent;
12e8819cdf80ca0e0602d22551a50f970aa68e108dmblighimport com.google.gwt.json.client.JSONObject;
13e8819cdf80ca0e0602d22551a50f970aa68e108dmblighimport com.google.gwt.json.client.JSONValue;
14e8819cdf80ca0e0602d22551a50f970aa68e108dmblighimport com.google.gwt.user.client.ui.Composite;
154cd4763a855d8eb7d25fd4963babc432eb4d25e6showardimport com.google.gwt.user.client.ui.HTMLTable;
1694b698cacab819b42104ce0db68aa63b68f4d1d1showardimport com.google.gwt.user.client.ui.Widget;
171c8c2215e525de8813c375e796354f8ffb811a08showard
181c8c2215e525de8813c375e796354f8ffb811a08showardimport java.util.ArrayList;
198579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshowardimport java.util.Collections;
201c8c2215e525de8813c375e796354f8ffb811a08showardimport java.util.List;
21e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
22e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh/**
23e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh * A table to display data from JSONObjects.  Each row displays data from one
24e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh * JSONObject.  A header row with column titles is automatically generated, and
25e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh * support is included for adding other arbitrary header rows.
26e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh * <br><br>
27e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh * Styles:
28e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh * <ul>
29e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh * <li>.data-table - the entire table
30e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh * <li>.data-row-header - the column title row
31e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh * <li>.data-row-one/.data-row-two - data row styles.  These two are alternated.
32e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh * </ul>
33e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh */
344cd4763a855d8eb7d25fd4963babc432eb4d25e6showardpublic class DataTable extends Composite implements ClickHandler, ContextMenuHandler {
35e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    public static final String HEADER_STYLE = "data-row-header";
36e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    public static final String CLICKABLE_STYLE = "data-row-clickable";
371c8c2215e525de8813c375e796354f8ffb811a08showard    public static final String HIGHLIGHTED_STYLE = "data-row-highlighted";
3894b698cacab819b42104ce0db68aa63b68f4d1d1showard    public static final String WIDGET_COLUMN = "_WIDGET_COLUMN_";
3935dbd8414c0e7022a6a4b54f7ef16b5ff51ae53bshoward    // use CLICKABLE_WIDGET_COLUMN for widget that expect to receive clicks.  The table will ignore
4035dbd8414c0e7022a6a4b54f7ef16b5ff51ae53bshoward    // click events coming from these columns.
4135dbd8414c0e7022a6a4b54f7ef16b5ff51ae53bshoward    public static final String CLICKABLE_WIDGET_COLUMN = "_CLICKABLE_WIDGET_COLUMN_";
421c8c2215e525de8813c375e796354f8ffb811a08showard    // for indexing into column subarrays (i.e. columns[1][COL_NAME])
431c8c2215e525de8813c375e796354f8ffb811a08showard    public static final int COL_NAME = 0, COL_TITLE = 1;
44e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
458579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    public static interface DataTableListener {
464cd4763a855d8eb7d25fd4963babc432eb4d25e6showard        public void onRowClicked(int rowIndex, JSONObject row, boolean isRightClick);
478579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    }
488579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward
49ef37eccb66c415fdb6a8fe63244fe8545463719fshoward    protected RightClickTable table;
50e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
51e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    protected String[][] columns;
52e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    protected int headerRow = 0;
53e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    protected boolean clickable = false;
541c8c2215e525de8813c375e796354f8ffb811a08showard
5594b698cacab819b42104ce0db68aa63b68f4d1d1showard    protected TableWidgetFactory widgetFactory = null;
568579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    private List<DataTableListener> listeners = new ArrayList<DataTableListener>();
5794b698cacab819b42104ce0db68aa63b68f4d1d1showard
581c8c2215e525de8813c375e796354f8ffb811a08showard    // keep a list of JSONObjects corresponding to rows in the table
596bc47015cce0ebc2fc255d3950bfeaf4851f36fdshoward    protected List<JSONObject> jsonObjects = new ArrayList<JSONObject>();
60e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
6194b698cacab819b42104ce0db68aa63b68f4d1d1showard
6294b698cacab819b42104ce0db68aa63b68f4d1d1showard    public static interface TableWidgetFactory {
639d821ab7d97c677a63589e6d71ee3c9da46f7077showard        public Widget createWidget(int row, int cell, JSONObject rowObject);
6494b698cacab819b42104ce0db68aa63b68f4d1d1showard    }
6594b698cacab819b42104ce0db68aa63b68f4d1d1showard
66e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    /**
67e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * @param columns An array specifying the name of each column and the field
68e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * to which it corresponds.  The array should have the form
69e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * {{'field_name1', 'Column Title 1'},
70e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     *  {'field_name2', 'Column Title 2'}, ...}.
71e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     */
72e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    public DataTable(String[][] columns) {
736bc47015cce0ebc2fc255d3950bfeaf4851f36fdshoward        int rows = columns.length;
746bc47015cce0ebc2fc255d3950bfeaf4851f36fdshoward        this.columns = new String[rows][2];
756bc47015cce0ebc2fc255d3950bfeaf4851f36fdshoward        for (int i = 0; i < rows; i++) {
766bc47015cce0ebc2fc255d3950bfeaf4851f36fdshoward            System.arraycopy(columns[i], 0, this.columns[i], 0, 2);
776bc47015cce0ebc2fc255d3950bfeaf4851f36fdshoward        }
786bc47015cce0ebc2fc255d3950bfeaf4851f36fdshoward
79ef37eccb66c415fdb6a8fe63244fe8545463719fshoward        table = new RightClickTable();
80e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        initWidget(table);
81e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
82e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        table.setCellSpacing(0);
83e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        table.setCellPadding(0);
84e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        table.setStyleName("data-table");
85e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
86e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        for (int i = 0; i < columns.length; i++) {
87e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh            table.setText(0, i, columns[i][1]);
88e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        }
89e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
90e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        table.getRowFormatter().setStylePrimaryName(0, HEADER_STYLE);
914cd4763a855d8eb7d25fd4963babc432eb4d25e6showard        table.addClickHandler(this);
92e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    }
9394b698cacab819b42104ce0db68aa63b68f4d1d1showard
943ed34fd6fa69b0d1faba7b67ab32225b63b64781showard    /**
953ed34fd6fa69b0d1faba7b67ab32225b63b64781showard     * Causes the last column of the data table to fill the remainder of the width left in the
963ed34fd6fa69b0d1faba7b67ab32225b63b64781showard     * parent widget.
973ed34fd6fa69b0d1faba7b67ab32225b63b64781showard     */
983ed34fd6fa69b0d1faba7b67ab32225b63b64781showard    public void fillParent() {
993ed34fd6fa69b0d1faba7b67ab32225b63b64781showard        table.getColumnFormatter().setWidth(table.getCellCount(0) - 1, "100%");
1003ed34fd6fa69b0d1faba7b67ab32225b63b64781showard    }
1013ed34fd6fa69b0d1faba7b67ab32225b63b64781showard
10294b698cacab819b42104ce0db68aa63b68f4d1d1showard    public void setWidgetFactory(TableWidgetFactory widgetFactory) {
10394b698cacab819b42104ce0db68aa63b68f4d1d1showard        this.widgetFactory = widgetFactory;
10494b698cacab819b42104ce0db68aa63b68f4d1d1showard    }
105e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
106e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    protected void setRowStyle(int row) {
107e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        table.getRowFormatter().setStyleName(row, "data-row");
108e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        if ((row & 1) == 0) {
109e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh            table.getRowFormatter().addStyleName(row, "data-row-alternate");
110e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        }
111e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        if (clickable) {
112e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh            table.getRowFormatter().addStyleName(row, CLICKABLE_STYLE);
113e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        }
114e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    }
115e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
116e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    public void setClickable(boolean clickable) {
117e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        this.clickable = clickable;
118e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        for(int i = headerRow + 1; i < table.getRowCount(); i++)
119e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh            setRowStyle(i);
120e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    }
121e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
122e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    /**
123e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * Clear all data rows from the table.  Leaves the header rows intact.
124e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     */
125e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    public void clear() {
1268579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward        while (table.getRowCount() > 1) {
1278579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward            table.removeRow(1);
128e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        }
1291c8c2215e525de8813c375e796354f8ffb811a08showard        jsonObjects.clear();
130e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    }
131e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
132e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    /**
133e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * This gets called for every JSONObject that gets added to the table using
134e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * addRow().  This allows subclasses to customize objects before they are
135e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * added to the table, for example to reformat fields or generate new
136e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * fields from the existing data.
137e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * @param row The row object about to be added to the table.
138e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     */
139e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    protected void preprocessRow(JSONObject row) {}
140e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
141e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    protected String[] getRowText(JSONObject row) {
142e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        String[] rowText = new String[columns.length];
143e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        for (int i = 0; i < columns.length; i++) {
14494b698cacab819b42104ce0db68aa63b68f4d1d1showard            if (isWidgetColumn(i))
14594b698cacab819b42104ce0db68aa63b68f4d1d1showard                continue;
14694b698cacab819b42104ce0db68aa63b68f4d1d1showard
147e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh            String columnKey = columns[i][0];
148e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh            JSONValue columnValue = row.get(columnKey);
1498b0ea2285c1327a686ff0b6ab245915e7fd20094showard            rowText[i] = Utils.jsonToString(columnValue);
150e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        }
151e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        return rowText;
152e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    }
153e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
154e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    /**
155e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * Add a row from an array of Strings, one String for each column.
156e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * @param rowData Data for each column, in left-to-right column order.
157e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     */
1581c8c2215e525de8813c375e796354f8ffb811a08showard    protected void addRowFromData(String[] rowData) {
159e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        int row = table.getRowCount();
16094b698cacab819b42104ce0db68aa63b68f4d1d1showard        for(int i = 0; i < columns.length; i++) {
16194b698cacab819b42104ce0db68aa63b68f4d1d1showard            if(isWidgetColumn(i)) {
1628579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward                table.setWidget(row, i, getWidgetForCell(row, i));
16394b698cacab819b42104ce0db68aa63b68f4d1d1showard            } else {
1648579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward                table.setText(row, i, rowData[i]);
16594b698cacab819b42104ce0db68aa63b68f4d1d1showard            }
16694b698cacab819b42104ce0db68aa63b68f4d1d1showard        }
167e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        setRowStyle(row);
168e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    }
169e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
17035dbd8414c0e7022a6a4b54f7ef16b5ff51ae53bshoward    protected boolean isWidgetColumn(int column) {
17135dbd8414c0e7022a6a4b54f7ef16b5ff51ae53bshoward        return columns[column][COL_NAME].equals(WIDGET_COLUMN) || isClickableWidgetColumn(column);
17235dbd8414c0e7022a6a4b54f7ef16b5ff51ae53bshoward    }
17335dbd8414c0e7022a6a4b54f7ef16b5ff51ae53bshoward
17435dbd8414c0e7022a6a4b54f7ef16b5ff51ae53bshoward    protected boolean isClickableWidgetColumn(int column) {
17535dbd8414c0e7022a6a4b54f7ef16b5ff51ae53bshoward        return columns[column][COL_NAME].equals(CLICKABLE_WIDGET_COLUMN);
17694b698cacab819b42104ce0db68aa63b68f4d1d1showard    }
17794b698cacab819b42104ce0db68aa63b68f4d1d1showard
178e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    /**
179e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * Add a row from a JSONObject.  Columns will be populated by pulling fields
180e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * from the objects, as dictated by the columns information passed into the
181e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * DataTable constructor.
182e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     */
183e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    public void addRow(JSONObject row) {
184e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        preprocessRow(row);
1851c8c2215e525de8813c375e796354f8ffb811a08showard        jsonObjects.add(row);
186e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        addRowFromData(getRowText(row));
187e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    }
188e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
189e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    /**
190e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * Add all objects in a JSONArray.
191e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * @param rows An array of JSONObjects
192e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * @throws IllegalArgumentException if any other type of JSONValue is in the
193e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * array.
194e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     */
1954879914c122f4ed97eae3b08c5af1930fd75b39dshoward    public void addRows(List<JSONObject> rows) {
1964879914c122f4ed97eae3b08c5af1930fd75b39dshoward        for (JSONObject row : rows) {
197e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh            addRow(row);
198e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        }
199e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    }
200e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
201e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    /**
202e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * Remove a data row from the table.
2031c8c2215e525de8813c375e796354f8ffb811a08showard     * @param rowIndex The index of the row, where the first data row is indexed 0.
204e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * Header rows are ignored.
205e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     */
2061c8c2215e525de8813c375e796354f8ffb811a08showard    public void removeRow(int rowIndex) {
2071c8c2215e525de8813c375e796354f8ffb811a08showard        jsonObjects.remove(rowIndex);
2081c8c2215e525de8813c375e796354f8ffb811a08showard        int realRow = rowIndex + 1; // header row
209e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        table.removeRow(realRow);
210e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh        for(int i = realRow; i < table.getRowCount(); i++)
211e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh            setRowStyle(i);
212e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    }
213e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
214e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    /**
215e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     * Returns the number of data rows in the table.  The actual number of
2161c8c2215e525de8813c375e796354f8ffb811a08showard     * visible table rows is more than this, due to the header row.
217e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     */
218e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    public int getRowCount() {
2191c8c2215e525de8813c375e796354f8ffb811a08showard        return table.getRowCount() - 1;
220e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    }
2211c8c2215e525de8813c375e796354f8ffb811a08showard
222e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    /**
2231c8c2215e525de8813c375e796354f8ffb811a08showard     * Get the JSONObject corresponding to the indexed row.
224e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh     */
2251c8c2215e525de8813c375e796354f8ffb811a08showard    public JSONObject getRow(int rowIndex) {
2266bc47015cce0ebc2fc255d3950bfeaf4851f36fdshoward        return jsonObjects.get(rowIndex);
227e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    }
228e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh
2298579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    public List<JSONObject> getAllRows() {
2308579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward        return Collections.unmodifiableList(jsonObjects);
2318579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    }
2328579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward
2331c8c2215e525de8813c375e796354f8ffb811a08showard    public void highlightRow(int row) {
2341c8c2215e525de8813c375e796354f8ffb811a08showard        row++; // account for header row
2351c8c2215e525de8813c375e796354f8ffb811a08showard        table.getRowFormatter().addStyleName(row, HIGHLIGHTED_STYLE);
2361c8c2215e525de8813c375e796354f8ffb811a08showard    }
2371c8c2215e525de8813c375e796354f8ffb811a08showard
2381c8c2215e525de8813c375e796354f8ffb811a08showard    public void unhighlightRow(int row) {
2396bc47015cce0ebc2fc255d3950bfeaf4851f36fdshoward        row++; // account for header row
2401c8c2215e525de8813c375e796354f8ffb811a08showard        table.getRowFormatter().removeStyleName(row, HIGHLIGHTED_STYLE);
241e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh    }
242ef37eccb66c415fdb6a8fe63244fe8545463719fshoward
243ef37eccb66c415fdb6a8fe63244fe8545463719fshoward    public void sinkRightClickEvents() {
2444cd4763a855d8eb7d25fd4963babc432eb4d25e6showard        table.addContextMenuHandler(this);
245ef37eccb66c415fdb6a8fe63244fe8545463719fshoward    }
2468579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward
2474cd4763a855d8eb7d25fd4963babc432eb4d25e6showard    @Override
2484cd4763a855d8eb7d25fd4963babc432eb4d25e6showard    public void onClick(ClickEvent event) {
2494cd4763a855d8eb7d25fd4963babc432eb4d25e6showard        onCellClicked(event, false);
2504cd4763a855d8eb7d25fd4963babc432eb4d25e6showard    }
2514cd4763a855d8eb7d25fd4963babc432eb4d25e6showard
2524cd4763a855d8eb7d25fd4963babc432eb4d25e6showard    @Override
2534cd4763a855d8eb7d25fd4963babc432eb4d25e6showard    public void onContextMenu(ContextMenuEvent event) {
2544cd4763a855d8eb7d25fd4963babc432eb4d25e6showard        onCellClicked(event, true);
2554cd4763a855d8eb7d25fd4963babc432eb4d25e6showard    }
2564cd4763a855d8eb7d25fd4963babc432eb4d25e6showard
2574cd4763a855d8eb7d25fd4963babc432eb4d25e6showard    private void onCellClicked(DomEvent<?> event, boolean isRightClick) {
2584cd4763a855d8eb7d25fd4963babc432eb4d25e6showard        HTMLTable.Cell tableCell = table.getCellForDomEvent(event);
2594cd4763a855d8eb7d25fd4963babc432eb4d25e6showard        int row = tableCell.getRowIndex();
2604cd4763a855d8eb7d25fd4963babc432eb4d25e6showard        int cell = tableCell.getCellIndex();
2614cd4763a855d8eb7d25fd4963babc432eb4d25e6showard
262eb0fd4c76ee70e68d907e542423ae249489552c2showard        if (isClickableWidgetColumn(cell) && table.getWidget(row, cell) != null) {
2638579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward            return;
264eb0fd4c76ee70e68d907e542423ae249489552c2showard        }
2658579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward
2664cd4763a855d8eb7d25fd4963babc432eb4d25e6showard        onCellClicked(row, cell, isRightClick);
2674cd4763a855d8eb7d25fd4963babc432eb4d25e6showard    }
2684cd4763a855d8eb7d25fd4963babc432eb4d25e6showard
2694cd4763a855d8eb7d25fd4963babc432eb4d25e6showard    protected void onCellClicked(int row, int cell, boolean isRightClick) {
2708579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward        if (row != headerRow) {
2714cd4763a855d8eb7d25fd4963babc432eb4d25e6showard            notifyListenersClicked(row - headerRow - 1, isRightClick);
2728579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward        }
2738579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    }
2748579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward
2758579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    public void addListener(DataTableListener listener) {
2768579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward        listeners.add(listener);
2778579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    }
2788579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward
2798579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    public void removeListener(DataTableListener listener) {
2808579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward        listeners.remove(listener);
2818579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    }
2828579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward
2834cd4763a855d8eb7d25fd4963babc432eb4d25e6showard    protected void notifyListenersClicked(int rowIndex, boolean isRightClick) {
2848579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward        JSONObject row = getRow(rowIndex);
2858579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward        for (DataTableListener listener : listeners) {
2864cd4763a855d8eb7d25fd4963babc432eb4d25e6showard            listener.onRowClicked(rowIndex, row, isRightClick);
2878579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward        }
2888579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    }
2898579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward
2908579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    public void refreshWidgets() {
2918579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward        for (int row = 1; row < table.getRowCount(); row++) {
2928579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward            for (int column = 0; column < columns.length; column++) {
2938579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward                if (!isWidgetColumn(column)) {
2948579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward                    continue;
2958579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward                }
2968579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward                table.clearCell(row, column);
2978579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward                table.setWidget(row, column, getWidgetForCell(row, column));
2988579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward            }
2998579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward        }
3008579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    }
3018579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward
3028579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    private Widget getWidgetForCell(int row, int column) {
3038579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward        return widgetFactory.createWidget(row - 1, column, jsonObjects.get(row - 1));
3048579ea343f8d4c74b44d5b5cb2df3ef7552b2f6eshoward    }
305e8819cdf80ca0e0602d22551a50f970aa68e108dmbligh}
306