OLD | NEW |
(Empty) | |
| 1 package autotest.common.spreadsheet; |
| 2 |
| 3 import autotest.common.UnmodifiableSublistView; |
| 4 import autotest.common.Utils; |
| 5 import autotest.common.table.FragmentedTable; |
| 6 import autotest.common.table.TableRenderer; |
| 7 import autotest.common.ui.RightClickTable; |
| 8 |
| 9 import com.google.gwt.dom.client.Element; |
| 10 import com.google.gwt.event.dom.client.ClickEvent; |
| 11 import com.google.gwt.event.dom.client.ClickHandler; |
| 12 import com.google.gwt.event.dom.client.ContextMenuEvent; |
| 13 import com.google.gwt.event.dom.client.ContextMenuHandler; |
| 14 import com.google.gwt.event.dom.client.DomEvent; |
| 15 import com.google.gwt.event.dom.client.ScrollEvent; |
| 16 import com.google.gwt.event.dom.client.ScrollHandler; |
| 17 import com.google.gwt.user.client.DeferredCommand; |
| 18 import com.google.gwt.user.client.IncrementalCommand; |
| 19 import com.google.gwt.user.client.Window; |
| 20 import com.google.gwt.user.client.ui.Composite; |
| 21 import com.google.gwt.user.client.ui.FlexTable; |
| 22 import com.google.gwt.user.client.ui.HTMLTable; |
| 23 import com.google.gwt.user.client.ui.Panel; |
| 24 import com.google.gwt.user.client.ui.ScrollPanel; |
| 25 import com.google.gwt.user.client.ui.SimplePanel; |
| 26 import com.google.gwt.user.client.ui.Widget; |
| 27 |
| 28 import java.util.ArrayList; |
| 29 import java.util.Collection; |
| 30 import java.util.HashMap; |
| 31 import java.util.List; |
| 32 import java.util.Map; |
| 33 |
| 34 public class Spreadsheet extends Composite |
| 35 implements ScrollHandler, ClickHandler, ContextMenuHandler { |
| 36 |
| 37 private static final int MIN_TABLE_SIZE_PX = 90; |
| 38 private static final int WINDOW_BORDER_PX = 15; |
| 39 private static final int SCROLLBAR_FUDGE = 16; |
| 40 private static final String BLANK_STRING = "(empty)"; |
| 41 private static final int CELL_PADDING_PX = 2; |
| 42 private static final int TD_BORDER_PX = 1; |
| 43 private static final String HIGHLIGHTED_CLASS = "highlighted"; |
| 44 private static final int CELLS_PER_ITERATION = 1000; |
| 45 |
| 46 private Header rowFields, columnFields; |
| 47 private List<Header> rowHeaderValues = new ArrayList<Header>(); |
| 48 private List<Header> columnHeaderValues = new ArrayList<Header>(); |
| 49 private Map<Header, Integer> rowHeaderMap = new HashMap<Header, Integer>(); |
| 50 private Map<Header, Integer> columnHeaderMap = new HashMap<Header, Integer>(
); |
| 51 protected CellInfo[][] dataCells, rowHeaderCells, columnHeaderCells; |
| 52 private RightClickTable rowHeaders = new RightClickTable(); |
| 53 private RightClickTable columnHeaders = new RightClickTable(); |
| 54 private FlexTable parentTable = new FlexTable(); |
| 55 private FragmentedTable dataTable = new FragmentedTable(); |
| 56 private int rowsPerIteration; |
| 57 private Panel rowHeadersClipPanel, columnHeadersClipPanel; |
| 58 private ScrollPanel scrollPanel = new ScrollPanel(dataTable); |
| 59 private TableRenderer renderer = new TableRenderer(); |
| 60 |
| 61 private SpreadsheetListener listener; |
| 62 |
| 63 public interface SpreadsheetListener { |
| 64 public void onCellClicked(CellInfo cellInfo, boolean isRightClick); |
| 65 } |
| 66 |
| 67 public static interface Header extends List<String> {} |
| 68 public static class HeaderImpl extends ArrayList<String> implements Header { |
| 69 public HeaderImpl() { |
| 70 } |
| 71 |
| 72 public HeaderImpl(Collection<? extends String> arg0) { |
| 73 super(arg0); |
| 74 } |
| 75 |
| 76 public static Header fromBaseType(List<String> baseType) { |
| 77 return new HeaderImpl(baseType); |
| 78 } |
| 79 } |
| 80 |
| 81 public static class CellInfo { |
| 82 public Header row, column; |
| 83 public String contents; |
| 84 public String color; |
| 85 public Integer widthPx, heightPx; |
| 86 public int rowSpan = 1, colSpan = 1; |
| 87 public int testCount = 0; |
| 88 public int testIndex; |
| 89 |
| 90 public CellInfo(Header row, Header column, String contents) { |
| 91 this.row = row; |
| 92 this.column = column; |
| 93 this.contents = contents; |
| 94 } |
| 95 |
| 96 public boolean isHeader() { |
| 97 return !isEmpty() && (row == null || column == null); |
| 98 } |
| 99 |
| 100 public boolean isEmpty() { |
| 101 return row == null && column == null; |
| 102 } |
| 103 } |
| 104 |
| 105 private class RenderCommand implements IncrementalCommand { |
| 106 private int state = 0; |
| 107 private int rowIndex = 0; |
| 108 private IncrementalCommand onFinished; |
| 109 |
| 110 public RenderCommand(IncrementalCommand onFinished) { |
| 111 this.onFinished = onFinished; |
| 112 } |
| 113 |
| 114 private void renderSomeRows() { |
| 115 renderer.renderRowsAndAppend(dataTable, dataCells, |
| 116 rowIndex, rowsPerIteration, true); |
| 117 rowIndex += rowsPerIteration; |
| 118 if (rowIndex > dataCells.length) { |
| 119 state++; |
| 120 } |
| 121 } |
| 122 |
| 123 public boolean execute() { |
| 124 switch (state) { |
| 125 case 0: |
| 126 computeRowsPerIteration(); |
| 127 computeHeaderCells(); |
| 128 break; |
| 129 case 1: |
| 130 renderHeaders(); |
| 131 expandRowHeaders(); |
| 132 break; |
| 133 case 2: |
| 134 // resize everything to the max dimensions (the window size) |
| 135 fillWindow(false); |
| 136 break; |
| 137 case 3: |
| 138 // set main table to match header sizes |
| 139 matchRowHeights(rowHeaders, dataCells); |
| 140 matchColumnWidths(columnHeaders, dataCells); |
| 141 dataTable.setVisible(false); |
| 142 break; |
| 143 case 4: |
| 144 // render the main data table |
| 145 renderSomeRows(); |
| 146 return true; |
| 147 case 5: |
| 148 dataTable.updateBodyElems(); |
| 149 dataTable.setVisible(true); |
| 150 break; |
| 151 case 6: |
| 152 // now expand headers as necessary |
| 153 // this can be very slow, so put it in it's own cycle |
| 154 matchRowHeights(dataTable, rowHeaderCells); |
| 155 break; |
| 156 case 7: |
| 157 matchColumnWidths(dataTable, columnHeaderCells); |
| 158 renderHeaders(); |
| 159 break; |
| 160 case 8: |
| 161 // shrink the scroller if the table ended up smaller than th
e window |
| 162 fillWindow(true); |
| 163 DeferredCommand.addCommand(onFinished); |
| 164 return false; |
| 165 } |
| 166 |
| 167 state++; |
| 168 return true; |
| 169 } |
| 170 } |
| 171 |
| 172 public Spreadsheet() { |
| 173 dataTable.setStyleName("spreadsheet-data"); |
| 174 killPaddingAndSpacing(dataTable); |
| 175 |
| 176 rowHeaders.setStyleName("spreadsheet-headers"); |
| 177 killPaddingAndSpacing(rowHeaders); |
| 178 rowHeadersClipPanel = wrapWithClipper(rowHeaders); |
| 179 |
| 180 columnHeaders.setStyleName("spreadsheet-headers"); |
| 181 killPaddingAndSpacing(columnHeaders); |
| 182 columnHeadersClipPanel = wrapWithClipper(columnHeaders); |
| 183 |
| 184 scrollPanel.setStyleName("spreadsheet-scroller"); |
| 185 scrollPanel.setAlwaysShowScrollBars(true); |
| 186 scrollPanel.addScrollHandler(this); |
| 187 |
| 188 parentTable.setStyleName("spreadsheet-parent"); |
| 189 killPaddingAndSpacing(parentTable); |
| 190 parentTable.setWidget(0, 1, columnHeadersClipPanel); |
| 191 parentTable.setWidget(1, 0, rowHeadersClipPanel); |
| 192 parentTable.setWidget(1, 1, scrollPanel); |
| 193 |
| 194 setupTableInput(dataTable); |
| 195 setupTableInput(rowHeaders); |
| 196 setupTableInput(columnHeaders); |
| 197 |
| 198 initWidget(parentTable); |
| 199 } |
| 200 |
| 201 private void setupTableInput(RightClickTable table) { |
| 202 table.addContextMenuHandler(this); |
| 203 table.addClickHandler(this); |
| 204 } |
| 205 |
| 206 protected void killPaddingAndSpacing(HTMLTable table) { |
| 207 table.setCellSpacing(0); |
| 208 table.setCellPadding(0); |
| 209 } |
| 210 |
| 211 /* |
| 212 * Wrap a widget with a panel that will clip its contents rather than grow |
| 213 * too much. |
| 214 */ |
| 215 protected Panel wrapWithClipper(Widget w) { |
| 216 SimplePanel wrapper = new SimplePanel(); |
| 217 wrapper.add(w); |
| 218 wrapper.setStyleName("clipper"); |
| 219 return wrapper; |
| 220 } |
| 221 |
| 222 public void setHeaderFields(Header rowFields, Header columnFields) { |
| 223 this.rowFields = rowFields; |
| 224 this.columnFields = columnFields; |
| 225 } |
| 226 |
| 227 private void addHeader(List<Header> headerList, Map<Header, Integer> headerM
ap, |
| 228 List<String> header) { |
| 229 Header headerObject = HeaderImpl.fromBaseType(header); |
| 230 assert !headerMap.containsKey(headerObject); |
| 231 headerList.add(headerObject); |
| 232 headerMap.put(headerObject, headerMap.size()); |
| 233 } |
| 234 |
| 235 public void addRowHeader(List<String> header) { |
| 236 addHeader(rowHeaderValues, rowHeaderMap, header); |
| 237 } |
| 238 |
| 239 public void addColumnHeader(List<String> header) { |
| 240 addHeader(columnHeaderValues, columnHeaderMap, header); |
| 241 } |
| 242 |
| 243 private int getHeaderPosition(Map<Header, Integer> headerMap, Header header)
{ |
| 244 assert headerMap.containsKey(header); |
| 245 return headerMap.get(header); |
| 246 } |
| 247 |
| 248 private int getRowPosition(Header rowHeader) { |
| 249 return getHeaderPosition(rowHeaderMap, rowHeader); |
| 250 } |
| 251 |
| 252 private int getColumnPosition(Header columnHeader) { |
| 253 return getHeaderPosition(columnHeaderMap, columnHeader); |
| 254 } |
| 255 |
| 256 /** |
| 257 * Must be called after adding headers but before adding data |
| 258 */ |
| 259 public void prepareForData() { |
| 260 dataCells = new CellInfo[rowHeaderValues.size()][columnHeaderValues.size
()]; |
| 261 } |
| 262 |
| 263 public CellInfo getCellInfo(int row, int column) { |
| 264 Header rowHeader = rowHeaderValues.get(row); |
| 265 Header columnHeader = columnHeaderValues.get(column); |
| 266 if (dataCells[row][column] == null) { |
| 267 dataCells[row][column] = new CellInfo(rowHeader, columnHeader, ""); |
| 268 } |
| 269 return dataCells[row][column]; |
| 270 } |
| 271 |
| 272 private CellInfo getCellInfo(CellInfo[][] cells, int row, int column) { |
| 273 if (cells[row][column] == null) { |
| 274 cells[row][column] = new CellInfo(null, null, " "); |
| 275 } |
| 276 return cells[row][column]; |
| 277 } |
| 278 |
| 279 /** |
| 280 * Render the data into HTML tables. Done through a deferred command. |
| 281 */ |
| 282 public void render(IncrementalCommand onFinished) { |
| 283 DeferredCommand.addCommand(new RenderCommand(onFinished)); |
| 284 } |
| 285 |
| 286 private void renderHeaders() { |
| 287 renderer.renderRows(rowHeaders, rowHeaderCells, false); |
| 288 renderer.renderRows(columnHeaders, columnHeaderCells, false); |
| 289 } |
| 290 |
| 291 public void computeRowsPerIteration() { |
| 292 int cellsPerRow = columnHeaderValues.size(); |
| 293 rowsPerIteration = Math.max(CELLS_PER_ITERATION / cellsPerRow, 1); |
| 294 dataTable.setRowsPerFragment(rowsPerIteration); |
| 295 } |
| 296 |
| 297 private void computeHeaderCells() { |
| 298 rowHeaderCells = new CellInfo[rowHeaderValues.size()][rowFields.size()]; |
| 299 fillHeaderCells(rowHeaderCells, rowFields, rowHeaderValues, true); |
| 300 |
| 301 columnHeaderCells = new CellInfo[columnFields.size()][columnHeaderValues
.size()]; |
| 302 fillHeaderCells(columnHeaderCells, columnFields, columnHeaderValues, fal
se); |
| 303 } |
| 304 |
| 305 /** |
| 306 * TODO (post-1.0) - this method needs good cleanup and documentation |
| 307 */ |
| 308 private void fillHeaderCells(CellInfo[][] cells, Header fields, List<Header>
headerValues, |
| 309 boolean isRows) { |
| 310 int headerSize = fields.size(); |
| 311 String[] lastFieldValue = new String[headerSize]; |
| 312 CellInfo[] lastCellInfo = new CellInfo[headerSize]; |
| 313 int[] counter = new int[headerSize]; |
| 314 boolean newHeader; |
| 315 for (int headerIndex = 0; headerIndex < headerValues.size(); headerIndex
++) { |
| 316 Header header = headerValues.get(headerIndex); |
| 317 newHeader = false; |
| 318 for (int fieldIndex = 0; fieldIndex < headerSize; fieldIndex++) { |
| 319 String fieldValue = header.get(fieldIndex); |
| 320 if (newHeader || !fieldValue.equals(lastFieldValue[fieldIndex]))
{ |
| 321 newHeader = true; |
| 322 Header currentHeader = getSubHeader(header, fieldIndex + 1); |
| 323 String cellContents = formatHeader(fields.get(fieldIndex), f
ieldValue); |
| 324 CellInfo cellInfo; |
| 325 if (isRows) { |
| 326 cellInfo = new CellInfo(currentHeader, null, cellContent
s); |
| 327 cells[headerIndex][fieldIndex] = cellInfo; |
| 328 } else { |
| 329 cellInfo = new CellInfo(null, currentHeader, cellContent
s); |
| 330 cells[fieldIndex][counter[fieldIndex]] = cellInfo; |
| 331 counter[fieldIndex]++; |
| 332 } |
| 333 lastFieldValue[fieldIndex] = fieldValue; |
| 334 lastCellInfo[fieldIndex] = cellInfo; |
| 335 } else { |
| 336 incrementSpan(lastCellInfo[fieldIndex], isRows); |
| 337 } |
| 338 } |
| 339 } |
| 340 } |
| 341 |
| 342 private String formatHeader(String field, String value) { |
| 343 if (value.equals("")) { |
| 344 return BLANK_STRING; |
| 345 } |
| 346 value = Utils.escape(value); |
| 347 if (field.equals("kernel")) { |
| 348 // line break after each /, for long paths |
| 349 value = value.replace("/", "/<br>").replace("/<br>/<br>", "//"); |
| 350 } |
| 351 return value; |
| 352 } |
| 353 |
| 354 private void incrementSpan(CellInfo cellInfo, boolean isRows) { |
| 355 if (isRows) { |
| 356 cellInfo.rowSpan++; |
| 357 } else { |
| 358 cellInfo.colSpan++; |
| 359 } |
| 360 } |
| 361 |
| 362 private Header getSubHeader(Header header, int length) { |
| 363 if (length == header.size()) { |
| 364 return header; |
| 365 } |
| 366 List<String> subHeader = new UnmodifiableSublistView<String>(header, 0,
length); |
| 367 return new HeaderImpl(subHeader); |
| 368 } |
| 369 |
| 370 private void matchRowHeights(HTMLTable from, CellInfo[][] to) { |
| 371 int lastColumn = to[0].length - 1; |
| 372 int rowCount = from.getRowCount(); |
| 373 for (int row = 0; row < rowCount; row++) { |
| 374 int height = getRowHeight(from, row); |
| 375 getCellInfo(to, row, lastColumn).heightPx = height - 2 * CELL_PADDIN
G_PX; |
| 376 } |
| 377 } |
| 378 |
| 379 private void matchColumnWidths(HTMLTable from, CellInfo[][] to) { |
| 380 int lastToRow = to.length - 1; |
| 381 int lastFromRow = from.getRowCount() - 1; |
| 382 for (int column = 0; column < from.getCellCount(lastFromRow); column++)
{ |
| 383 int width = getColumnWidth(from, column); |
| 384 getCellInfo(to, lastToRow, column).widthPx = width - 2 * CELL_PADDIN
G_PX; |
| 385 } |
| 386 } |
| 387 |
| 388 protected String getTableCellText(HTMLTable table, int row, int column) { |
| 389 Element td = table.getCellFormatter().getElement(row, column); |
| 390 Element div = td.getFirstChildElement(); |
| 391 if (div == null) |
| 392 return null; |
| 393 String contents = Utils.unescape(div.getInnerHTML()); |
| 394 if (contents.equals(BLANK_STRING)) |
| 395 contents = ""; |
| 396 return contents; |
| 397 } |
| 398 |
| 399 public void clear() { |
| 400 rowHeaderValues.clear(); |
| 401 columnHeaderValues.clear(); |
| 402 rowHeaderMap.clear(); |
| 403 columnHeaderMap.clear(); |
| 404 dataCells = rowHeaderCells = columnHeaderCells = null; |
| 405 dataTable.reset(); |
| 406 |
| 407 setRowHeadersOffset(0); |
| 408 setColumnHeadersOffset(0); |
| 409 } |
| 410 |
| 411 /** |
| 412 * Make the spreadsheet fill the available window space to the right and bot
tom |
| 413 * of its position. |
| 414 */ |
| 415 public void fillWindow(boolean useTableSize) { |
| 416 int newHeightPx = Window.getClientHeight() - (columnHeaders.getAbsoluteT
op() + |
| 417 columnHeaders.getOffsetHei
ght()); |
| 418 newHeightPx = adjustMaxDimension(newHeightPx); |
| 419 int newWidthPx = Window.getClientWidth() - (rowHeaders.getAbsoluteLeft()
+ |
| 420 rowHeaders.getOffsetWidth())
; |
| 421 newWidthPx = adjustMaxDimension(newWidthPx); |
| 422 if (useTableSize) { |
| 423 newHeightPx = Math.min(newHeightPx, rowHeaders.getOffsetHeight()); |
| 424 newWidthPx = Math.min(newWidthPx, columnHeaders.getOffsetWidth()); |
| 425 } |
| 426 |
| 427 // apply the changes all together |
| 428 rowHeadersClipPanel.setHeight(getSizePxString(newHeightPx)); |
| 429 columnHeadersClipPanel.setWidth(getSizePxString(newWidthPx)); |
| 430 scrollPanel.setSize(getSizePxString(newWidthPx + SCROLLBAR_FUDGE), |
| 431 getSizePxString(newHeightPx + SCROLLBAR_FUDGE)); |
| 432 } |
| 433 |
| 434 /** |
| 435 * Adjust a maximum table dimension to allow room for edge decoration and |
| 436 * always maintain a minimum height |
| 437 */ |
| 438 protected int adjustMaxDimension(int maxDimensionPx) { |
| 439 return Math.max(maxDimensionPx - WINDOW_BORDER_PX - SCROLLBAR_FUDGE, |
| 440 MIN_TABLE_SIZE_PX); |
| 441 } |
| 442 |
| 443 protected String getSizePxString(int sizePx) { |
| 444 return sizePx + "px"; |
| 445 } |
| 446 |
| 447 /** |
| 448 * Ensure the row header clip panel allows the full width of the row headers |
| 449 * to display. |
| 450 */ |
| 451 protected void expandRowHeaders() { |
| 452 int width = rowHeaders.getOffsetWidth(); |
| 453 rowHeadersClipPanel.setWidth(getSizePxString(width)); |
| 454 } |
| 455 |
| 456 private Element getCellElement(HTMLTable table, int row, int column) { |
| 457 return table.getCellFormatter().getElement(row, column); |
| 458 } |
| 459 |
| 460 private Element getCellElement(CellInfo cellInfo) { |
| 461 assert cellInfo.row != null || cellInfo.column != null; |
| 462 Element tdElement; |
| 463 if (cellInfo.row == null) { |
| 464 tdElement = getCellElement(columnHeaders, 0, getColumnPosition(cellI
nfo.column)); |
| 465 } else if (cellInfo.column == null) { |
| 466 tdElement = getCellElement(rowHeaders, getRowPosition(cellInfo.row),
0); |
| 467 } else { |
| 468 tdElement = getCellElement(dataTable, getRowPosition(cellInfo.row), |
| 469 getColumnPosition(cellInfo.col
umn)); |
| 470 } |
| 471 Element cellElement = tdElement.getFirstChildElement(); |
| 472 assert cellElement != null; |
| 473 return cellElement; |
| 474 } |
| 475 |
| 476 protected int getColumnWidth(HTMLTable table, int column) { |
| 477 // using the column formatter doesn't seem to work |
| 478 int numRows = table.getRowCount(); |
| 479 return table.getCellFormatter().getElement(numRows - 1, column).getOffse
tWidth() - |
| 480 TD_BORDER_PX; |
| 481 } |
| 482 |
| 483 protected int getRowHeight(HTMLTable table, int row) { |
| 484 // see getColumnWidth() |
| 485 int numCols = table.getCellCount(row); |
| 486 return table.getCellFormatter().getElement(row, numCols - 1).getOffsetHe
ight() - |
| 487 TD_BORDER_PX; |
| 488 } |
| 489 |
| 490 /** |
| 491 * Update floating headers. |
| 492 */ |
| 493 @Override |
| 494 public void onScroll(ScrollEvent event) { |
| 495 int scrollLeft = scrollPanel.getHorizontalScrollPosition(); |
| 496 int scrollTop = scrollPanel.getScrollPosition(); |
| 497 |
| 498 setColumnHeadersOffset(-scrollLeft); |
| 499 setRowHeadersOffset(-scrollTop); |
| 500 } |
| 501 |
| 502 protected void setRowHeadersOffset(int offset) { |
| 503 rowHeaders.getElement().getStyle().setPropertyPx("top", offset); |
| 504 } |
| 505 |
| 506 protected void setColumnHeadersOffset(int offset) { |
| 507 columnHeaders.getElement().getStyle().setPropertyPx("left", offset); |
| 508 } |
| 509 |
| 510 @Override |
| 511 public void onClick(ClickEvent event) { |
| 512 handleEvent(event, false); |
| 513 } |
| 514 |
| 515 @Override |
| 516 public void onContextMenu(ContextMenuEvent event) { |
| 517 handleEvent(event, true); |
| 518 } |
| 519 |
| 520 private void handleEvent(DomEvent<?> event, boolean isRightClick) { |
| 521 if (listener == null) |
| 522 return; |
| 523 |
| 524 assert event.getSource() instanceof RightClickTable; |
| 525 HTMLTable.Cell tableCell = ((RightClickTable) event.getSource()).getCell
ForDomEvent(event); |
| 526 int row = tableCell.getRowIndex(); |
| 527 int column = tableCell.getCellIndex(); |
| 528 |
| 529 CellInfo[][] cells; |
| 530 if (event.getSource() == rowHeaders) { |
| 531 cells = rowHeaderCells; |
| 532 column = adjustRowHeaderColumnIndex(row, column); |
| 533 } |
| 534 else if (event.getSource() == columnHeaders) { |
| 535 cells = columnHeaderCells; |
| 536 } |
| 537 else { |
| 538 assert event.getSource() == dataTable; |
| 539 cells = dataCells; |
| 540 } |
| 541 CellInfo cell = cells[row][column]; |
| 542 if (cell == null || cell.isEmpty()) |
| 543 return; // don't report clicks on empty cells |
| 544 |
| 545 listener.onCellClicked(cell, isRightClick); |
| 546 } |
| 547 |
| 548 /** |
| 549 * In HTMLTables, a cell with rowspan > 1 won't count in column indices for
the extra rows it |
| 550 * spans, which will mess up column indices for other cells in those rows.
This method adjusts |
| 551 * the column index passed to onCellClicked() to account for that. |
| 552 */ |
| 553 private int adjustRowHeaderColumnIndex(int row, int column) { |
| 554 for (int i = 0; i < rowFields.size(); i++) { |
| 555 if (rowHeaderCells[row][i] != null) { |
| 556 return i + column; |
| 557 } |
| 558 } |
| 559 |
| 560 throw new RuntimeException("Failed to find non-null cell"); |
| 561 } |
| 562 |
| 563 public void setListener(SpreadsheetListener listener) { |
| 564 this.listener = listener; |
| 565 } |
| 566 |
| 567 public void setHighlighted(CellInfo cell, boolean highlighted) { |
| 568 Element cellElement = getCellElement(cell); |
| 569 if (highlighted) { |
| 570 cellElement.setClassName(HIGHLIGHTED_CLASS); |
| 571 } else { |
| 572 cellElement.setClassName(""); |
| 573 } |
| 574 } |
| 575 |
| 576 public List<Integer> getAllTestIndices() { |
| 577 List<Integer> testIndices = new ArrayList<Integer>(); |
| 578 |
| 579 for (CellInfo[] row : dataCells) { |
| 580 for (CellInfo cellInfo : row) { |
| 581 if (cellInfo != null && !cellInfo.isEmpty()) { |
| 582 testIndices.add(cellInfo.testIndex); |
| 583 } |
| 584 } |
| 585 } |
| 586 |
| 587 return testIndices; |
| 588 } |
| 589 } |
OLD | NEW |