Index: frontend/client/src/autotest/common/table/FragmentedTable.java |
diff --git a/frontend/client/src/autotest/common/table/FragmentedTable.java b/frontend/client/src/autotest/common/table/FragmentedTable.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..53a30b3cbdc83ec24123dba807825d62d2d93411 |
--- /dev/null |
+++ b/frontend/client/src/autotest/common/table/FragmentedTable.java |
@@ -0,0 +1,148 @@ |
+package autotest.common.table; |
+ |
+import autotest.common.DomUtils; |
+import autotest.common.ui.RightClickTable; |
+ |
+import com.google.gwt.event.dom.client.ClickEvent; |
+import com.google.gwt.user.client.DOM; |
+import com.google.gwt.user.client.Element; |
+import com.google.gwt.user.client.Event; |
+import com.google.gwt.user.client.ui.HTMLTable; |
+ |
+import java.util.ArrayList; |
+import java.util.List; |
+ |
+/** |
+ * Customized table class supporting multiple tbody elements. It is modified to support input |
+ * handling, getRowCount(), getCellCount(), and getCellFormatter().getElement(). getElement() |
+ * also works. Calls to other methods aren't guaranteed to work. |
+ */ |
+public class FragmentedTable extends RightClickTable { |
+ public class FragmentedCellFormatter extends HTMLTable.CellFormatter { |
+ @Override |
+ public Element getElement(int row, int column) { |
+ checkCellBounds(row, column); |
+ Element bodyElem = bodyElems.get(getFragmentIndex(row)); |
+ return getCellElement(bodyElem, getRowWithinFragment(row), column); |
+ } |
+ |
+ /** |
+ * Native method to efficiently get a td element from a tbody. Copied from GWT's |
+ * HTMLTable.java. |
+ */ |
+ private native Element getCellElement(Element tbody, int row, int col) /*-{ |
+ return tbody.rows[row].cells[col]; |
+ }-*/; |
+ } |
+ |
+ private List<Element> bodyElems = new ArrayList<Element>(); |
+ private int totalRowCount; |
+ private int rowsPerFragment; |
+ |
+ public FragmentedTable() { |
+ super(); |
+ setCellFormatter(new FragmentedCellFormatter()); |
+ |
+ // Reset the FragmentedTable to clear out elements that were added by the HTMLTable and |
+ // FlexTable constructors |
+ reset(); |
+ } |
+ |
+ /** |
+ * This method must be called after added or removing tbody elements and before using other |
+ * functionality (accessing cell elements, input handling, etc.). |
+ */ |
+ public void updateBodyElems() { |
+ totalRowCount = 0; |
+ Element tbody = DOM.getFirstChild(getElement()); |
+ for(; tbody != null; tbody = DOM.getNextSibling(tbody)) { |
+ assert tbody.getTagName().equalsIgnoreCase("tbody"); |
+ bodyElems.add(tbody); |
+ totalRowCount += getRowCount(tbody); |
+ } |
+ } |
+ |
+ public void reset() { |
+ bodyElems.clear(); |
+ DomUtils.clearDomChildren(getElement()); |
+ } |
+ |
+ private int getRowWithinFragment(int row) { |
+ return row % rowsPerFragment; |
+ } |
+ |
+ private int getFragmentIndex(int row) { |
+ return row / rowsPerFragment; |
+ } |
+ |
+ @Override |
+ public HTMLTable.Cell getCellForEvent(ClickEvent event) { |
+ return getCellForDomEvent(event); |
+ } |
+ |
+ @Override |
+ protected RowColumn getCellPosition(Element td) { |
+ Element tr = DOM.getParent(td); |
+ Element body = DOM.getParent(tr); |
+ int fragmentIndex = DOM.getChildIndex(getElement(), body); |
+ int rowWithinFragment = DOM.getChildIndex(body, tr); |
+ int row = fragmentIndex * rowsPerFragment + rowWithinFragment; |
+ int column = DOM.getChildIndex(tr, td); |
+ return new RowColumn(row, column); |
+ } |
+ |
+ /** |
+ * This is a modified version of getEventTargetCell() from HTMLTable.java. |
+ */ |
+ @Override |
+ protected Element getEventTargetCell(Event event) { |
+ Element td = DOM.eventGetTarget(event); |
+ for (; td != null; td = DOM.getParent(td)) { |
+ // If it's a TD, it might be the one we're looking for. |
+ if (DOM.getElementProperty(td, "tagName").equalsIgnoreCase("td")) { |
+ // Make sure it's directly a part of this table before returning |
+ // it. |
+ Element tr = DOM.getParent(td); |
+ Element body = DOM.getParent(tr); |
+ Element tableElem = DOM.getParent(body); |
+ if (tableElem == getElement()) { |
+ return td; |
+ } |
+ } |
+ // If we run into this table's element, we're out of options. |
+ if (td == getElement()) { |
+ return null; |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ @Override |
+ public int getCellCount(int row) { |
+ Element bodyElem = bodyElems.get(getFragmentIndex(row)); |
+ return getCellCount(bodyElem, getRowWithinFragment(row)); |
+ } |
+ |
+ @Override |
+ public int getRowCount() { |
+ return totalRowCount; |
+ } |
+ |
+ private native int getRowCount(Element tbody) /*-{ |
+ return tbody.rows.length; |
+ }-*/; |
+ |
+ private native int getCellCount(Element tbody, int row) /*-{ |
+ return tbody.rows[row].cells.length; |
+ }-*/; |
+ |
+ /** |
+ * This must be called before using other functionality (accessing cell elements, input |
+ * handling, etc.). |
+ * @param rowsPerFragment The number of rows in each tbody. The last tbody may have fewer |
+ * rows. All others must have exactly this number of rows. |
+ */ |
+ public void setRowsPerFragment(int rowsPerFragment) { |
+ this.rowsPerFragment = rowsPerFragment; |
+ } |
+} |