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 #include <algorithm>
10 
11 #include <QDebug>
12 #include <QList>
13 #include <QMutableListIterator>
14 #include <QMutableSetIterator>
15 #include <QPointF>
16 #include <QRectF>
17 #include <QSet>
18 #include <QString>
19 #include <QTransform>
20 #include <QtAlgorithms>
21 
22 #include "appmodes.h"
23 #include "cellarea.h"
24 #include "collapsedtablepainter.h"
25 #include "pageitem.h"
26 #include "pageitem_textframe.h"
27 #include "scpainter.h"
28 #include "scribusdoc.h"
29 #include "styles/tablestyle.h"
30 #include "tablehandle.h"
31 #include "tableutils.h"
32 
33 #include "pageitem_table.h"
34 
35 #ifdef WANT_DEBUG
36 	#define ASSERT_VALID() assertValid(); qt_noop()
37 #else
38 	#define ASSERT_VALID() qt_noop()
39 #endif
40 
41 // The minimum row height.
42 const double PageItem_Table::MinimumRowHeight = 3.0;
43 
44 // The minimum column width.
45 const double PageItem_Table::MinimumColumnWidth = 3.0;
46 
PageItem_Table(ScribusDoc * pa,double x,double y,double w,double h,double w2,const QString & fill,const QString & outline,int numRows,int numColumns)47 PageItem_Table::PageItem_Table(ScribusDoc *pa, double x, double y, double w, double h, double w2, const QString& fill, const QString& outline, int numRows, int numColumns) :
48 	PageItem(pa, PageItem::Table, x, y, w, h, w2, fill, outline),
49 	m_rows(0), m_columns(0), m_tablePainter(new CollapsedTablePainter(this))
50 {
51 	initialize(numRows, numColumns);
52 
53 	doc()->dontResize = true;
54 	adjustTableToFrame();
55 	adjustFrameToTable();
56 	doc()->dontResize = false;
57 
58 	ASSERT_VALID();
59 }
60 
~PageItem_Table()61 PageItem_Table::~PageItem_Table()
62 {
63 	delete m_tablePainter;
64 }
65 
adjustTable()66 void PageItem_Table::adjustTable()
67 {
68 	doc()->dontResize = true;
69 	adjustTableToFrame();
70 	adjustFrameToTable();
71 	doc()->dontResize = false;
72 }
73 
currentTextProps(ParagraphStyle & parStyle) const74 void PageItem_Table::currentTextProps(ParagraphStyle& parStyle) const
75 {
76 	if (m_Doc && (m_Doc->appMode == modeEditTable))
77 		m_activeCell.textFrame()->currentTextProps(parStyle);
78 	else
79 		parStyle = this->itemText.defaultStyle();
80 }
81 
getChildren() const82 QList<PageItem*> PageItem_Table::getChildren() const
83 {
84 	QList<PageItem*> ret;
85 
86 	int numRows = this->rows();
87 	int numColums = this->columns();
88 	for (int row = 0; row < numRows; ++row)
89 	{
90 		for (int col = 0; col < numColums; ++col)
91 		{
92 			TableCell cell = cellAt(row, col);
93 			if (cell.row() == row && cell.column() == col)
94 			{
95 				PageItem* textFrame = cell.textFrame();
96 				ret.append(textFrame);
97 			}
98 		}
99 	}
100 
101 	return ret;
102 }
103 
getNamedResources(ResourceCollection & lists) const104 void PageItem_Table::getNamedResources(ResourceCollection& lists) const
105 {
106 	TableBorder lborder = leftBorder();
107 	for (const TableBorderLine& line : lborder.borderLines())
108 	{
109 		if (line.color() == CommonStrings::None)
110 			continue;
111 		lists.collectColor(line.color());
112 	}
113 
114 	TableBorder rborder = rightBorder();
115 	for (const TableBorderLine& line : rborder.borderLines())
116 	{
117 		if (line.color() == CommonStrings::None)
118 			continue;
119 		lists.collectColor(line.color());
120 	}
121 
122 	TableBorder bborder = bottomBorder();
123 	for (const TableBorderLine& line : bborder.borderLines())
124 	{
125 		if (line.color() == CommonStrings::None)
126 			continue;
127 		lists.collectColor(line.color());
128 	}
129 
130 	TableBorder tborder = topBorder();
131 	for (const TableBorderLine& line : tborder.borderLines())
132 	{
133 		if (line.color() == CommonStrings::None)
134 			continue;
135 		lists.collectColor(line.color());
136 	}
137 
138 	QString tableStyleName = this->styleName();
139 	if (!tableStyleName.isEmpty())
140 		lists.collectTableStyle(tableStyleName);
141 
142 	for (int row = 0; row < rows(); ++row)
143 	{
144 		int colSpan = 0;
145 		for (int col = 0; col < columns(); col += colSpan)
146 		{
147 			TableCell cell = cellAt(row, col);
148 			PageItem_TextFrame* textFrame = cell.textFrame();
149 			textFrame->getNamedResources(lists);
150 
151 			QString cellStyle = cell.styleName();
152 			if (!cellStyle.isEmpty())
153 				lists.collectCellStyle(cellStyle);
154 
155 			QString cellFill = cell.fillColor();
156 			if (cellFill != CommonStrings::None)
157 				lists.collectColor(cellFill);
158 
159 			lborder = cell.leftBorder();
160 			for (const TableBorderLine& line : lborder.borderLines())
161 			{
162 				if (line.color() == CommonStrings::None)
163 					continue;
164 				lists.collectColor(line.color());
165 			}
166 
167 			rborder = cell.rightBorder();
168 			for (const TableBorderLine& line : rborder.borderLines())
169 			{
170 				if (line.color() == CommonStrings::None)
171 					continue;
172 				lists.collectColor(line.color());
173 			}
174 
175 			bborder = cell.bottomBorder();
176 			for (const TableBorderLine& line : bborder.borderLines())
177 			{
178 				if (line.color() == CommonStrings::None)
179 					continue;
180 				lists.collectColor(line.color());
181 			}
182 
183 			tborder = cell.topBorder();
184 			for (const TableBorderLine& line : tborder.borderLines())
185 			{
186 				if (line.color() == CommonStrings::None)
187 					continue;
188 				lists.collectColor(line.color());
189 			}
190 
191 			colSpan = cell.columnSpan();
192 		}
193 	}
194 
195 	PageItem::getNamedResources(lists);
196 }
197 
replaceNamedResources(ResourceCollection & newNames)198 void PageItem_Table::replaceNamedResources(ResourceCollection& newNames)
199 {
200 	QMap<QString, QString>::ConstIterator it;
201 
202 	PageItem::replaceNamedResources(newNames);
203 
204 	bool lborderChanged = false;
205 	TableBorder lborder = leftBorder();
206 	auto lborderLines = lborder.borderLines();
207 	for (int i = 0; i < lborderLines.count(); ++i)
208 	{
209 		TableBorderLine line = lborderLines.at(i);
210 		if (line.color() == CommonStrings::None)
211 			continue;
212 		it = newNames.colors().find(line.color());
213 		if (it != newNames.colors().end())
214 		{
215 			line.setColor(*it);
216 			lborder.replaceBorderLine(i, line);
217 			lborderChanged = true;
218 		}
219 	}
220 
221 	if (lborderChanged)
222 		setLeftBorder(lborder);
223 
224 	bool rborderChanged = false;
225 	TableBorder rborder = rightBorder();
226 	auto rborderLines = rborder.borderLines();
227 	for (int i = 0; i < rborderLines.count(); ++i)
228 	{
229 		TableBorderLine line = rborderLines.at(i);
230 		if (line.color() == CommonStrings::None)
231 			continue;
232 		it = newNames.colors().find(line.color());
233 		if (it != newNames.colors().end())
234 		{
235 			line.setColor(*it);
236 			rborder.replaceBorderLine(i, line);
237 			rborderChanged = true;
238 		}
239 	}
240 
241 	if (rborderChanged)
242 		setRightBorder(rborder);
243 
244 	bool bborderChanged = false;
245 	TableBorder bborder = bottomBorder();
246 	auto bborderLines = bborder.borderLines();
247 	for (int i = 0; i < bborderLines.count(); ++i)
248 	{
249 		TableBorderLine line = bborderLines.at(i);
250 		if (line.color() == CommonStrings::None)
251 			continue;
252 		it = newNames.colors().find(line.color());
253 		if (it != newNames.colors().end())
254 		{
255 			line.setColor(*it);
256 			bborder.replaceBorderLine(i, line);
257 			bborderChanged = true;
258 		}
259 	}
260 
261 	if (bborderChanged)
262 		setBottomBorder(bborder);
263 
264 	bool tborderChanged = false;
265 	TableBorder tborder = topBorder();
266 	auto tborderLines = tborder.borderLines();
267 	for (int i = 0; i < tborderLines.count(); ++i)
268 	{
269 		TableBorderLine line = tborderLines.at(i);
270 		if (line.color() == CommonStrings::None)
271 			continue;
272 		it = newNames.colors().find(line.color());
273 		if (it != newNames.colors().end())
274 		{
275 			line.setColor(*it);
276 			tborder.replaceBorderLine(i, line);
277 			tborderChanged = true;
278 		}
279 	}
280 
281 	if (tborderChanged)
282 		setTopBorder(tborder);
283 
284 	it = newNames.tableStyles().find(this->styleName());
285 	if (it != newNames.tableStyles().end())
286 		this->setStyle(*it);
287 
288 	for (int row = 0; row < rows(); ++row)
289 	{
290 		int colSpan = 0;
291 		for (int col = 0; col < columns(); col += colSpan)
292 		{
293 			TableCell cell = cellAt(row, col);
294 			PageItem_TextFrame* textFrame = cell.textFrame();
295 			textFrame->replaceNamedResources(newNames);
296 
297 			it = newNames.cellStyles().find(cell.styleName());
298 			if (it != newNames.cellStyles().end())
299 				cell.setStyle(*it);
300 
301 			it = newNames.colors().find(cell.fillColor());
302 			if (it != newNames.colors().end())
303 				cell.setFillColor(*it);
304 
305 			lborderChanged = false;
306 			lborder = cell.leftBorder();
307 			lborderLines = lborder.borderLines();
308 			for (int i = 0; i < lborderLines.count(); ++i)
309 			{
310 				TableBorderLine line = lborderLines.at(i);
311 				if (line.color() == CommonStrings::None)
312 					continue;
313 				it = newNames.colors().find(line.color());
314 				if (it != newNames.colors().end())
315 				{
316 					line.setColor(*it);
317 					lborder.replaceBorderLine(i, line);
318 					lborderChanged = true;
319 				}
320 			}
321 
322 			if (lborderChanged)
323 				setLeftBorder(lborder);
324 
325 			rborderChanged = false;
326 			rborder = cell.rightBorder();
327 			rborderLines = rborder.borderLines();
328 			for (int i = 0; i < rborderLines.count(); ++i)
329 			{
330 				TableBorderLine line = rborderLines.at(i);
331 				if (line.color() == CommonStrings::None)
332 					continue;
333 				it = newNames.colors().find(line.color());
334 				if (it != newNames.colors().end())
335 				{
336 					line.setColor(*it);
337 					rborder.replaceBorderLine(i, line);
338 					rborderChanged = true;
339 				}
340 			}
341 
342 			if (rborderChanged)
343 				setRightBorder(rborder);
344 
345 			bborderChanged = false;
346 			bborder = cell.bottomBorder();
347 			bborderLines = bborder.borderLines();
348 			for (int i = 0; i < bborderLines.count(); ++i)
349 			{
350 				TableBorderLine line = bborderLines.at(i);
351 				if (line.color() == CommonStrings::None)
352 					continue;
353 				it = newNames.colors().find(line.color());
354 				if (it != newNames.colors().end())
355 				{
356 					line.setColor(*it);
357 					bborder.replaceBorderLine(i, line);
358 					bborderChanged = true;
359 				}
360 			}
361 
362 			if (bborderChanged)
363 				setBottomBorder(bborder);
364 
365 			tborderChanged = false;
366 			tborder = cell.topBorder();
367 			tborderLines = tborder.borderLines();
368 			for (int i = 0; i < tborderLines.count(); ++i)
369 			{
370 				TableBorderLine line = tborderLines.at(i);
371 				if (line.color() == CommonStrings::None)
372 					continue;
373 				it = newNames.colors().find(line.color());
374 				if (it != newNames.colors().end())
375 				{
376 					line.setColor(*it);
377 					tborder.replaceBorderLine(i, line);
378 					tborderChanged = true;
379 				}
380 			}
381 
382 			if (tborderChanged)
383 				setTopBorder(tborder);
384 
385 			colSpan = cell.columnSpan();
386 		}
387 	}
388 }
389 
layout()390 void PageItem_Table::layout()
391 {
392 	int rowCount = rows();
393 	int columnCount = columns();
394 
395 	for (int row = 0; row < rowCount; ++row)
396 	{
397 		for (int col = 0; col < columnCount; col ++)
398 		{
399 			TableCell cell = cellAt(row, col);
400 			if (cell.row() == row && cell.column() == col)
401 			{
402 				PageItem* textFrame = cell.textFrame();
403 				textFrame->layout();
404 			}
405 		}
406 	}
407 }
408 
setLayer(int newLayerID)409 void PageItem_Table::setLayer(int newLayerID)
410 {
411 	m_layerID = newLayerID;
412 
413 	int rowCount = rows();
414 	int columnCount = columns();
415 
416 	for (int row = 0; row < rowCount; ++row)
417 	{
418 		for (int col = 0; col < columnCount; col++)
419 		{
420 			TableCell cell = cellAt(row, col);
421 			if (cell.row() == row && cell.column() == col)
422 			{
423 				PageItem* textFrame = cell.textFrame();
424 				textFrame->m_layerID = newLayerID;
425 			}
426 		}
427 	}
428 }
429 
setMasterPage(int page,const QString & mpName)430 void PageItem_Table::setMasterPage(int page, const QString& mpName)
431 {
432 	PageItem::setMasterPage(page, mpName);
433 
434 	int rowCount = rows();
435 	int columnCount = columns();
436 
437 	for (int row = 0; row < rowCount; ++row)
438 	{
439 		for (int col = 0; col < columnCount; col++)
440 		{
441 			TableCell cell = cellAt(row, col);
442 			if (cell.row() == row && cell.column() == col)
443 			{
444 				PageItem* textFrame = cell.textFrame();
445 				textFrame->OwnPage = page;
446 				textFrame->OnMasterPage = mpName;
447 			}
448 		}
449 	}
450 }
451 
setMasterPageName(const QString & mpName)452 void PageItem_Table::setMasterPageName(const QString& mpName)
453 {
454 	PageItem::setMasterPageName(mpName);
455 
456 	int rowCount = rows();
457 	int columnCount = columns();
458 
459 	for (int row = 0; row < rowCount; ++row)
460 	{
461 		for (int col = 0; col < columnCount; col++)
462 		{
463 			TableCell cell = cellAt(row, col);
464 			if (cell.row() == row && cell.column() == col)
465 			{
466 				PageItem* textFrame = cell.textFrame();
467 				textFrame->OnMasterPage = mpName;
468 			}
469 		}
470 	}
471 }
472 
setOwnerPage(int page)473 void PageItem_Table::setOwnerPage(int page)
474 {
475 	PageItem::setOwnerPage(page);
476 
477 	int rowCount = rows();
478 	int columnCount = columns();
479 
480 	for (int row = 0; row < rowCount; ++row)
481 	{
482 		for (int col = 0; col < columnCount; col++)
483 		{
484 			TableCell cell = cellAt(row, col);
485 			if (cell.row() == row && cell.column() == col)
486 			{
487 				PageItem* textFrame = cell.textFrame();
488 				textFrame->OwnPage = page;
489 			}
490 		}
491 	}
492 }
493 
resize(double width,double height)494 void PageItem_Table::resize(double width, double height)
495 {
496 	ASSERT_VALID();
497 
498 	/*
499 	 * Distribute width proportionally to columns, but don't let any column width below
500 	 * MinimumColumnWidth.
501 	 */
502 	double requestedWidthFactor = width / tableWidth();
503 	double oldMinWidth = *std::min_element(m_columnWidths.begin(), m_columnWidths.end());
504 	double newMinWidth = qMax(oldMinWidth * requestedWidthFactor, MinimumColumnWidth);
505 	double actualWidthFactor = newMinWidth / oldMinWidth;
506 	for (int col = 0; col < columns(); ++col)
507 	{
508 		m_columnWidths[col] *= actualWidthFactor;
509 		m_columnPositions[col] *= actualWidthFactor;
510 	}
511 
512 	/*
513 	 * Distribute height proportionally to rows, but don't let any row height below
514 	 * MinimumRowHeight.
515 	 */
516 	double requestedHeightFactor = height / tableHeight();
517 	double oldMinHeight = *std::min_element(m_rowHeights.begin(), m_rowHeights.end());
518 	double newMinHeight = qMax(oldMinHeight * requestedHeightFactor, MinimumRowHeight);
519 	double actualHeightFactor = newMinHeight / oldMinHeight;
520 	for (int row = 0; row < rows(); ++row)
521 	{
522 		m_rowHeights[row] *= actualHeightFactor;
523 		m_rowPositions[row] *= actualHeightFactor;
524 	}
525 
526 	// Update cells. TODO: Not for entire table.
527 	updateCells();
528 
529 	emit changed();
530 
531 	ASSERT_VALID();
532 }
533 
insertRows(int index,int numRows)534 void PageItem_Table::insertRows(int index, int numRows)
535 {
536 	ASSERT_VALID();
537 
538 	if (index < 0 || index > rows() || numRows < 1)
539 		return;
540 
541 	double rowHeight = m_rowHeights.at(qMax(index - 1, 0));
542 	double rowPosition = index == 0 ? 0.0 : m_rowPositions.at(index - 1) + rowHeight;
543 
544 	for (int row = index; row < index + numRows; ++row)
545 	{
546 		// Insert row height and position.
547 		m_rowHeights.insert(row, rowHeight);
548 		m_rowPositions.insert(row, rowPosition);
549 		rowPosition += rowHeight;
550 
551 		// Insert a row of cells.
552 		QList<TableCell> cellRow;
553 		cellRow.reserve(columns());
554 		for (int col = 0; col < columns(); ++col)
555 			cellRow.append(TableCell(row, col, this));
556 		m_cellRows.insert(row, cellRow);
557 	}
558 
559 	// Adjust following rows.
560 	double insertedHeight = rowHeight * numRows;
561 	for (int nextRow = index + numRows; nextRow < rows() + numRows; ++nextRow)
562 	{
563 		// Adjust position of following row.
564 		m_rowPositions[nextRow] += insertedHeight;
565 
566 		// "Move" cells in following row down.
567 		foreach (TableCell cell, m_cellRows[nextRow])
568 			cell.moveDown(numRows);
569 	}
570 
571 	// Update row spans.
572 	updateSpans(index, numRows, RowsInserted);
573 
574 	// Increase number of rows.
575 	m_rows += numRows;
576 
577 	// Update cells. TODO: Not for entire table.
578 	updateCells();
579 
580 	emit changed();
581 
582 	ASSERT_VALID();
583 }
584 
removeRows(int index,int numRows)585 void PageItem_Table::removeRows(int index, int numRows)
586 {
587 	ASSERT_VALID();
588 
589 	if (!validRow(index) || numRows < 1 || numRows >= rows() || index + numRows > rows())
590 		return;
591 
592 	// Remove row heights, row positions and rows of cells.
593 	double removedHeight = 0.0;
594 	for (int i = 0; i < numRows; ++i)
595 	{
596 		// Remove row height and position.
597 		removedHeight += m_rowHeights.takeAt(index);
598 		m_rowPositions.removeAt(index);
599 
600 		// Invalidate removed cells.
601 		foreach (TableCell removedCell, m_cellRows[index])
602 			removedCell.setValid(false);
603 
604 		// Remove row of cells.
605 		m_cellRows.removeAt(index);
606 	}
607 
608 	// Adjust following rows.
609 	for (int nextRow = index; nextRow < rows() - numRows; ++nextRow)
610 	{
611 		// Adjust position of following row.
612 		m_rowPositions[nextRow] -= removedHeight;
613 
614 		// "Move" cells in following row up.
615 		foreach (TableCell cell, m_cellRows[nextRow])
616 			cell.moveUp(numRows);
617 	}
618 
619 	// Update row spans.
620 	updateSpans(index, numRows, RowsRemoved);
621 
622 	// Decrease number of rows.
623 	m_rows -= numRows;
624 
625 	// Update cells. TODO: Not for entire table.
626 	updateCells();
627 
628 	// Remove any invalid cells from selection.
629 	QMutableSetIterator<TableCell> cellIt(m_selection);
630 	while (cellIt.hasNext())
631 		if (!cellIt.next().isValid())
632 			cellIt.remove();
633 
634 	// Move to cell below.
635 	moveTo(cellAt(qMin(index + 1, rows() - 1), m_activeColumn));
636 
637 	emit changed();
638 
639 	ASSERT_VALID();
640 }
641 
insertColumns(int index,int numColumns)642 void PageItem_Table::insertColumns(int index, int numColumns)
643 {
644 	ASSERT_VALID();
645 
646 	if (index < 0 || index > columns() || numColumns < 1)
647 		return;
648 
649 	double columnWidth = m_columnWidths.at(qMax(index - 1, 0));
650 	double columnPosition = index == 0 ? 0.0 : m_columnPositions.at(index - 1) + columnWidth;
651 
652 	for (int col = index; col < index + numColumns; ++col)
653 	{
654 		// Insert column width and position.
655 		m_columnWidths.insert(col, columnWidth);
656 		m_columnPositions.insert(col, columnPosition);
657 		columnPosition += columnWidth;
658 
659 		// Insert a column of cells.
660 		for (int row = 0; row < rows(); ++row)
661 			m_cellRows[row].insert(col, TableCell(row, col, this));
662 	}
663 
664 	// Adjust following columns.
665 	double insertedWidth = columnWidth * numColumns;
666 	for (int nextColumn = index + numColumns; nextColumn < columns() + numColumns; ++nextColumn)
667 	{
668 		// Adjust position of following column.
669 		m_columnPositions[nextColumn] += insertedWidth;
670 
671 		// "Move" cells in following column right.
672 		foreach (QList<TableCell> cellRow, m_cellRows)
673 			cellRow[nextColumn].moveRight(numColumns);
674 	}
675 
676 	// Update column spans.
677 	updateSpans(index, numColumns, ColumnsInserted);
678 
679 	// Increase number of columns.
680 	m_columns += numColumns;
681 
682 	// Update cells. TODO: Not for entire table.
683 	updateCells();
684 
685 	emit changed();
686 
687 	ASSERT_VALID();
688 }
689 
removeColumns(int index,int numColumns)690 void PageItem_Table::removeColumns(int index, int numColumns)
691 {
692 	ASSERT_VALID();
693 
694 	if (!validColumn(index) || numColumns < 1 || numColumns >= columns() || index + numColumns > columns())
695 		return;
696 
697 	// Remove column widths, column positions and columns of cells.
698 	double removedWidth = 0.0;
699 	for (int i = 0; i < numColumns; ++i)
700 	{
701 		// Remove columns widths and positions.
702 		removedWidth += m_columnWidths.takeAt(index);
703 		m_columnPositions.removeAt(index);
704 
705 		// Remove and invalidate cells.
706 		QMutableListIterator<QList<TableCell> > rowIt(m_cellRows);
707 		while (rowIt.hasNext())
708 			rowIt.next().takeAt(index).setValid(false);
709 	}
710 
711 	// Adjust following columns.
712 	for (int nextColumn = index; nextColumn < columns() - numColumns; ++nextColumn)
713 	{
714 		// Adjust position of following column.
715 		m_columnPositions[nextColumn] -= removedWidth;
716 
717 		// "Move" cells in following column left.
718 		foreach (QList<TableCell> cellRow, m_cellRows)
719 			cellRow[nextColumn].moveLeft(numColumns);
720 	}
721 
722 	// Update column spans.
723 	updateSpans(index, numColumns, ColumnsRemoved);
724 
725 	// Decrease number of columns.
726 	m_columns -= numColumns;
727 
728 	// Update cells. TODO: Not for entire table.
729 	updateCells();
730 
731 	// Remove any invalid cells from selection.
732 	QMutableSetIterator<TableCell> cellIt(m_selection);
733 	while (cellIt.hasNext())
734 		if (!cellIt.next().isValid())
735 			cellIt.remove();
736 
737 	// Move to cell to the right.
738 	moveTo(cellAt(m_activeRow, qMin(m_activeColumn + 1, columns() - 1)));
739 
740 	emit changed();
741 
742 	ASSERT_VALID();
743 }
744 
rowHeight(int row) const745 double PageItem_Table::rowHeight(int row) const
746 {
747 	if (!validRow(row))
748 		return 0.0;
749 
750 	return m_rowHeights.at(row);
751 }
752 
resizeRow(int row,double height,ResizeStrategy strategy)753 void PageItem_Table::resizeRow(int row, double height, ResizeStrategy strategy)
754 {
755 	ASSERT_VALID();
756 
757 	if (!validRow(row))
758 		return;
759 
760 	if (strategy == MoveFollowing)
761 		resizeRowMoveFollowing(row, height);
762 	else if (strategy == ResizeFollowing)
763 		resizeRowResizeFollowing(row, height);
764 	else
765 		qWarning("Unknown resize strategy!");
766 
767 	// Update cells. TODO: Not for entire table.
768 	updateCells();
769 
770 	emit changed();
771 
772 	ASSERT_VALID();
773 }
774 
distributeRows(int startRow,int endRow)775 void PageItem_Table::distributeRows(int startRow, int endRow)
776 {
777 	if (startRow < 0 || endRow > rows() - 1 || startRow > endRow)
778 		return;
779 
780 	const int numRows = endRow - startRow + 1;
781 	const double newHeight = (rowPosition(endRow) + rowHeight(endRow) - rowPosition(startRow)) / numRows;
782 
783 	for (int row = startRow; row <= endRow; ++row)
784 		resizeRow(row, newHeight);
785 }
786 
distributeColumns(int startColumn,int endColumn)787 void PageItem_Table::distributeColumns(int startColumn, int endColumn)
788 {
789 	if (startColumn < 0 || endColumn > columns() - 1 || startColumn > endColumn)
790 		return;
791 
792 	const int numColumns = endColumn - startColumn + 1;
793 	const double newWidth = (columnPosition(endColumn) + columnWidth(endColumn) - columnPosition(startColumn)) / numColumns;
794 
795 	for (int column = startColumn; column <= endColumn; ++column)
796 		resizeColumn(column, newWidth);
797 }
798 
rowPosition(int row) const799 double PageItem_Table::rowPosition(int row) const
800 {
801 	if (!validRow(row))
802 		return 0.0;
803 
804 	return m_rowPositions.at(row);
805 }
806 
columnWidth(int column) const807 double PageItem_Table::columnWidth(int column) const
808 {
809 	if (!validColumn(column))
810 		return 0.0;
811 
812 	return m_columnWidths.at(column);
813 }
814 
resizeColumn(int column,double width,ResizeStrategy strategy)815 void PageItem_Table::resizeColumn(int column, double width, ResizeStrategy strategy)
816 {
817 	ASSERT_VALID();
818 
819 	if (!validColumn(column))
820 		return;
821 
822 	if (strategy == MoveFollowing)
823 		resizeColumnMoveFollowing(column, width);
824 	else if (strategy == ResizeFollowing)
825 		resizeColumnResizeFollowing(column, width);
826 	else
827 		qWarning("Unknown resize strategy!");
828 
829 	// Update cells. TODO: Not for entire table.
830 	updateCells();
831 
832 	emit changed();
833 
834 	ASSERT_VALID();
835 }
836 
columnPosition(int column) const837 double PageItem_Table::columnPosition(int column) const
838 {
839 	if (!validColumn(column))
840 		return 0.0;
841 
842 	return m_columnPositions.at(column);
843 }
844 
mergeCells(int row,int column,int numRows,int numCols)845 void PageItem_Table::mergeCells(int row, int column, int numRows, int numCols)
846 {
847 	ASSERT_VALID();
848 
849 	if (!validCell(row, column) || !validCell(row + numRows - 1, column + numCols - 1))
850 		return;
851 
852 	CellArea newArea(row, column, numCols, numRows);
853 
854 	// Unite intersecting areas.
855 	QMutableListIterator<CellArea> areaIt(m_cellAreas);
856 	while (areaIt.hasNext())
857 	{
858 		CellArea oldArea = areaIt.next();
859 		if (newArea.intersects(oldArea))
860 		{
861 			// The two areas intersect, so unite them.
862 			newArea = newArea.united(oldArea);
863 
864 			// Reset row/column span of old spanning cell, then remove old area.
865 			TableCell oldSpanningCell = cellAt(oldArea.row(), oldArea.column());
866 			oldSpanningCell.setRowSpan(1);
867 			oldSpanningCell.setColumnSpan(1);
868 			areaIt.remove();
869 		}
870 	}
871 
872 	// Set row/column span of new spanning cell, and add new area.
873 	TableCell newSpanningCell = cellAt(newArea.row(), newArea.column());
874 	newSpanningCell.setRowSpan(newArea.height());
875 	newSpanningCell.setColumnSpan(newArea.width());
876 	m_cellAreas.append(newArea);
877 
878 	// Update cells. TODO: Not for entire table.
879 	updateCells();
880 
881 	// If merged area covers active position, move to the spanning cell.
882 	if (newArea.contains(m_activeRow, m_activeColumn))
883 		moveTo(newSpanningCell);
884 
885 	// Remove all cells covered by the merged area from the selection.
886 	QMutableSetIterator<TableCell> cellIt(m_selection);
887 	while (cellIt.hasNext())
888 	{
889 		TableCell cell = cellIt.next();
890 		if (newArea.contains(cell.row(), cell.column()) &&
891 			!(cell.row() == newArea.row() && cell.column() == newArea.column()))
892 			cellIt.remove();
893 	}
894 
895 	emit changed();
896 
897 	ASSERT_VALID();
898 }
899 
splitCell(int row,int column,int numRows,int numCols)900 void PageItem_Table::splitCell(int row, int column, int numRows, int numCols)
901 {
902 	// Not implemented.
903 	emit changed();
904 }
905 
selectedRows() const906 QSet<int> PageItem_Table::selectedRows() const
907 {
908 	QSet<int> rows;
909 	for (const TableCell& cell : selectedCells())
910 	{
911 		const int startRow = cell.row();
912 		const int endRow = startRow + cell.rowSpan() - 1;
913 		for (int row = startRow; row <= endRow; ++row)
914 			rows.insert(row);
915 	}
916 	return rows;
917 }
918 
selectedColumns() const919 QSet<int> PageItem_Table::selectedColumns() const
920 {
921 	QSet<int> columns;
922 	for (const TableCell& cell : selectedCells())
923 	{
924 		const int startColumn = cell.column();
925 		const int endColumn = startColumn + cell.columnSpan() - 1;
926 		for (int col = startColumn; col <= endColumn; ++col)
927 			columns.insert(col);
928 	}
929 	return columns;
930 }
931 
selectCell(int row,int column)932 void PageItem_Table::selectCell(int row, int column)
933 {
934 	if (!validCell(row, column))
935 		return;
936 
937 	m_selection.insert(cellAt(row, column));
938 	emit selectionChanged();
939 }
940 
selectCells(int startRow,int startColumn,int endRow,int endColumn)941 void PageItem_Table::selectCells(int startRow, int startColumn, int endRow, int endColumn)
942 {
943 	if (!validCell(startRow, startColumn) || !validCell(endRow, endColumn))
944 		return;
945 
946 	const TableCell startCell = cellAt(startRow, startColumn);
947 	const TableCell endCell = cellAt(endRow, endColumn);
948 
949 	const int topRow = qMin(startCell.row(), endCell.row());
950 	const int bottomRow = qMax(startCell.row() + startCell.rowSpan() - 1,
951 		endCell.row() + endCell.rowSpan() - 1);
952 
953 	const int leftCol = qMin(startCell.column(), endCell.column());
954 	const int rightCol = qMax(startCell.column() + startCell.columnSpan() - 1,
955 		endCell.column() + endCell.columnSpan() - 1);
956 
957 	for (int row = topRow; row <= bottomRow; ++row)
958 		for (int col = leftCol; col <= rightCol; ++col)
959 			selectCell(row, col);
960 	emit selectionChanged();
961 }
962 
selectColumn(int column)963 void PageItem_Table::selectColumn(int column)
964 {
965 	if (!validCell(0, column))
966 		return;
967 	selectCells(0, column, rows() - 1, column);
968 }
969 
selectRow(int row)970 void PageItem_Table::selectRow(int row)
971 {
972 	if (!validCell(row, 0))
973 		return;
974 	selectCells(row, 0, row, columns() - 1);
975 }
976 
clearSelection()977 void PageItem_Table::clearSelection()
978 {
979 	m_selection.clear();
980 	emit selectionChanged();
981 }
982 
cellAt(int row,int column) const983 TableCell PageItem_Table::cellAt(int row, int column) const
984 {
985 	if (!validCell(row, column))
986 		return TableCell();
987 
988 	TableCell cell = m_cellRows[row][column];
989 
990 	QList<CellArea>::const_iterator areaIt;
991 	for (areaIt = m_cellAreas.begin(); areaIt != m_cellAreas.end(); ++areaIt)
992 	{
993 		CellArea area = (*areaIt);
994 		if (area.contains(row, column))
995 		{
996 			// Cell was contained in merged area, so use spanning cell.
997 			cell = m_cellRows[area.row()][area.column()];
998 			break;
999 		}
1000 	}
1001 
1002 	return cell;
1003 }
1004 
cellAt(const QPointF & point) const1005 TableCell PageItem_Table::cellAt(const QPointF& point) const
1006 {
1007 	QPointF gridPoint = getTransform().inverted().map(point) - gridOffset();
1008 
1009 	if (!QRectF(0, 0, tableWidth(), tableHeight()).contains(gridPoint))
1010 		return TableCell(); // Outside table grid.
1011 
1012 	return cellAt(
1013 		std::upper_bound(m_rowPositions.begin(), m_rowPositions.end(), gridPoint.y()) - m_rowPositions.begin() - 1,
1014 		std::upper_bound(m_columnPositions.begin(), m_columnPositions.end(), gridPoint.x()) - m_columnPositions.begin() - 1);
1015 }
1016 
moveLeft()1017 void PageItem_Table::moveLeft()
1018 {
1019 	if (m_activeCell.column() < 1)
1020 		return;
1021 
1022 	// Move active position left and activate cell at new position.
1023 //	m_activeColumn = m_activeCell.column() - 1;
1024 	activateCell(cellAt(m_activeRow, m_activeCell.column() - 1));
1025 }
1026 
moveRight()1027 void PageItem_Table::moveRight()
1028 {
1029 	if (m_activeCell.column() + m_activeCell.columnSpan() >= columns())
1030 		return;
1031 
1032 	// Move active position right and activate cell at new position.
1033 //	m_activeColumn = m_activeCell.column() + m_activeCell.columnSpan();
1034 	activateCell(cellAt(m_activeRow, m_activeCell.column() + m_activeCell.columnSpan()));
1035 }
1036 
moveUp()1037 void PageItem_Table::moveUp()
1038 {
1039 	if (m_activeCell.row() < 1)
1040 		return;
1041 
1042 	// Move active position up and activate cell at new position.
1043 //	m_activeRow = m_activeCell.row() - 1;
1044 	activateCell(cellAt(m_activeCell.row() - 1, m_activeColumn));
1045 }
1046 
moveDown()1047 void PageItem_Table::moveDown()
1048 {
1049 	if (m_activeCell.row() + m_activeCell.rowSpan() >= rows())
1050 		return;
1051 	activateCell(cellAt(m_activeCell.row() + m_activeCell.rowSpan(), m_activeColumn));
1052 
1053 	// Deselect previous active cell and its text.
1054 //	m_activeCell.textFrame()->setSelected(false);
1055 //	m_activeCell.textFrame()->itemText.deselectAll();
1056 
1057 	// Move active logical position down.
1058 //	m_activeRow = m_activeCell.row() + m_activeCell.rowSpan();
1059 
1060 	// Set the new active cell and select it.
1061 //	m_activeCell = cellAt(m_activeRow, m_activeColumn);
1062 //	m_activeCell.textFrame()->setSelected(true);
1063 //	m_Doc->currentStyle = m_activeCell.textFrame()->currentStyle();
1064 }
1065 
moveTo(const TableCell & cell)1066 void PageItem_Table::moveTo(const TableCell& cell)
1067 {
1068 	// Activate the cell and move active position to its top left.
1069 	activateCell(cell);
1070 }
1071 
hitTest(const QPointF & point,double threshold) const1072 TableHandle PageItem_Table::hitTest(const QPointF& point, double threshold) const
1073 {
1074 	const QPointF framePoint = getTransform().inverted().map(point);
1075 	const QPointF gridPoint = framePoint - gridOffset();
1076 	const QRectF gridRect = QRectF(0.0, 0.0, tableWidth(), tableHeight());
1077 
1078 	// Test if hit is outside frame.
1079 	if (!QRectF(0.0, 0.0, width(), height()).contains(framePoint))
1080 		return TableHandle(TableHandle::None);
1081 
1082 	// Test if hit is outside table.
1083 	if (!gridRect.adjusted(-threshold, -threshold, threshold, threshold).contains(gridPoint))
1084 		return TableHandle(TableHandle::None);
1085 
1086 	const double tableHeight = this->tableHeight();
1087 	const double tableWidth = this->tableWidth();
1088 	const double x = gridPoint.x();
1089 	const double y = gridPoint.y();
1090 
1091 	// Test if hit is on left edge of table.
1092 	if (x <= threshold)
1093 		return TableHandle(TableHandle::RowSelect);
1094 
1095 	// Test if hit is on top edge of table.
1096 	if (y <= threshold)
1097 		return TableHandle(TableHandle::ColumnSelect);
1098 
1099 	// Test if hit is on bottom right corner of table.
1100 	if (x >= tableWidth - threshold && y >= tableHeight - threshold)
1101 		return TableHandle(TableHandle::TableResize);
1102 
1103 	// Test if hit is on right edge of table.
1104 	if (y >= tableHeight - threshold && y <= tableHeight + threshold)
1105 		return TableHandle(TableHandle::RowResize, rows() - 1);
1106 
1107 	// Test if hit is on bottom edge of table.
1108 	if (x >= tableWidth - threshold && x <= tableWidth + threshold)
1109 		return TableHandle(TableHandle::ColumnResize, columns() - 1);
1110 
1111 	const TableCell hitCell = cellAt(point);
1112 	const QRectF hitRect = hitCell.boundingRect();
1113 
1114 	// Test if hit is on cell interior.
1115 	if (hitRect.adjusted(threshold, threshold, -threshold, -threshold).contains(gridPoint))
1116 		return TableHandle(TableHandle::CellSelect); // Hit interior of cell.
1117 
1118 	const double toLeft = x - hitRect.left();
1119 	const double toRight = hitRect.right() - x;
1120 	const double toTop = y - hitRect.top();
1121 	const double toBottom = hitRect.bottom() - y;
1122 	TableHandle handle(TableHandle::None);
1123 
1124 	// Test which side of the cell was hit.
1125 	if (qMin(toLeft, toRight) < qMin(toTop, toBottom))
1126 	{
1127 		handle.setType(TableHandle::ColumnResize);
1128 		handle.setIndex((toLeft < toRight ? hitCell.column() : hitCell.column() + hitCell.columnSpan()) - 1);
1129 	}
1130 	else
1131 	{
1132 		handle.setType(TableHandle::RowResize);
1133 		handle.setIndex((toTop < toBottom ? hitCell.row() : hitCell.row() + hitCell.rowSpan()) - 1);
1134 	}
1135 	return handle;
1136 }
1137 
adjustTableToFrame()1138 void PageItem_Table::adjustTableToFrame()
1139 {
1140 	resize(width() - (maxLeftBorderWidth() + maxRightBorderWidth()) / 2,
1141 		height() - (maxTopBorderWidth() + maxBottomBorderWidth()) / 2);
1142 }
1143 
adjustFrameToTable()1144 void PageItem_Table::adjustFrameToTable()
1145 {
1146 	if (!m_Doc)
1147 		return;
1148 
1149 	m_Doc->sizeItem(effectiveWidth(), effectiveHeight(), this);
1150 }
1151 
setFillColor(const QString & color)1152 void PageItem_Table::setFillColor(const QString& color)
1153 {
1154 	m_style.setFillColor(color);
1155 	emit changed();
1156 }
1157 
unsetFillColor()1158 void PageItem_Table::unsetFillColor()
1159 {
1160 	m_style.resetFillColor();
1161 	emit changed();
1162 }
1163 
fillColor() const1164 QString PageItem_Table::fillColor() const
1165 {
1166 	return m_style.fillColor();
1167 }
1168 
setFillShade(const double & shade)1169 void PageItem_Table::setFillShade(const double& shade)
1170 {
1171 	m_style.setFillShade(shade);
1172 	emit changed();
1173 }
1174 
unsetFillShade()1175 void PageItem_Table::unsetFillShade()
1176 {
1177 	m_style.resetFillShade();
1178 	emit changed();
1179 }
1180 
fillShade() const1181 double PageItem_Table::fillShade() const
1182 {
1183 	return m_style.fillShade();
1184 }
1185 
setLeftBorder(const TableBorder & border)1186 void PageItem_Table::setLeftBorder(const TableBorder& border)
1187 {
1188 	m_style.setLeftBorder(border);
1189 	updateCells(0, 0, rows() - 1, 0);
1190 	emit changed();
1191 }
1192 
unsetLeftBorder()1193 void PageItem_Table::unsetLeftBorder()
1194 {
1195 	m_style.resetLeftBorder();
1196 	updateCells(0, 0, rows() - 1, 0);
1197 	emit changed();
1198 }
1199 
leftBorder() const1200 TableBorder PageItem_Table::leftBorder() const
1201 {
1202 	return m_style.leftBorder();
1203 }
1204 
setRightBorder(const TableBorder & border)1205 void PageItem_Table::setRightBorder(const TableBorder& border)
1206 {
1207 	m_style.setRightBorder(border);
1208 	updateCells(0, columns() - 1, rows() - 1, columns() - 1);
1209 	emit changed();
1210 }
1211 
unsetRightBorder()1212 void PageItem_Table::unsetRightBorder()
1213 {
1214 	m_style.resetRightBorder();
1215 	updateCells(0, columns() - 1, rows() - 1, columns() - 1);
1216 	emit changed();
1217 }
1218 
rightBorder() const1219 TableBorder PageItem_Table::rightBorder() const
1220 {
1221 	return m_style.rightBorder();
1222 }
1223 
setTopBorder(const TableBorder & border)1224 void PageItem_Table::setTopBorder(const TableBorder& border)
1225 {
1226 	m_style.setTopBorder(border);
1227 	updateCells(0, 0, 0, columns() - 1);
1228 	emit changed();
1229 }
1230 
unsetTopBorder()1231 void PageItem_Table::unsetTopBorder()
1232 {
1233 	m_style.resetTopBorder();
1234 	updateCells(0, 0, 0, columns() - 1);
1235 	emit changed();
1236 }
1237 
topBorder() const1238 TableBorder PageItem_Table::topBorder() const
1239 {
1240 	return m_style.topBorder();
1241 }
1242 
setBottomBorder(const TableBorder & border)1243 void PageItem_Table::setBottomBorder(const TableBorder& border)
1244 {
1245 	m_style.setBottomBorder(border);
1246 	updateCells(rows() - 1, 0, rows() - 1, columns() - 1);
1247 	emit changed();
1248 }
1249 
unsetBottomBorder()1250 void PageItem_Table::unsetBottomBorder()
1251 {
1252 	m_style.resetBottomBorder();
1253 	updateCells(rows() - 1, 0, rows() - 1, columns() - 1);
1254 	emit changed();
1255 }
1256 
bottomBorder() const1257 TableBorder PageItem_Table::bottomBorder() const
1258 {
1259 	return m_style.bottomBorder();
1260 }
1261 
setStyle(const QString & style)1262 void PageItem_Table::setStyle(const QString& style)
1263 {
1264 	doc()->dontResize = true;
1265 	m_style.setParent(style);
1266 	updateCells();
1267 	doc()->dontResize = false;
1268 	emit changed();
1269 }
1270 
unsetStyle()1271 void PageItem_Table::unsetStyle()
1272 {
1273 	doc()->dontResize = true;
1274 	m_style.setParent("");
1275 	updateCells();
1276 	doc()->dontResize = false;
1277 	emit changed();
1278 }
1279 
unsetDirectFormatting()1280 void PageItem_Table::unsetDirectFormatting()
1281 {
1282 	doc()->dontResize = true;
1283 	QString parentStyle = m_style.parent();
1284 	m_style.setParent("");
1285 	m_style.erase();
1286 	m_style.setParent(parentStyle);
1287 	adjustTableToFrame();
1288 	adjustFrameToTable();
1289 	updateCells();
1290 	doc()->dontResize = false;
1291 	emit changed();
1292 }
1293 
style() const1294 const TableStyle& PageItem_Table::style() const
1295 {
1296 	return m_style;
1297 }
1298 
styleName() const1299 QString PageItem_Table::styleName() const
1300 {
1301 	return m_style.parent();
1302 }
1303 
handleStyleChanged()1304 void PageItem_Table::handleStyleChanged()
1305 {
1306 	doc()->dontResize = true;
1307 	updateCells();
1308 	doc()->dontResize = false;
1309 }
1310 
applicableActions(QStringList & actionList)1311 void PageItem_Table::applicableActions(QStringList& actionList)
1312 {
1313 	const bool tableEdit = m_Doc->appMode == modeEditTable;
1314 	const int tableRows = rows();
1315 	const int tableColumns = columns();
1316 	const int selectedRows = tableEdit ? this->selectedRows().size() : 0;
1317 	const int selectedColumns = tableEdit ? this->selectedColumns().size() : 0;
1318 	const int selectedCells = tableEdit ? this->selectedCells().size() : 0;
1319 
1320 	if (!tableEdit || selectedCells<1)
1321 		actionList << "tableInsertRows";
1322 	if (!tableEdit || selectedCells<1)
1323 		actionList << "tableInsertColumns";
1324 	if (tableEdit && ((selectedRows < 1 && tableRows > 1) || (selectedRows > 0 && selectedRows < tableRows)))
1325 		actionList << "tableDeleteRows";
1326 	if ((selectedColumns < 1 && tableColumns > 1) || (selectedColumns > 0 && selectedColumns < tableColumns))
1327 		actionList << "tableDeleteColumns";
1328 	if (selectedCells > 1)
1329 		actionList << "tableMergeCells";
1330 	if (tableEdit)
1331 		actionList << "tableSetRowHeights";
1332 	if (tableEdit)
1333 		actionList << "tableSetColumnWidths";
1334 	if (!tableEdit || (tableEdit && selectedRows > 1))
1335 		actionList << "tableDistributeRowsEvenly";
1336 	if (!tableEdit || (tableEdit && selectedColumns > 1))
1337 		actionList << "tableDistributeColumnsEvenly";
1338 	actionList << "tableAdjustFrameToTable";
1339 	actionList << "tableAdjustTableToFrame";
1340 }
1341 
DrawObj_Item(ScPainter * p,QRectF)1342 void PageItem_Table::DrawObj_Item(ScPainter *p, QRectF /*e*/)
1343 {
1344 	if (m_Doc->RePos)
1345 		return;
1346 
1347 	p->save();
1348 
1349 	// Set the clip path.
1350 	p->setupPolygon(&PoLine);
1351 	p->setClipPath();
1352 
1353 	// Paint the table.
1354 	m_tablePainter->paintTable(p);
1355 
1356 	p->restore();
1357 
1358 	// Paint the overflow marker.
1359 	if (isOverflowing())
1360 		drawOverflowMarker(p);
1361 }
1362 
initialize(int numRows,int numColumns)1363 void PageItem_Table::initialize(int numRows, int numColumns)
1364 {
1365 	Q_ASSERT(m_Doc);
1366 	Q_ASSERT(numRows > 0);
1367 	Q_ASSERT(numColumns > 0);
1368 
1369 	// Internal style is in document-wide style context.
1370 	m_style.setContext(&m_Doc->tableStyles());
1371 
1372 	// Reserve space in lists.
1373 	m_cellRows.reserve(numRows);
1374 	m_rowHeights.reserve(numRows);
1375 	m_rowPositions.reserve(numRows);
1376 	m_columnWidths.reserve(numColumns);
1377 	m_columnPositions.reserve(numColumns);
1378 
1379 	// Initialize rows of cells.
1380 	QList<TableCell> initialRow;
1381 	initialRow.append(TableCell(0, 0, this));
1382 	m_cellRows.append(initialRow);
1383 
1384 	// Initialize row/column geometries.
1385 	m_rowPositions.insert(0, 0.0);
1386 	m_rowHeights.insert(0, 10.0);
1387 	m_columnPositions.insert(0, 0.0);
1388 	m_columnWidths.insert(0, 10.0);
1389 
1390 	// Initialize row/column counts.
1391 	m_rows = 1;
1392 	m_columns = 1;
1393 
1394 	// Insert any remaining rows and/or columns.
1395 	insertRows(0, numRows - 1);
1396 	insertColumns(0, numColumns - 1);
1397 
1398 	// Listen to changes in the document-wide cell/table style contexts.
1399 	m_Doc->tableStyles().connect(this, SLOT(handleStyleChanged()));
1400 	m_Doc->cellStyles().connect(this, SLOT(handleStyleChanged()));
1401 
1402 	m_activeCell = cellAt(0, 0);
1403 	m_activeRow = 0;
1404 	m_activeColumn = 0;
1405 }
1406 
activateCell(const TableCell & cell)1407 void PageItem_Table::activateCell(const TableCell& cell)
1408 {
1409 	ASSERT_VALID();
1410 
1411 	TableCell newActiveCell = validCell(cell.row(), cell.column()) ? cell : cellAt(0, 0);
1412 
1413 	// Deselect previous active cell and its text.
1414 	PageItem_TextFrame* textFrame = m_activeCell.textFrame();
1415 	textFrame->setSelected(false);
1416 	textFrame->itemText.deselectAll();
1417 	textFrame->HasSel = false;
1418 
1419 	// Set current style context befor assigning new active cell:
1420 	// if old active cell ref count is 1, the old context might be deleted
1421 	const ParagraphStyle& curStyle = newActiveCell.textFrame()->currentStyle();
1422 	m_Doc->currentStyle.setContext(curStyle.context());
1423 	m_Doc->currentStyle = newActiveCell.textFrame()->currentStyle();
1424 
1425 	// Set the new active cell and select it.
1426 	m_activeCell = newActiveCell;
1427 	m_activeCell.textFrame()->setSelected(true);
1428 	m_activeRow = m_activeCell.row();
1429 	m_activeColumn = m_activeCell.column();
1430 	emit selectionChanged();
1431 
1432 	ASSERT_VALID();
1433 }
1434 
maxLeftBorderWidth() const1435 double PageItem_Table::maxLeftBorderWidth() const
1436 {
1437 	double maxWidth = 0.0;
1438 	TableCell cell;
1439 	for (int row = 0; row < rows(); row += cell.rowSpan())
1440 	{
1441 		cell = cellAt(row, 0);
1442 		maxWidth = qMax(maxWidth, TableUtils::collapseBorders(cell.leftBorder(), leftBorder()).width());
1443 	}
1444 	return maxWidth;
1445 }
1446 
maxRightBorderWidth() const1447 double PageItem_Table::maxRightBorderWidth() const
1448 {
1449 	double maxWidth = 0.0;
1450 	TableCell cell;
1451 	for (int row = 0; row < rows(); row += cell.rowSpan())
1452 	{
1453 		cell = cellAt(row, columns() - 1);
1454 		maxWidth = qMax(maxWidth, TableUtils::collapseBorders(leftBorder(), cell.rightBorder()).width());
1455 	}
1456 	return maxWidth;
1457 }
1458 
maxTopBorderWidth() const1459 double PageItem_Table::maxTopBorderWidth() const
1460 {
1461 	double maxWidth = 0.0;
1462 	TableCell cell;
1463 	for (int col = 0; col < columns(); col += cell.columnSpan())
1464 	{
1465 		cell = cellAt(0, col);
1466 		maxWidth = qMax(maxWidth, TableUtils::collapseBorders(cell.topBorder(), topBorder()).width());
1467 	}
1468 	return maxWidth;
1469 }
1470 
maxBottomBorderWidth() const1471 double PageItem_Table::maxBottomBorderWidth() const
1472 {
1473 	double maxWidth = 0.0;
1474 	TableCell cell;
1475 	for (int col = 0; col < columns(); col += cell.columnSpan())
1476 	{
1477 		cell = cellAt(rows() - 1, col);
1478 		maxWidth = qMax(maxWidth, TableUtils::collapseBorders(bottomBorder(), cell.bottomBorder()).width());
1479 	}
1480 	return maxWidth;
1481 }
1482 
resizeRowMoveFollowing(int row,double height)1483 double PageItem_Table::resizeRowMoveFollowing(int row, double height)
1484 {
1485 	// Set row height.
1486 	double newHeight = m_rowHeights[row] = qMax(MinimumRowHeight, height);
1487 
1488 	// Move following rows.
1489 	double rowPosition = m_rowPositions[row];
1490 	for (int nextRow = row; nextRow < m_rowPositions.size(); ++nextRow)
1491 	{
1492 		m_rowPositions[nextRow] = rowPosition;
1493 		rowPosition += m_rowHeights[nextRow];
1494 	}
1495 
1496 	return newHeight;
1497 }
1498 
resizeRowResizeFollowing(int row,double height)1499 double PageItem_Table::resizeRowResizeFollowing(int row, double height)
1500 {
1501 	double oldHeight = m_rowHeights[row];
1502 	double newHeight = oldHeight;
1503 
1504 	if (row < rows() - 1)
1505 	{
1506 		// Following row exists, so height is bounded at both ends.
1507 		newHeight = m_rowHeights[row] = qBound(
1508 			PageItem_Table::MinimumRowHeight, height,
1509 			oldHeight + m_rowHeights[row + 1] - MinimumRowHeight);
1510 
1511 		// Resize/move following row.
1512 		double heightChange = newHeight - oldHeight;
1513 		m_rowPositions[row + 1] += heightChange;
1514 		m_rowHeights[row + 1] -= heightChange;
1515 	}
1516 	else
1517 	{
1518 		// Last row, so height only bounded by MinimumRowHeight.
1519 		newHeight = m_rowHeights[row] = qMax(MinimumRowHeight, height);
1520 	}
1521 
1522 	return newHeight;
1523 }
1524 
resizeColumnMoveFollowing(int column,double width)1525 double PageItem_Table::resizeColumnMoveFollowing(int column, double width)
1526 {
1527 	// Set column width.
1528 	double newWidth = m_columnWidths[column] = qMax(MinimumColumnWidth, width);
1529 
1530 	// Move following columns.
1531 	double columnPosition = m_columnPositions[column];
1532 	for (int nextColumn = column; nextColumn < m_columnPositions.size(); ++nextColumn)
1533 	{
1534 		m_columnPositions[nextColumn] = columnPosition;
1535 		columnPosition += m_columnWidths[nextColumn];
1536 	}
1537 
1538 	return newWidth;
1539 }
1540 
resizeColumnResizeFollowing(int column,double width)1541 double PageItem_Table::resizeColumnResizeFollowing(int column, double width)
1542 {
1543 	double oldWidth = m_columnWidths[column];
1544 	double newWidth = oldWidth;
1545 
1546 	if (column < columns() - 1)
1547 	{
1548 		// Following column exists, so width is bounded at both ends.
1549 		newWidth = m_columnWidths[column] = qBound(
1550 			PageItem_Table::MinimumColumnWidth, width,
1551 			oldWidth + m_columnWidths[column + 1] - MinimumColumnWidth);
1552 
1553 		// Resize/move following column.
1554 		double widthChange = newWidth - oldWidth;
1555 		m_columnPositions[column + 1] += widthChange;
1556 		m_columnWidths[column + 1] -= widthChange;
1557 	}
1558 	else
1559 	{
1560 		// Last column, so width only bounded by MinimumColumnWidth.
1561 		newWidth = m_columnWidths[column] = qMax(MinimumColumnWidth, width);
1562 	}
1563 
1564 	return newWidth;
1565 }
1566 
updateCells(int startRow,int startColumn,int endRow,int endColumn)1567 void PageItem_Table::updateCells(int startRow, int startColumn, int endRow, int endColumn)
1568 {
1569 	if (startRow > endRow || startColumn > endColumn)
1570 		return; // Invalid area.
1571 
1572 	if (!validCell(startRow, startColumn) || !validCell(endRow, endColumn))
1573 		return; // Invalid area.
1574 
1575 	foreach (const QList<TableCell>& cellRow, m_cellRows)
1576 		foreach (TableCell cell, cellRow)
1577 			cell.updateContent();
1578 }
1579 
updateSpans(int index,int number,ChangeType changeType)1580 void PageItem_Table::updateSpans(int index, int number, ChangeType changeType)
1581 {
1582 	// Loop through areas of merged cells.
1583 	QMutableListIterator<CellArea> areaIt(m_cellAreas);
1584 	while (areaIt.hasNext())
1585 	{
1586 		CellArea oldArea = areaIt.next();
1587 
1588 		// Get a copy of the area adjusted to the change.
1589 		CellArea newArea;
1590 		switch (changeType)
1591 		{
1592 			case RowsInserted:
1593 				newArea = oldArea.adjustedForRowInsertion(index, number);
1594 				break;
1595 			case RowsRemoved:
1596 				newArea = oldArea.adjustedForRowRemoval(index, number);
1597 				break;
1598 			case ColumnsInserted:
1599 				newArea = oldArea.adjustedForColumnInsertion(index, number);
1600 				break;
1601 			case ColumnsRemoved:
1602 				newArea = oldArea.adjustedForColumnRemoval(index, number);
1603 				break;
1604 			default:
1605 				break;
1606 		}
1607 
1608 		// Check if the area was affected by the change.
1609 		if (newArea != oldArea)
1610 		{
1611 			if (newArea.height() < 1 || newArea.width() < 1)
1612 			{
1613 				// Adjusted area was annihilated, so remove it.
1614 				areaIt.remove();
1615 			}
1616 			else if (newArea.height() == 1 && newArea.width() == 1)
1617 			{
1618 				// Adjusted area is 1x1, so remove it.
1619 				areaIt.remove();
1620 
1621 				// And reset row/column span of spanning cell to 1.
1622 				TableCell oldSpanningCell = cellAt(oldArea.row(), oldArea.column());
1623 				oldSpanningCell.setRowSpan(1);
1624 				oldSpanningCell.setColumnSpan(1);
1625 			}
1626 			else
1627 			{
1628 				// Replace the area with the adjusted copy.
1629 				areaIt.setValue(newArea);
1630 
1631 				// And set row/column spanning of spanning cell.
1632 				TableCell newSpanningCell = cellAt(newArea.row(), newArea.column());
1633 				newSpanningCell.setRowSpan(newArea.height());
1634 				newSpanningCell.setColumnSpan(newArea.width());
1635 			}
1636 		}
1637 	}
1638 }
1639 
debug() const1640 void PageItem_Table::debug() const
1641 {
1642 	qDebug() << "-------------------------------------------------";
1643 	qDebug() << "Table Debug";
1644 	qDebug() << "-------------------------------------------------";
1645 	qDebug() << "m_rows: " <<  m_rows;
1646 	qDebug() << "m_columns: " <<  m_columns;
1647 	qDebug() << "m_columnPositions: " <<  m_columnPositions;
1648 	qDebug() << "m_columnWidths: " <<  m_columnWidths;
1649 	qDebug() << "m_rowPositions: " <<  m_rowPositions;
1650 	qDebug() << "m_rowHeights: " <<  m_rowHeights;
1651 	qDebug() << "m_cellAreas: " <<  m_cellAreas;
1652 	qDebug() << "m_cellRows: ";
1653 	for (const QList<TableCell>& cellRow : m_cellRows)
1654 		for (const TableCell& cell : cellRow)
1655 			qDebug() << cell.asString();
1656 	qDebug() << "-------------------------------------------------";
1657 }
1658 
assertValid() const1659 void PageItem_Table::assertValid() const
1660 {
1661 	// Check list sizes.
1662 	Q_ASSERT(rows() == m_rowPositions.size());
1663 	Q_ASSERT(rows() == m_rowHeights.size());
1664 	Q_ASSERT(columns() == m_columnPositions.size());
1665 	Q_ASSERT(columns() == m_columnWidths.size());
1666 	Q_ASSERT(rows() == m_cellRows.size());
1667 	for (const QList<TableCell>& cellRow : m_cellRows)
1668 		Q_ASSERT(columns() == cellRow.size());
1669 
1670 	for (int row = 0; row < rows(); ++row)
1671 	{
1672 		for (int col = 0; col < columns(); ++col)
1673 		{
1674 			TableCell cell = m_cellRows[row][col];
1675 
1676 			// Check that the cell reports correct row and column.
1677 			Q_ASSERT(cell.row() == row);
1678 			Q_ASSERT(cell.column() == col);
1679 
1680 			// Check that the row and column span is sane.
1681 			Q_ASSERT(cell.rowSpan() >= 1 && cell.columnSpan() >= 1);
1682 
1683 			if (cell.rowSpan() > 1 || cell.columnSpan() > 1)
1684 			{
1685 				// Check that there's exactly one matching cell area.
1686 				CellArea expectedArea(cell.row(), cell.column(), cell.columnSpan(), cell.rowSpan());
1687 				Q_ASSERT(m_cellAreas.count(expectedArea) == 1);
1688 			}
1689 		}
1690 	}
1691 
1692 	// Check that the active position is in this table.
1693 	Q_ASSERT(validCell(m_activeRow, m_activeColumn));
1694 
1695 	// Check that the active cell is valid.
1696 	Q_ASSERT(m_activeCell.isValid());
1697 	Q_ASSERT(validCell(m_activeCell.row(), m_activeCell.column()));
1698 
1699 	// Check that selected cells are valid.
1700 	for (const TableCell& cell : m_selection)
1701 	{
1702 		Q_ASSERT(cell.isValid());
1703 		Q_ASSERT(validCell(cell.row(), cell.column()));
1704 	}
1705 
1706 	for (const CellArea& cellArea : m_cellAreas)
1707 	{
1708 		// Check that the active cell is not covered.
1709 		if (cellArea.contains(m_activeCell.row(), m_activeCell.column()))
1710 			Q_ASSERT(m_activeCell.row() == cellArea.row() && m_activeCell.column() == cellArea.column());
1711 
1712 		// Check that the selected cells are not covered.
1713 		for (const TableCell& cell : m_selection)
1714 			if (cellArea.contains(cell.row(), cell.column()))
1715 				Q_ASSERT(cell.row() == cellArea.row() && cell.column() == cellArea.column());
1716 	}
1717 }
1718