| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * A class encapsulating the managemnet of the Html [:<table>:] representing the
spreadsheet grid. | |
| 7 */ | |
| 8 class HtmlTable { | |
| 9 | |
| 10 DivElement _formulaCellSelectingDiv; | |
| 11 Spreadsheet _spreadsheet; | |
| 12 TableElement _table; | |
| 13 | |
| 14 // Must be called after the creation of the <tbody> element | |
| 15 DivElement get formulaCellSelectingDiv() { | |
| 16 if (_formulaCellSelectingDiv == null) { | |
| 17 _formulaCellSelectingDiv = new Element.tag("div"); | |
| 18 _formulaCellSelectingDiv.id = "formulaSelectingCell-${_spreadsheet.name}"; | |
| 19 _formulaCellSelectingDiv.attributes["class"] = "formulaSelectingCell"; | |
| 20 } | |
| 21 _table.nodes.add(_formulaCellSelectingDiv); | |
| 22 return _formulaCellSelectingDiv; | |
| 23 } | |
| 24 | |
| 25 // FIXME | |
| 26 ElementEvents get on() => _table.on; | |
| 27 | |
| 28 HtmlTable(this._spreadsheet, Element parent) { | |
| 29 Document doc = parent.document; | |
| 30 | |
| 31 _table = new Element.tag("table"); | |
| 32 _table.attributes["class"] = "spreadsheet"; | |
| 33 _table.style.setProperty("position", "absolute"); | |
| 34 _table.style.setProperty("z-index", "2"); | |
| 35 _table.style.setProperty("left", "0"); | |
| 36 _table.style.setProperty("top", "0"); | |
| 37 | |
| 38 parent.nodes.add(_table); | |
| 39 } | |
| 40 | |
| 41 void addColumn(int col, int columnWidth) { | |
| 42 Document doc = _table.document; | |
| 43 bool first = true; | |
| 44 for (TableRowElement row in _table.rows) { | |
| 45 TableCellElement cell; | |
| 46 if (first) { | |
| 47 first = false; | |
| 48 cell = new Element.tag("td"); | |
| 49 cell.width = HtmlUtils.toPx(columnWidth); | |
| 50 row.insertBefore(cell, row.cells[col]); | |
| 51 } else { | |
| 52 cell = row.insertCell(col); | |
| 53 } | |
| 54 } | |
| 55 } | |
| 56 | |
| 57 void addRow(int row, int columns) { | |
| 58 TableRowElement rowElement = _table.insertRow(row); | |
| 59 rowElement.style.setProperty("height", HtmlUtils.toPx(CssStyles.DEFAULT_ROW_
HEIGHT)); | |
| 60 for (int col = 0; col <= columns; col++) { | |
| 61 TableCellElement cell = rowElement.insertCell(col); | |
| 62 // If the row being added is the first row, set the column widths | |
| 63 if (row == 0) { | |
| 64 cell.width = HtmlUtils.toPx(_spreadsheet.getColumnWidth(col)); | |
| 65 } | |
| 66 } | |
| 67 } | |
| 68 | |
| 69 // FIXME | |
| 70 void appendChild(Node child) { | |
| 71 _table.nodes.add(child); | |
| 72 } | |
| 73 | |
| 74 void deleteRow(int index) { | |
| 75 _table.deleteRow(index); | |
| 76 } | |
| 77 | |
| 78 Future<ElementRect> get rect() => _table.rect; | |
| 79 | |
| 80 // FIXME? | |
| 81 TableRowElement getRowElement(int row) => _table.rows[row]; | |
| 82 | |
| 83 // Add or remove a class name from a table cell. | |
| 84 void modifyClasses(Set<CellLocation> locations, String className, bool add, | |
| 85 int rows, int columns, int rowShift, int columnShift) { | |
| 86 List<Node> tableRows = _table.rows; | |
| 87 locations.forEach((CellLocation loc) { | |
| 88 if (!_inView(loc.rowCol, rows, columns, rowShift, columnShift)) { | |
| 89 return; | |
| 90 } | |
| 91 TableRowElement rowElement = tableRows[loc.row - rowShift]; | |
| 92 TableCellElement cellElement = rowElement.cells[loc.col - columnShift]; | |
| 93 if (add) { | |
| 94 cellElement.classes.add(className); | |
| 95 } else { | |
| 96 cellElement.classes.remove(className); | |
| 97 } | |
| 98 }); | |
| 99 } | |
| 100 | |
| 101 int numRows() => _table.rows.length; | |
| 102 | |
| 103 int redraw(SelectionManager selectionManager, | |
| 104 int rows, int columns, int rowShift, int columnShift, int cellDisplay) { | |
| 105 List<Node> tableRows = _table.rows; | |
| 106 TableRowElement tableHeaderRow = tableRows[0]; | |
| 107 List<Node> tableHeaderCells = tableHeaderRow.cells; | |
| 108 TableRowElement rowElement; | |
| 109 TableCellElement cellElement; | |
| 110 DOMTokenList cellClasses; | |
| 111 | |
| 112 redrawHeaders(selectionManager, rows, columns, rowShift, columnShift, cellDi
splay); | |
| 113 | |
| 114 int rendered = 0; | |
| 115 | |
| 116 // Render dirty cells | |
| 117 _spreadsheet.forEachDirtyCell((RowCol rowCol) { | |
| 118 if (!_inView(rowCol, rows, columns, rowShift, columnShift)) { | |
| 119 return; | |
| 120 } | |
| 121 rowElement = tableRows[rowCol.row - rowShift]; | |
| 122 cellElement = rowElement.cells[rowCol.col - columnShift]; | |
| 123 Cell cell = _spreadsheet.getCell(rowCol); | |
| 124 if (cell != null && cellDisplay == SpreadsheetPresenter.CELL_DISPLAY_VALUE
S) { | |
| 125 Style style = cell.style; | |
| 126 CSSStyleDeclaration cssStyle = cellElement.style; | |
| 127 setCellStyle(style, cssStyle); | |
| 128 } else { | |
| 129 cellElement.attributes.remove("style"); | |
| 130 } | |
| 131 | |
| 132 if (cell != null) { | |
| 133 // Force recalculation | |
| 134 String html = cell.toHtml(); | |
| 135 switch (cellDisplay) { | |
| 136 case SpreadsheetPresenter.CELL_DISPLAY_VALUES: | |
| 137 break; | |
| 138 case SpreadsheetPresenter.CELL_DISPLAY_CONTENTS: | |
| 139 if (cell.isEmpty()) { | |
| 140 html = "<i>empty</i>"; | |
| 141 } else if (cell.isFormula()) { | |
| 142 html = "${HtmlUtils.quoteHtml(cell.getContentString())}"; | |
| 143 } else { | |
| 144 html = ""${HtmlUtils.quoteHtml(cell.getContentString())}""
; | |
| 145 } | |
| 146 break; | |
| 147 case SpreadsheetPresenter.CELL_DISPLAY_PASTE_CONTENTS: | |
| 148 if (cell.isEmpty()) { | |
| 149 html = "<i>empty</i>"; | |
| 150 } else { | |
| 151 html = HtmlUtils.quoteHtml(cell.getPasteContent()); | |
| 152 } | |
| 153 break; | |
| 154 case SpreadsheetPresenter.CELL_DISPLAY_STYLES: | |
| 155 html = cell.getStyleAsHtml(); | |
| 156 break; | |
| 157 case SpreadsheetPresenter.CELL_DISPLAY_DATATYPE: | |
| 158 html = HtmlUtils.quoteHtml(cell.getDatatypeAsString()); | |
| 159 break; | |
| 160 case SpreadsheetPresenter.CELL_DISPLAY_DEPS: | |
| 161 StringBuffer sb = new StringBuffer(); | |
| 162 Set<CellLocation> deps = cell.getDependencies(); | |
| 163 if (deps == null) { | |
| 164 html = "--"; | |
| 165 } else { | |
| 166 bool first = true; | |
| 167 deps.forEach((CellLocation cellLocation) { | |
| 168 if (!first) { | |
| 169 sb.add(","); | |
| 170 } | |
| 171 first = false; | |
| 172 sb.add(cellLocation.rowCol.toA1String()); | |
| 173 }); | |
| 174 html = HtmlUtils.quoteHtml(sb.toString()); | |
| 175 } | |
| 176 break; | |
| 177 case SpreadsheetPresenter.CELL_DISPLAY_FORWARD_DEPS: | |
| 178 StringBuffer sb = new StringBuffer(); | |
| 179 Set<CellLocation> deps = cell.dependents; | |
| 180 if (deps == null) { | |
| 181 html = "--"; | |
| 182 } else { | |
| 183 bool first = true; | |
| 184 deps.forEach((CellLocation cellLocation) { | |
| 185 if (!first) { | |
| 186 sb.add(","); | |
| 187 } | |
| 188 first = false; | |
| 189 sb.add(cellLocation.rowCol.toA1String()); | |
| 190 }); | |
| 191 html = HtmlUtils.quoteHtml(sb.toString()); | |
| 192 } | |
| 193 break; | |
| 194 } | |
| 195 | |
| 196 cellElement.innerHTML = html; | |
| 197 ++rendered; | |
| 198 | |
| 199 cell.clearStyleDirty(); | |
| 200 } else { | |
| 201 cellElement.innerHTML = ""; | |
| 202 } | |
| 203 }); | |
| 204 | |
| 205 // Set row/column styles for empty cells | |
| 206 RowColStyle sheetStyle = _spreadsheet.getSheetStyle(); | |
| 207 for (int r = 1; r <= rows; r++) { | |
| 208 int row = r + rowShift; | |
| 209 rowElement = tableRows[r]; | |
| 210 RowColStyle rowStyle = _spreadsheet.getRowStyle(row); | |
| 211 for (int c = 1; c <= columns; c++) { | |
| 212 int col = c + columnShift; | |
| 213 Cell cell = _spreadsheet.getCell(new RowCol(row, col)); | |
| 214 // Non-null cells have already been rendered above | |
| 215 if (cell != null) { | |
| 216 continue; | |
| 217 } | |
| 218 RowColStyle columnStyle = _spreadsheet.getColumnStyle(col); | |
| 219 Style style = RowColStyle.merge3(sheetStyle, rowStyle, columnStyle); | |
| 220 cellElement = rowElement.cells[c]; | |
| 221 CSSStyleDeclaration cssStyle = cellElement.style; | |
| 222 if (cellDisplay == SpreadsheetPresenter.CELL_DISPLAY_VALUES) { | |
| 223 if (style != null) { | |
| 224 setCellStyle(style, cssStyle); | |
| 225 } else { | |
| 226 cssStyle.removeProperty("background-color"); | |
| 227 } | |
| 228 } else if (cellDisplay == SpreadsheetPresenter.CELL_DISPLAY_STYLES) { | |
| 229 if (style != null) { | |
| 230 cellElement.innerHTML = style.toHtml(); | |
| 231 } else { | |
| 232 cellElement.innerHTML = ""; | |
| 233 } | |
| 234 } | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 return rendered; | |
| 239 } | |
| 240 | |
| 241 void redrawHeaders(SelectionManager selectionManager, | |
| 242 int rows, int columns, int rowShift, int columnShift, int cellDisplay) { | |
| 243 List<Node> tableRows = _table.rows; | |
| 244 TableRowElement tableHeaderRow = tableRows[0]; | |
| 245 List<Node> tableHeaderCells = tableHeaderRow.cells; | |
| 246 TableCellElement cellElement; | |
| 247 Set<String> cellClasses; | |
| 248 | |
| 249 // Redraw first column header | |
| 250 TableCellElement firstColumnCell = tableHeaderCells[0]; | |
| 251 firstColumnCell.classes.add("cornerHeader"); | |
| 252 firstColumnCell.width = HtmlUtils.toPx(_spreadsheet.getColumnWidth(0)); | |
| 253 | |
| 254 RowColStyle sheetStyle = _spreadsheet.getSheetStyle(); | |
| 255 | |
| 256 bool displayStyles = cellDisplay == SpreadsheetPresenter.CELL_DISPLAY_STYLES
; | |
| 257 | |
| 258 // Redraw column headers | |
| 259 for (int c = 0; c <= columns; c++) { | |
| 260 // Column headers - physical row 0 contains the column numbers | |
| 261 cellElement = tableHeaderCells[c]; | |
| 262 cellClasses = cellElement.classes; | |
| 263 cellElement.width = HtmlUtils.toPx(_spreadsheet.getColumnWidth(c == 0 ? 0
: c + columnShift)); | |
| 264 if (displayStyles) { | |
| 265 cellClasses.remove("columnHeader"); | |
| 266 cellClasses.remove("columnHeader-selected"); | |
| 267 } else { | |
| 268 if (selectionManager.isColumnSelected(c + columnShift)) { | |
| 269 cellClasses.remove("columnHeader"); | |
| 270 cellClasses.add("columnHeader-selected"); | |
| 271 } else { | |
| 272 cellClasses.remove("columnHeader-selected"); | |
| 273 cellClasses.add("columnHeader"); | |
| 274 } | |
| 275 } | |
| 276 | |
| 277 int col = c + columnShift; | |
| 278 String html = c == 0 ? HtmlUtils.quoteHtml(_spreadsheet.name) : StringUtil
s.columnString(col); | |
| 279 if (displayStyles) { | |
| 280 RowColStyle s; | |
| 281 if (c == 0) { | |
| 282 s = _spreadsheet.getSheetStyle(); | |
| 283 if (s != null) { | |
| 284 html += s.style.toHtml(); | |
| 285 } | |
| 286 } else { | |
| 287 s = _spreadsheet.getColumnStyle(col); | |
| 288 if (s != null) { | |
| 289 html = "${html}: ${s.style.toHtml()}"; | |
| 290 } | |
| 291 } | |
| 292 } | |
| 293 cellElement.innerHTML = html; | |
| 294 } | |
| 295 | |
| 296 // Redraw first row header | |
| 297 TableRowElement firstTableRow = tableRows[0]; | |
| 298 firstTableRow.style.setProperty("height", HtmlUtils.toPx(_spreadsheet.getRow
Height(0))); | |
| 299 | |
| 300 // Redraw row headers | |
| 301 for (int r = 1; r <= rows; r++) { | |
| 302 TableRowElement tableRow = tableRows[r]; | |
| 303 tableRow.style.setProperty("height", | |
| 304 HtmlUtils.toPx(_spreadsheet.getRowHeight(r + rowShift))); | |
| 305 List<Node> rowCells = tableRow.cells; | |
| 306 // Row headers - physical column 0 contains the row numbers | |
| 307 cellElement = rowCells[0]; | |
| 308 cellClasses = cellElement.classes; | |
| 309 if (displayStyles) { | |
| 310 cellClasses.remove("rowHeader"); | |
| 311 cellClasses.remove("rowHeader-selected"); | |
| 312 } else { | |
| 313 if (selectionManager.isRowSelected(r + rowShift)) { | |
| 314 cellClasses.remove("rowHeader"); | |
| 315 cellClasses.add("rowHeader-selected"); | |
| 316 } else { | |
| 317 cellClasses.remove("rowHeader-selected"); | |
| 318 cellClasses.add("rowHeader"); | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 int row = r + rowShift; | |
| 323 String html = StringUtils.rowString(row); | |
| 324 if (displayStyles) { | |
| 325 RowColStyle rowStyle = _spreadsheet.getRowStyle(row); | |
| 326 if (rowStyle != null) { | |
| 327 html = "${html}: ${rowStyle.style.toHtml()}"; | |
| 328 } | |
| 329 } | |
| 330 cellElement.innerHTML = html; | |
| 331 } | |
| 332 } | |
| 333 | |
| 334 void removeColumn(int col) { | |
| 335 for (TableRowElement row in _table.rows) { | |
| 336 row.deleteCell(col); | |
| 337 } | |
| 338 } | |
| 339 | |
| 340 // Remove innerHTML and class names from the entire visible portion of the tab
le | |
| 341 void resetTableContents(int rows, int columns) { | |
| 342 List<Node> tableRows = _table.rows; | |
| 343 for (int r = 0; r <= rows; r++) { | |
| 344 TableRowElement tableRow = tableRows[r]; | |
| 345 List<Node> tableRowCells = tableRow.cells; | |
| 346 int oldLen = tableRowCells.length; | |
| 347 for (int c = 0; c <= columns; c++) { | |
| 348 TableCellElement cellElement = tableRowCells[c]; | |
| 349 cellElement.attributes.remove("class"); | |
| 350 // TODO: can the style attribute be removed entirely? | |
| 351 cellElement.style.cssText = ""; | |
| 352 cellElement.innerHTML = ""; | |
| 353 } | |
| 354 // Delete extra cells | |
| 355 for (int c = oldLen - 1; c > columns; c--) { | |
| 356 tableRow.deleteCell(c); | |
| 357 } | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 void setCellContents(int row, int col, String value) { | |
| 362 TableRowElement rowElement = _table.rows[row]; | |
| 363 TableCellElement cellElement = rowElement.cells[col]; | |
| 364 cellElement.innerHTML = HtmlUtils.quoteHtml(value); | |
| 365 } | |
| 366 | |
| 367 void setCellCursor() { | |
| 368 _table.style.setProperty("cursor", "cell"); | |
| 369 } | |
| 370 | |
| 371 // Renders aspects of a cell's style that require styling the containing <TD>
element, | |
| 372 // namely text alignment, text color, and background color. | |
| 373 void setCellStyle(Style style, CSSStyleDeclaration cssStyle) { | |
| 374 String textAlignment = style.getTextAlignmentString(); | |
| 375 cssStyle.setProperty("text-align", textAlignment); | |
| 376 | |
| 377 int textColor = style.textColor; | |
| 378 if (textColor != Style.BLACK && textColor != Style.UNSET) { | |
| 379 String textColorString = Formats.getHtmlColor(textColor); | |
| 380 cssStyle.setProperty("color", textColorString); | |
| 381 } else { | |
| 382 cssStyle.removeProperty("color"); | |
| 383 } | |
| 384 | |
| 385 int backgroundColor = style.backgroundColor; | |
| 386 if (backgroundColor != Style.UNSET && backgroundColor != Style.WHITE) { | |
| 387 String backgroundColorString = Formats.getHtmlColor(backgroundColor); | |
| 388 cssStyle.setProperty("background-color", backgroundColorString); | |
| 389 } else { | |
| 390 cssStyle.removeProperty("background-color"); | |
| 391 } | |
| 392 } | |
| 393 | |
| 394 void setColResizeCursor() { | |
| 395 _table.style.setProperty("cursor", "col-resize"); | |
| 396 } | |
| 397 | |
| 398 void setDefaultCursor() { | |
| 399 _table.style.removeProperty("cursor"); | |
| 400 } | |
| 401 | |
| 402 void setRowResizeCursor() { | |
| 403 _table.style.setProperty("cursor", "row-resize"); | |
| 404 } | |
| 405 | |
| 406 void setWidth(int width) { | |
| 407 _table.style.setProperty("width", HtmlUtils.toPx(width)); | |
| 408 } | |
| 409 | |
| 410 // Return true if the given (row, col) is within the viewport of this presente
r. | |
| 411 bool _inView(RowCol rowCol, int rows, int columns, int rowShift, int columnShi
ft) { | |
| 412 int row = rowCol.row - rowShift; | |
| 413 if (row <= 0 || row > rows) { | |
| 414 return false; | |
| 415 } | |
| 416 int col = rowCol.col - columnShift; | |
| 417 if (col <= 0 || col > columns) { | |
| 418 return false; | |
| 419 } | |
| 420 return true; | |
| 421 } | |
| 422 } | |
| OLD | NEW |