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