1 /* 2 Copyright (C) 2011 Elvis Stansvik <elvstone@gmail.com> 3 4 For general Scribus (>=1.3.2) copyright and licensing information please refer 5 to the COPYING file provided with the program. Following this notice may exist 6 a copyright and/or license notice that predates the release of Scribus 1.3.2 7 for which a new license (GPL+exception) is in place. 8 */ 9 #ifndef PAGEITEM_TABLE_H 10 #define PAGEITEM_TABLE_H 11 12 #include <QList> 13 #include <QPointF> 14 #include <QRectF> 15 #include <QSet> 16 #include <QString> 17 18 #include "cellarea.h" 19 #include "pageitem.h" 20 #include "scribusapi.h" 21 #include "styles/tablestyle.h" 22 #include "tablecell.h" 23 #include "tablehandle.h" 24 25 class ScPainter; 26 class ScribusDoc; 27 class TablePainter; 28 29 /** 30 * The PageItem_Table class represents a table. 31 * <p> 32 * A table is a group of cells ordered into rows and columns. Each table contains at least 33 * one row and one column. 34 * <p> 35 * A table's dimensions can be changed by using <code>insertRows()</code>, 36 * <code>insertColumns()</code>, <code>removeRows()</code>, or <code>removeColumns()</code>. 37 * Use <code>cellAt()</code> to retrieve table cells. 38 * <p> 39 * Rows and columns can be resized by using <code>resizeRow()</code> and <code>resizeColumn()</code>. 40 * The entire table can be resized by using <code>resize()</code> 41 * <p> 42 * Cells in the table can be merged by using <code>mergeCells()</code>. Cells that have been 43 * previously merged can be split up again by using <code>splitCells()</code>. 44 * <p> 45 * The style of the table can be controlled by setting a named style for the table using 46 * <code>setStyle()</code>. Formatting properties can also be set directly on the table. 47 * Directly set properties overrides properties from the style. 48 */ 49 class SCRIBUS_API PageItem_Table : public PageItem 50 { 51 Q_OBJECT 52 53 Q_PROPERTY(int rows READ rows NOTIFY changed) 54 Q_PROPERTY(int columns READ columns NOTIFY changed) 55 Q_PROPERTY(QString fillColor READ fillColor WRITE setFillColor RESET unsetFillColor NOTIFY changed) 56 Q_PROPERTY(TableBorder leftBorder READ leftBorder WRITE setLeftBorder RESET unsetLeftBorder NOTIFY changed) 57 Q_PROPERTY(TableBorder rightBorder READ rightBorder WRITE setRightBorder RESET unsetRightBorder NOTIFY changed) 58 Q_PROPERTY(TableBorder topBorder READ topBorder WRITE setTopBorder RESET unsetTopBorder NOTIFY changed) 59 Q_PROPERTY(TableBorder bottomBorder READ bottomBorder WRITE setBottomBorder RESET unsetBottomBorder NOTIFY changed) 60 Q_PROPERTY(QString style READ styleName WRITE setStyle RESET unsetStyle NOTIFY changed) 61 62 public: 63 /** 64 * This enum specifies two different strategies for resizing rows and columns. With the 65 * <code>MoveFollowing</code> resize strategy, all following rows or columns are moved 66 * when a column is resized. With the <code>ResizeFollowing</code> strategy, the row or 67 * column that immediately follows the resized row or column is resized to match, but 68 * apart from that, all other rows or columns remain unaffected by the resize. 69 */ 70 enum ResizeStrategy 71 { 72 MoveFollowing, 73 ResizeFollowing 74 }; 75 76 /// The minimum row height. 77 static const double MinimumRowHeight; 78 79 /// The minimum column width. 80 static const double MinimumColumnWidth; 81 82 Q_ENUM(ResizeStrategy HitTarget) 83 84 public: 85 /// Construct a new table item with @a numRows rows and @a numColumns columns. 86 PageItem_Table(ScribusDoc *pa, double x, double y, double w, double h, double w2, const QString& fill, const QString& outline, int numRows = 1, int numColumns = 1); 87 88 /// Destructor. 89 ~PageItem_Table(); 90 91 /// Resizes the table to fit the frame. 92 void adjustTable(); 93 94 /// Return current text properties (current char + paragraph properties) 95 virtual void currentTextProps(ParagraphStyle& parStyle) const override; 96 97 /// Return the list of cell items 98 virtual QList<PageItem*> getChildren() const override; 99 100 /// Return the list of cell items getAllChildren()101 virtual QList<PageItem*> getAllChildren() const override { return getChildren(); } 102 103 /// Returns the number of rows in the table. rows()104 int rows() const { return m_rows; } 105 106 /// Returns the number of columns in the table. columns()107 int columns() const { return m_columns; } 108 109 /** 110 * Returns the width of the table. 111 * 112 * This is the width of the table grid, not including any borders along the left and right side. 113 */ tableWidth()114 double tableWidth() const { return m_columnPositions.last() + m_columnWidths.last(); } 115 116 /** 117 * Returns the height of the table. 118 * 119 * This is the height of the table grid, not including any borders along the top and left side. 120 */ tableHeight()121 double tableHeight() const { return m_rowPositions.last() + m_rowHeights.last(); } 122 123 /** 124 * Returns the offset of the table grid from the item origin. 125 */ gridOffset()126 QPointF gridOffset() const { return QPointF(maxLeftBorderWidth()/2, maxTopBorderWidth()/2); } 127 128 /** 129 * Returns the effective width of the table. 130 * 131 * The effective table width includes the width of the table grid plus half the width of the widest 132 * borders found along its left and right side. 133 */ effectiveWidth()134 double effectiveWidth() const { return tableWidth() + (maxLeftBorderWidth() + maxRightBorderWidth()) / 2; } 135 136 /** 137 * Returns the effective height of the table. 138 * 139 * The effective table height includes the height of the table grid plus half the width of the widest 140 * borders found along its top and bottom side. 141 */ effectiveHeight()142 double effectiveHeight() const { return tableHeight() + (maxTopBorderWidth() + maxBottomBorderWidth()) / 2; } 143 144 /** 145 * Resizes the table grid to @a width, @a height. 146 * 147 * The change in width and height will be distributed proportionally to columns and rows. 148 * 149 * If any row or column hits the limit MinimumRowHeight or MinimumColumnWidth, resizing in that 150 * direction will stop for the entire table. 151 */ 152 void resize(double width, double height); 153 154 /** 155 * Inserts @a numRows rows before the row at @a index. 156 * 157 * If @a index is rows(), a row is appended to the table. 158 * 159 * If @a index is less than 0 or greater than rows(), or if @a numRows is less than 1, 160 * this method does nothing. 161 */ 162 void insertRows(int index, int numRows); 163 164 /** 165 * Removes @a numRows rows from the table, starting with the row at @a index. 166 * 167 * If the specified range falls outside the table or the number of rows is 168 * less than 1 then this method does nothing. 169 */ 170 void removeRows(int index, int numRows); 171 172 /** 173 * Returns the height of @a row, or 0 if @a row does not exist. 174 */ 175 double rowHeight(int row) const; 176 177 /** 178 * Returns the list of row heights for the table. 179 */ rowHeights()180 const QList<double>& rowHeights() const { return m_rowHeights; } 181 182 /** 183 * Resizes @a row to @a height using resize strategy @a strategy. 184 * 185 * If @a row does not exists, this method does nothing. 186 */ 187 void resizeRow(int row, double height, ResizeStrategy strategy = MoveFollowing); 188 189 /** 190 * Distributes the rows from @a startRow to @a endRow evenly. 191 * 192 * All rows in the range will have the same size while the height of the entire range will 193 * remain unaffected. 194 * 195 * If the range is not in the table, this method does nothing. 196 */ 197 void distributeRows(int startRow, int endRow); 198 199 /** 200 * Returns the position of @a row, or 0 if @a row does not exist. 201 */ 202 double rowPosition(int row) const; 203 204 /** 205 * Returns the list of row positions for the table. 206 */ rowPositions()207 const QList<double>& rowPositions() const { return m_rowPositions; } 208 209 /** 210 * Inserts @a numColumns columns before the column at @a index. 211 * 212 * If @a index is columns(), a column is appended to the table. 213 * 214 * If @a index is less than 0 or greater than columns(), or if @a numColumns is less than 1, 215 * this method does nothing. 216 */ 217 void insertColumns(int index, int numColumns); 218 219 /** 220 * Removes @a numColumns columns from the table, starting with the column at @a index. 221 * 222 * If the specified range falls outside the table or the number of columns is 223 * less than 1 then this method does nothing. 224 */ 225 void removeColumns(int index, int numColumns); 226 227 /** 228 * Returns the width of @a column, or 0 if @a column does not exist. 229 */ 230 double columnWidth(int column) const; 231 232 /** 233 * Returns the list of column widths for the table. 234 */ columnWidths()235 const QList<double>& columnWidths() const { return m_columnWidths; } 236 237 /** 238 * Resizes @a column to @a width using resize strategy @a strategy. 239 * 240 * If @a column does not exists, this method does nothing. 241 */ 242 void resizeColumn(int column, double width, ResizeStrategy strategy = MoveFollowing); 243 244 /** 245 * Distributes the columns from @a startColumn to @a endColumn evenly. 246 * 247 * All columns in the range will have the same size while the width of the entire range will 248 * remain unaffected. 249 * 250 * If the range is not in the table, this method does nothing. 251 */ 252 void distributeColumns(int startColumn, int endColumn); 253 254 /** 255 * Returns the position of @a column, or 0 if @a column does not exist. 256 */ 257 double columnPosition(int column) const; 258 259 /** 260 * Returns the list of column positions for the table. 261 */ columnPositions()262 const QList<double>& columnPositions() const { return m_columnPositions; } 263 264 /** 265 * Merges the cell at the specified @a row and @a column with the adjacent cells into 266 * one cell. 267 * 268 * The new cell will span @a numRows rows and @a numCols columns. If @a numRows or 269 * @a numCols is less than the current number of rows or columns the cell spans then 270 * this method does nothing. 271 * 272 * If any of the merged cells are already merged with other cells, the two areas of 273 * merged cells will be united. 274 */ 275 void mergeCells(int row, int column, int numRows, int numCols); 276 277 /** 278 * Splits the specified cell at @a row and @a column into an array of multiple cells with 279 * dimensions specified by @a numRows and @a numCols. 280 */ 281 void splitCell(int row, int column, int numRows, int numCols); 282 283 /** 284 * Returns the set of selected cells. 285 */ selectedCells()286 const QSet<TableCell>& selectedCells() const { return m_selection; } 287 288 /** 289 * Returns the set of selected rows. 290 * 291 * Any row that is spanned by one of the selected cells is considered selected. 292 */ 293 QSet<int> selectedRows() const; 294 295 /** 296 * Returns the set of selected columns. 297 * 298 * Any column that is spanned by one of the selected cells is considered selected. 299 */ 300 QSet<int> selectedColumns() const; 301 302 /** 303 * Adds the cell at @a row, @column to the selection. 304 * 305 * If there's no cell at @a row, @a column, this function does nothing. 306 */ 307 void selectCell(int row, int column); 308 309 /** 310 * Adds the cells in the area between two cells to the selection. 311 * 312 * This functions first fetches the two cells at @a startRow, @a startColumn and 313 * @a endRow, @a endColumn and then selects all cells that intersect the rectangular 314 * area of cells that encloses the two cells. 315 * 316 * If any of the specified cell locations are outside the table, this function does nothing. 317 */ 318 void selectCells(int startRow, int startColumn, int endRow, int endColumn); 319 320 /** 321 * Adds the cells in specified column to the selection. 322 * 323 * If specified column is outside the table, this function does nothing. 324 */ 325 void selectColumn(int column); 326 327 /** 328 * Adds the cells in specified row to the selection. 329 * 330 * If specified row is outside the table, this function does nothing. 331 */ 332 void selectRow(int row); 333 334 /** 335 * Clears the cell selection. 336 */ 337 void clearSelection(); 338 339 /** 340 * Test if item has an active cell selection 341 */ hasSelection()342 bool hasSelection() const { return m_selection.count() > 0; } 343 344 /** 345 * Returns the cell at @a row, @a column. 346 * 347 * If the cell is covered by a spanning cell, the spanning cell is returned. If the cell is 348 * not in this table, an invalid cell is returned. The table may later mark cells returned 349 * by this function as invalid, if the row or column containing the cell is removed. 350 */ 351 TableCell cellAt(int row, int column) const; 352 353 /** 354 * Returns the cell at @a point, which is in canvas coordinates. 355 * 356 * If the cell at @a point is covered by a spanning cell, the spanning cell is returned. 357 * If @a point is outside the table grid, an invalid cell is returned. The table may later 358 * mark cells returned by this function as invalid, if the row or column containing the 359 * cell is removed. 360 */ 361 TableCell cellAt(const QPointF& point) const; 362 363 /** 364 * Returns the currently active cell. 365 * 366 * A table always has a valid active cell. This is the cell containing the cursor and that 367 * will receive keyboard input during cell text editing. 368 */ activeCell()369 TableCell activeCell() const { return m_activeCell; } 370 371 /** 372 * Moves left in the table, or do nothing if at the table end. 373 */ 374 void moveLeft(); 375 376 /** 377 * Moves right in the table, or do nothing if at the table end. 378 */ 379 void moveRight(); 380 381 /** 382 * Moves up in the table, or do nothing if at the table end. 383 */ 384 void moveUp(); 385 386 /** 387 * Moves down in the table, or do nothing if at the table end. 388 */ 389 void moveDown(); 390 391 /** 392 * Moves to @a cell, or the cell at row 0, column 0 if @a cell is invalid. 393 */ 394 void moveTo(const TableCell& cell); 395 396 /** 397 * Performs a hit test at @a point, which is in canvas coordinates. 398 * 399 * The returned handle describes what was hit. @a threshold is a distance in points. 400 * @a point is considered to hit a handle if it is within @a threshold from it. 401 */ 402 TableHandle hitTest(const QPointF& point, double threshold) const; 403 404 /// Resizes the table to fit the frame. 405 void adjustTableToFrame(); 406 407 /// Resizes the frame to fit the table. 408 void adjustFrameToTable(); 409 410 /// Sets the fill color of this table to @a color. 411 void setFillColor(const QString& color); 412 413 /// Unsets the fill color of this table. 414 void unsetFillColor(); 415 416 /// Returns the fill color of this table. 417 QString fillColor() const; 418 419 /// Sets the fill shade of this table to @a color. 420 void setFillShade(const double& shade); 421 422 /// Unsets the fill shade of this table. 423 void unsetFillShade(); 424 425 /// Returns the fill shade of this table. 426 double fillShade() const; 427 428 /// Sets the left border of this table to @a border. 429 void setLeftBorder(const TableBorder& border); 430 431 /// Unsets the left border of this table. 432 void unsetLeftBorder(); 433 434 /// Returns the left border of this table. 435 TableBorder leftBorder() const; 436 437 /// Sets the right border of this table to @a border. 438 void setRightBorder(const TableBorder& border); 439 440 /// Unsets the right border of this table. 441 void unsetRightBorder(); 442 443 /// Returns the right border of this table. 444 TableBorder rightBorder() const; 445 446 /// Sets the top border of this table to @a border. 447 void setTopBorder(const TableBorder& border); 448 449 /// Unsets the top border of this table. 450 void unsetTopBorder(); 451 452 /// Returns the top border of this table. 453 TableBorder topBorder() const; 454 455 /// Sets the bottom border of this table to @a border. 456 void setBottomBorder(const TableBorder& border); 457 458 /// Unsets the bottom border of this table. 459 void unsetBottomBorder(); 460 461 /// Returns the bottom border of this table. 462 TableBorder bottomBorder() const; 463 464 /// Sets the table style of this table to @a style. 465 void setStyle(const QString& style); 466 467 /// Unsets the style of this table. 468 void unsetStyle(); 469 470 /// Unsets direct formatting 471 void unsetDirectFormatting(); 472 473 /// Returns the style of this table. 474 const TableStyle& style() const; 475 476 /// Returns the style name of this table. 477 QString styleName() const; 478 479 /// Updates the position and size of all cell text frames for this table. updateCells()480 void updateCells() { updateCells(0, 0, rows() - 1, columns() - 1); } 481 482 /// Updates the position and size of cell text frames for cells in the specified area. 483 void updateCells(int startRow, int startColumn, int endRow, int endColumn); 484 485 /// Returns <code>true</code> if the table is overflowing its frame. isOverflowing()486 bool isOverflowing() const { return effectiveWidth() > width() || effectiveHeight() > height(); } 487 488 /// Returns this item as a PageItem_Table. asTable()489 PageItem_Table* asTable() override { return this; } 490 491 /// Returns <code>true</code>. isTable()492 bool isTable() const override { return true; } 493 494 /// Returns PageItem::Table. realItemType()495 ItemType realItemType() const override { return PageItem::Table; } 496 497 /// Return true if item is susceptible to contain text in a way or another isTextContainer()498 bool isTextContainer() const override { return true; } 499 500 /// Adds the applicable actions for this table to @a actionList. 501 void applicableActions(QStringList& actionList) override; 502 503 /// Returns a textual description of this item. infoDescription()504 QString infoDescription() const override { return QString(); } 505 506 /// Returns the Cell Areas from this table cellAreas()507 const QList<CellArea>& cellAreas() const { return m_cellAreas; } 508 509 /// Returns the rows of the table for writing to SLA cellRows()510 const QList<QList<TableCell> >& cellRows() const { return m_cellRows; } 511 512 /// Set the layer for the item 513 void setLayer(int layerId) override; 514 515 /// Set the masterpage the object is on 516 void setMasterPage(int page, const QString& mpName) override; 517 518 /// Set the masterpage the object is on 519 void setMasterPageName(const QString& mpName) override; 520 521 /// Set the page "owning" the object 522 void setOwnerPage(int page) override; 523 524 /// Collect named resource of table and its cells 525 void getNamedResources(ResourceCollection& lists) const override; 526 527 /// Replace named resource of table and its cells 528 void replaceNamedResources(ResourceCollection& newNames) override; 529 530 /// creates valid layout information 531 void layout() override; 532 533 signals: 534 /// This signal is emitted whenever the table changes. 535 void changed(); 536 /// This signal is emitted whenever the cell selection changes. 537 void selectionChanged(); 538 539 private slots: 540 /// Handles cell and table style changes in the document. 541 void handleStyleChanged(); 542 543 protected: 544 /// Paints this item. 545 void DrawObj_Item(ScPainter *p, QRectF clipRect) override; 546 547 private: 548 /// Enum describing types of changes on a table. For internal use. 549 enum ChangeType 550 { 551 RowsInserted, /**< Rows were inserted. */ 552 RowsRemoved, /**< Rows were removed. */ 553 ColumnsInserted, /**< Columns were inserted. */ 554 ColumnsRemoved /**< Columns were removed. */ 555 }; 556 557 /** 558 * Initializes the table with @a numRows rows and @a numColumns columns. 559 * 560 * Should be called once, and once only, during table construction. 561 */ 562 void initialize(int numRows, int numColumns); 563 564 /// Activates the cell @a cell, or the cell at row 0, column 0 if @a cell is invalid. 565 void activateCell(const TableCell& cell); 566 567 /// Returns true if @a row is a row in this table. validRow(int row)568 bool validRow(int row) const { return row >= 0 && row < m_rows; } 569 /// Returns true if @a column is a column in this table. validColumn(int column)570 bool validColumn(int column) const { return column >= 0 && column < m_columns; } 571 /// Returns true if there is a cell at @a row, @a column in this table. validCell(int row,int column)572 bool validCell(int row, int column) const { return validRow(row) && validColumn(column); } 573 574 /// Returns the width of the widest border along the left side of this table. 575 double maxLeftBorderWidth() const; 576 577 /// Returns the width of the widest border along the right side of this table. 578 double maxRightBorderWidth() const; 579 580 /// Returns the width of the widest border along the top side of this table. 581 double maxTopBorderWidth() const; 582 583 /// Returns the width of the widest border along the bottom side of this table. 584 double maxBottomBorderWidth() const; 585 586 /// TODO: Turn these into strategies to be reused in resize gestures. 587 /// Resizes @a row according to the MoveFollowing strategy and returns the new height. 588 double resizeRowMoveFollowing(int row, double height); 589 /// Resizes @a row according to the ResizeFollowing strategy and returns the new height. 590 double resizeRowResizeFollowing(int row, double height); 591 /// Resizes @a column according to the MoveFollowing strategy and returns the new width. 592 double resizeColumnMoveFollowing(int row, double width); 593 /// Resizes @a column according to the ResizeFollowing strategy and returns the new width. 594 double resizeColumnResizeFollowing(int row, double width); 595 596 /** 597 * Updates row and column spans following a change in rows or columns. 598 * 599 * If @a changeType is <code>RowsInserted</code> or <code>ColumnsInserted</code>, @a index 600 * and @a number specifies that @a number rows or columns were inserted before the row or 601 * or column at @a index. Similarly, if @a changeType is <code>RowsRemoved</code> or 602 * <code>ColumnsRemoved</code>, @a index and @a number specifies that @a number rows or 603 * columns were removed, starting with the row or column at @a index. 604 */ 605 void updateSpans(int index, int number, ChangeType changeType); 606 607 /// Prints internal table information. For internal use. 608 void debug() const; 609 610 /// Table sanity check. Very slow. For internal use. 611 void assertValid() const; 612 613 private: 614 //<<Data we need to save 615 /// List of rows of cells in the table. 616 QList<QList<TableCell> > m_cellRows; 617 618 /// Number of rows. 619 int m_rows; 620 /// Number of columns. 621 int m_columns; 622 623 /// Vertical positions of rows. 624 QList<double> m_rowPositions; 625 /// Height of rows. 626 QList<double> m_rowHeights; 627 628 /// Horizontal positions of columns. 629 QList<double> m_columnPositions; 630 /// Width of columns. 631 QList<double> m_columnWidths; 632 633 /// List of areas of merged cells. 634 QList<CellArea> m_cellAreas; 635 636 /// Style of the table. 637 TableStyle m_style; 638 //>>End of data we need to save 639 //----------------------------- 640 //<<Live working variables/data 641 /// Set of selected cells. 642 QSet<TableCell> m_selection; 643 644 /// The table painter to paint the table with. 645 TablePainter* m_tablePainter; 646 647 /// Currently active cell. 648 TableCell m_activeCell; 649 650 /* 651 * The two members below describe the active "logical position" on the table grid. 652 * This position may or may not correspond with the position of the active cell. 653 * 654 * As an example, imagine the following scenario: 655 * 656 * +--------+--------+ +--------+--------+ 657 * | | | | | | | 658 * +--------+ | --> User presses Tab --> +--------+ | 659 * | | | | | | | 660 * +--------+--------+ +--------+--------+ 661 * Cursor in (1, 0) Cursor in (0, 1) 662 * 663 * The active cell is now the spanning cell at (0, 1). If we would use the row and 664 * column of the active cell as the basis for keyboard navigation between cells, 665 * then pressing Shift+Tab after the scenario above would bring the cursor into 666 * cell (0, 0), not back to to (1, 0). Hence, in addition to the active cell, we 667 * keep track of a "logical position". This is the logical position on the table 668 * grid that is active. In the above scenario, the logical position would change 669 * from (1, 0) to (1, 1). 670 */ 671 672 /// The logical active row. 673 int m_activeRow {0}; 674 675 /// The logical active column. 676 int m_activeColumn {0}; 677 //>>End of live working variables/data 678 }; 679 680 #endif // PAGEITEM_TABLE_H 681