1 package autotest.common.table; 2 3 import autotest.common.DomUtils; 4 import autotest.common.ui.RightClickTable; 5 6 import com.google.gwt.event.dom.client.ClickEvent; 7 import com.google.gwt.user.client.DOM; 8 import com.google.gwt.user.client.Element; 9 import com.google.gwt.user.client.Event; 10 import com.google.gwt.user.client.ui.HTMLTable; 11 12 import java.util.ArrayList; 13 import java.util.List; 14 15 /** 16 * Customized table class supporting multiple tbody elements. It is modified to support input 17 * handling, getRowCount(), getCellCount(), and getCellFormatter().getElement(). getElement() 18 * also works. Calls to other methods aren't guaranteed to work. 19 */ 20 public class FragmentedTable extends RightClickTable { 21 public class FragmentedCellFormatter extends HTMLTable.CellFormatter { 22 @Override getElement(int row, int column)23 public Element getElement(int row, int column) { 24 checkCellBounds(row, column); 25 Element bodyElem = bodyElems.get(getFragmentIndex(row)); 26 return getCellElement(bodyElem, getRowWithinFragment(row), column); 27 } 28 29 /** 30 * Native method to efficiently get a td element from a tbody. Copied from GWT's 31 * HTMLTable.java. 32 */ getCellElement(Element tbody, int row, int col)33 private native Element getCellElement(Element tbody, int row, int col) /*-{ 34 return tbody.rows[row].cells[col]; 35 }-*/; 36 } 37 38 private List<Element> bodyElems = new ArrayList<Element>(); 39 private int totalRowCount; 40 private int rowsPerFragment; 41 FragmentedTable()42 public FragmentedTable() { 43 super(); 44 setCellFormatter(new FragmentedCellFormatter()); 45 46 // Reset the FragmentedTable to clear out elements that were added by the HTMLTable and 47 // FlexTable constructors 48 reset(); 49 } 50 51 /** 52 * This method must be called after added or removing tbody elements and before using other 53 * functionality (accessing cell elements, input handling, etc.). 54 */ updateBodyElems()55 public void updateBodyElems() { 56 totalRowCount = 0; 57 Element tbody = DOM.getFirstChild(getElement()); 58 for(; tbody != null; tbody = DOM.getNextSibling(tbody)) { 59 assert tbody.getTagName().equalsIgnoreCase("tbody"); 60 bodyElems.add(tbody); 61 totalRowCount += getRowCount(tbody); 62 } 63 } 64 reset()65 public void reset() { 66 bodyElems.clear(); 67 DomUtils.clearDomChildren(getElement()); 68 } 69 getRowWithinFragment(int row)70 private int getRowWithinFragment(int row) { 71 return row % rowsPerFragment; 72 } 73 getFragmentIndex(int row)74 private int getFragmentIndex(int row) { 75 return row / rowsPerFragment; 76 } 77 78 @Override getCellForEvent(ClickEvent event)79 public HTMLTable.Cell getCellForEvent(ClickEvent event) { 80 return getCellForDomEvent(event); 81 } 82 83 @Override getCellPosition(Element td)84 protected RowColumn getCellPosition(Element td) { 85 Element tr = DOM.getParent(td); 86 Element body = DOM.getParent(tr); 87 int fragmentIndex = DOM.getChildIndex(getElement(), body); 88 int rowWithinFragment = DOM.getChildIndex(body, tr); 89 int row = fragmentIndex * rowsPerFragment + rowWithinFragment; 90 int column = DOM.getChildIndex(tr, td); 91 return new RowColumn(row, column); 92 } 93 94 /** 95 * This is a modified version of getEventTargetCell() from HTMLTable.java. 96 */ 97 @Override getEventTargetCell(Event event)98 protected Element getEventTargetCell(Event event) { 99 Element td = DOM.eventGetTarget(event); 100 for (; td != null; td = DOM.getParent(td)) { 101 // If it's a TD, it might be the one we're looking for. 102 if (DOM.getElementProperty(td, "tagName").equalsIgnoreCase("td")) { 103 // Make sure it's directly a part of this table before returning 104 // it. 105 Element tr = DOM.getParent(td); 106 Element body = DOM.getParent(tr); 107 Element tableElem = DOM.getParent(body); 108 if (tableElem == getElement()) { 109 return td; 110 } 111 } 112 // If we run into this table's element, we're out of options. 113 if (td == getElement()) { 114 return null; 115 } 116 } 117 return null; 118 } 119 120 @Override getCellCount(int row)121 public int getCellCount(int row) { 122 Element bodyElem = bodyElems.get(getFragmentIndex(row)); 123 return getCellCount(bodyElem, getRowWithinFragment(row)); 124 } 125 126 @Override getRowCount()127 public int getRowCount() { 128 return totalRowCount; 129 } 130 getRowCount(Element tbody)131 private native int getRowCount(Element tbody) /*-{ 132 return tbody.rows.length; 133 }-*/; 134 getCellCount(Element tbody, int row)135 private native int getCellCount(Element tbody, int row) /*-{ 136 return tbody.rows[row].cells.length; 137 }-*/; 138 139 /** 140 * This must be called before using other functionality (accessing cell elements, input 141 * handling, etc.). 142 * @param rowsPerFragment The number of rows in each tbody. The last tbody may have fewer 143 * rows. All others must have exactly this number of rows. 144 */ setRowsPerFragment(int rowsPerFragment)145 public void setRowsPerFragment(int rowsPerFragment) { 146 this.rowsPerFragment = rowsPerFragment; 147 } 148 } 149