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