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 <QColor>
10 #include <QLineF>
11 #include <QRectF>
12 
13 #include "scpainter.h"
14 #include "tablecell.h"
15 #include "pageitem_table.h"
16 #include "prefsmanager.h"
17 #include "scribusdoc.h"
18 #include "tableborder.h"
19 #include "tableutils.h"
20 
21 #include "collapsedtablepainter.h"
22 
23 using namespace TableUtils;
24 
paintTable(ScPainter * p)25 void CollapsedTablePainter::paintTable(ScPainter* p)
26 {
27 	p->save();
28 	p->translate(table()->gridOffset());
29 
30 	// Paint table fill.
31 	paintTableFill(p);
32 
33 	/*
34 	 * We paint the table in five passes:
35 	 *
36 	 * 1) Cell fills.
37 	 * 2) Vertical borders.
38 	 * 3) Horizontal borders
39 	 * 4) Decorative grid lines.
40 	 * 5) Cell content.
41 	 */
42 
43 	// Pass 1: Paint cell fills.
44 	for (int row = 0; row < table()->rows(); ++row)
45 	{
46 		int colSpan = 0;
47 		for (int col = 0; col < table()->columns(); col += colSpan)
48 		{
49 			TableCell cell = table()->cellAt(row, col);
50 			if (row == cell.row())
51 				paintCellFill(cell, p);
52 			colSpan = cell.columnSpan();
53 		}
54 	}
55 
56 	// Pass 2: Paint vertical borders.
57 	for (int row = 0; row < table()->rows(); ++row)
58 	{
59 		int colSpan = 0;
60 		for (int col = 0; col < table()->columns(); col += colSpan)
61 		{
62 			TableCell cell = table()->cellAt(row, col);
63 			if (row == cell.row())
64 			{
65 				paintCellRightBorders(cell, p);
66 				if (col == 0)
67 					paintCellLeftBorders(cell, p);
68 			}
69 			colSpan = cell.columnSpan();
70 		}
71 	}
72 
73 	// Pass 3: Paint horizontal borders.
74 	for (int row = 0; row < table()->rows(); ++row)
75 	{
76 		int colSpan = 0;
77 		for (int col = 0; col < table()->columns(); col += colSpan)
78 		{
79 			TableCell cell = table()->cellAt(row, col);
80 			if (row == cell.row())
81 			{
82 				paintCellBottomBorders(cell, p);
83 				if (row == 0)
84 					paintCellTopBorders(cell, p);
85 			}
86 			colSpan = cell.columnSpan();
87 		}
88 	}
89 
90 	// Pass 4: Paint grid lines.
91 	if (table()->m_Doc->guidesPrefs().framesShown)
92 	{
93 		for (int row = 0; row < table()->rows(); ++row)
94 		{
95 			int colSpan = 0;
96 			for (int col = 0; col < table()->columns(); col += colSpan)
97 			{
98 				TableCell cell = table()->cellAt(row, col);
99 				if (row == cell.row())
100 				{
101 					int endCol = col + cell.columnSpan() - 1;
102 					int endRow = row + cell.rowSpan() - 1;
103 					double left = table()->columnPosition(col);
104 					double right = table()->columnPosition(endCol) + table()->columnWidth(endCol);
105 					double top = table()->rowPosition(row);
106 					double bottom = table()->rowPosition(endRow) + table()->rowHeight(endRow);
107 					// Paint right and bottom grid line.
108 					paintGridLine(QPointF(right, top), QPointF(right, bottom), p);
109 					paintGridLine(QPointF(left, bottom), QPointF(right, bottom), p);
110 					// Paint left and top grid line.
111 					if (col == 0)
112 						paintGridLine(QPointF(left, top), QPointF(left, bottom), p);
113 					if (row == 0)
114 						paintGridLine(QPointF(left, top), QPointF(right, top), p);
115 				}
116 				colSpan = cell.columnSpan();
117 			}
118 		}
119 	}
120 
121 	// Pass 5: Paint cell content.
122 	for (int row = 0; row < table()->rows(); ++row)
123 	{
124 		for (int col = 0; col < table()->columns(); col ++)
125 		{
126 			TableCell cell = table()->cellAt(row, col);
127 			if (cell.row() == row && cell.column() == col)
128 			{
129 				PageItem* textFrame = cell.textFrame();
130 				textFrame->DrawObj(p, QRectF());
131 				textFrame->DrawObj_Decoration(p);
132 			}
133 		}
134 	}
135 
136 	p->restore();
137 }
138 
paintTableFill(ScPainter * p) const139 void CollapsedTablePainter::paintTableFill(ScPainter* p) const
140 {
141 	QString colorName = table()->fillColor();
142 
143 	if (colorName == CommonStrings::None)
144 		return;
145 
146 	p->save();
147 
148 	int lastCol = table()->columns() - 1;
149 	int lastRow = table()->rows() - 1;
150 
151 	double x = table()->columnPosition(0);
152 	double y = table()->rowPosition(0);
153 	double width = table()->columnPosition(lastCol) + table()->columnWidth(lastCol) - x;
154 	double height = table()->rowPosition(lastRow) + table()->rowHeight(lastRow) - y;
155 
156 	QColor color;
157 	table()->SetQColor(&color, colorName, table()->fillShade());
158 	p->setBrush(color);
159 	p->setFillMode(ScPainter::Solid);
160 	p->setStrokeMode(ScPainter::None);
161 	p->drawRect(x, y, width, height);
162 
163 	p->restore();
164 }
165 
paintCellLeftBorders(const TableCell & cell,ScPainter * p) const166 void CollapsedTablePainter::paintCellLeftBorders(const TableCell& cell, ScPainter* p) const
167 {
168 	/*
169 	 * We are going to paint the border marked # in the following setup.
170 	 *
171 	 *       +----------------------+----------------------+
172 	 *       |                      |                      |
173 	 *       |                      |                      |
174 	 *       |    topLeftCell      top     topRightCell    |
175 	 *       |                      |                      |
176 	 *       |                      |                      |
177 	 *       +--------topLeft-------+-------topRight-------+
178 	 *       |                      #                      |
179 	 *       |                      #                      |
180 	 *       |      leftCell     border        cell        |
181 	 *       |                      #                      |
182 	 *       |                      #                      |
183 	 *       +-------bottomLeft-----+------bottomRight-----+
184 	 *       |                      |                      |
185 	 *       |                      |                      |
186 	 *       |  bottomLeftCell   bottom   bottomRightCell  |
187 	 *       |                      |                      |
188 	 *       |                      |                      |
189 	 *       +----------------------+----------------------+
190 	 */
191 
192 	// The cell ends in this row.
193 	const int lastRow = cell.row() + cell.rowSpan() - 1;
194 	// The cell starts in this column.
195 	const int firstCol = cell.column();
196 	// The X position of the border segments to paint.
197 	const double borderX = table()->columnPosition(firstCol);
198 
199 	// The start point of the border segment currently being painted.
200 	QPointF start(borderX, 0.0);
201 	// The end point of the border segment currently being painted.
202 	QPointF end(borderX, 0.0);
203 	// The start and end offset factors for the border segment currently being painted.
204 	QPointF startOffsetFactors, endOffsetFactors;
205 	// Start and end row of border segment currently being painted.
206 	int startRow, endRow;
207 
208 	for (int row = cell.row(); row <= lastRow; row += endRow - startRow + 1)
209 	{
210 		// Get the neighboring cell to the left and determine border segment row interval.
211 		TableCell leftCell = table()->cellAt(row, firstCol - 1);
212 		startRow = qMax(cell.row(), leftCell.row());
213 		endRow = qMin(lastRow, leftCell.isValid() ? leftCell.row() + leftCell.rowSpan() - 1 : lastRow);
214 
215 		// Get the remaining neighboring cells.
216 		TableCell topLeftCell = table()->cellAt(startRow - 1, firstCol - 1);
217 		TableCell topRightCell = table()->cellAt(startRow - 1, firstCol);
218 		TableCell bottomRightCell = table()->cellAt(lastRow + 1, firstCol);
219 		TableCell bottomLeftCell = table()->cellAt(lastRow + 1, firstCol - 1);
220 
221 		// Resolve borders between neighboring cells.
222 		TableBorder topLeft, top, topRight, border, bottomLeft, bottom, bottomRight;
223 		resolveBordersVertical(topLeftCell, topRightCell, leftCell, cell, bottomLeftCell, bottomRightCell, &topLeft, &top, &topRight, &border, &bottomLeft, &bottom, &bottomRight, table());
224 
225 		if (border.isNull())
226 			continue; // Quit early if the border to paint is null.
227 
228 		// Set initial coordinates.
229 		start.setY(table()->rowPosition(startRow));
230 		end.setY(table()->rowPosition(endRow) + table()->rowHeight(endRow));
231 
232 		// Adjust coordinates for joining.
233 		joinVertical(border, topLeft, top, topRight, bottomLeft, bottom, bottomRight,
234 			 &start, &end, &startOffsetFactors, &endOffsetFactors);
235 
236 		// Paint the border segment.
237 		paintBorder(border, start, end, startOffsetFactors, endOffsetFactors, p);
238 	}
239 }
240 
paintCellRightBorders(const TableCell & cell,ScPainter * p) const241 void CollapsedTablePainter::paintCellRightBorders(const TableCell& cell, ScPainter* p) const
242 {
243 	/*
244 	 * We are going to paint the border marked # in the following setup.
245 	 *
246 	 *       +----------------------+----------------------+
247 	 *       |                      |                      |
248 	 *       |                      |                      |
249 	 *       |    topLeftCell      top     topRightCell    |
250 	 *       |                      |                      |
251 	 *       |                      |                      |
252 	 *       +--------topLeft-------+-------topRight-------+
253 	 *       |                      #                      |
254 	 *       |                      #                      |
255 	 *       |         cell      border      rightCell     |
256 	 *       |                      #                      |
257 	 *       |                      #                      |
258 	 *       +-------bottomLeft-----+------bottomRight-----+
259 	 *       |                      |                      |
260 	 *       |                      |                      |
261 	 *       |  bottomLeftCell   bottom   bottomRightCell  |
262 	 *       |                      |                      |
263 	 *       |                      |                      |
264 	 *       +----------------------+----------------------+
265 	 */
266 
267 	// The cell ends in this row.
268 	const int lastRow = cell.row() + cell.rowSpan() - 1;
269 	// The cell ends in this column.
270 	const int lastCol = cell.column() + cell.columnSpan() - 1;
271 	// The X position of the border segments to paint.
272 	const double borderX = table()->columnPosition(lastCol) + table()->columnWidth(lastCol);
273 
274 	// The start point of the border segment currently being painted.
275 	QPointF start(borderX, 0.0);
276 	// The end point of the border segment currently being painted.
277 	QPointF end(borderX, 0.0);
278 	// The start and end offset factors for the border segment currently being painted.
279 	QPointF startOffsetFactors, endOffsetFactors;
280 	// The start and end row of border segment currently being painted.
281 	int startRow, endRow;
282 
283 	for (int row = cell.row(); row <= lastRow; row += endRow - startRow + 1)
284 	{
285 		// Get the neighboring cell to the right and determine border segment row interval.
286 		TableCell rightCell = table()->cellAt(row, lastCol + 1);
287 		startRow = qMax(cell.row(), rightCell.row());
288 		endRow = qMin(lastRow, rightCell.isValid() ? rightCell.row() + rightCell.rowSpan() - 1 : lastRow);
289 
290 		// Get the remaining neighboring cells surrounding the segment.
291 		TableCell topLeftCell = table()->cellAt(startRow - 1, lastCol);
292 		TableCell topRightCell = table()->cellAt(startRow - 1, lastCol + 1);
293 		TableCell bottomRightCell = table()->cellAt(endRow + 1, lastCol + 1);
294 		TableCell bottomLeftCell = table()->cellAt(endRow + 1, lastCol);
295 
296 		// Resolve borders between neighboring cells.
297 		TableBorder topLeft, top, topRight, border, bottomLeft, bottom, bottomRight;
298 		resolveBordersVertical(topLeftCell, topRightCell, cell, rightCell, bottomLeftCell, bottomRightCell, &topLeft, &top, &topRight, &border, &bottomLeft, &bottom, &bottomRight, table());
299 
300 		if (border.isNull())
301 			continue; // Quit early if the border to paint is null.
302 
303 		// Set initial coordinates.
304 		start.setY(table()->rowPosition(startRow));
305 		end.setY(table()->rowPosition(endRow) + table()->rowHeight(endRow));
306 
307 		// Adjust coordinates for joining.
308 		joinVertical(border, topLeft, top, topRight, bottomLeft, bottom, bottomRight,
309 			 &start, &end, &startOffsetFactors, &endOffsetFactors);
310 
311 		// Paint the border.
312 		paintBorder(border, start, end, startOffsetFactors, endOffsetFactors, p);
313 	}
314 }
315 
paintCellTopBorders(const TableCell & cell,ScPainter * p) const316 void CollapsedTablePainter::paintCellTopBorders(const TableCell& cell, ScPainter* p) const
317 {
318 	/*
319 	 * We are going to paint the border marked # in the following setup.
320 	 *
321 	 *  +--------------------------+--------------------------+--------------------------+
322 	 *  |                          |                          |                          |
323 	 *  |                          |                          |                          |
324 	 *  |        topLeftCell   topLeft        topCell      topRight   topRightCell       |
325 	 *  |                          |                          |                          |
326 	 *  |                          |                          |                          |
327 	 *  +----------left------------+######### border-#########+----------right-----------+
328 	 *  |                          |                          |                          |
329 	 *  |                          |                          |                          |
330 	 *  |     bottomLeftCell   bottomLeft      cell      bottomRight  bottomRightCell    |
331 	 *  |                          |                          |                          |
332 	 *  |                          |                          |                          |
333 	 *  +--------------------------+--------------------------+--------------------------+
334 	 */
335 	// The cell starts in this row.
336 	const int firstRow = cell.row();
337 	// The cell ends in this column.
338 	const int lastCol = cell.column() + cell.columnSpan() - 1;
339 	// The Y position of the border segments to paint.
340 	const double borderY = table()->rowPosition(firstRow);
341 
342 	// The start point of the border segment currently being painted.
343 	QPointF start(0.0, borderY);
344 	// The end point of the border segment currently being painted.
345 	QPointF end(0.0, borderY);
346 	// The start and end offset factors for the border segment currently being painted.
347 	QPointF startOffsetFactors, endOffsetFactors;
348 	// Start and end column of border segment currently being painted.
349 	int startCol, endCol;
350 
351 	for (int col = cell.column(); col <= lastCol; col += endCol - startCol + 1)
352 	{
353 		// Get the neighboring cell above and determine border segment column interval.
354 		TableCell topCell = table()->cellAt(firstRow - 1, col);
355 		startCol = qMax(cell.column(), topCell.column());
356 		endCol = qMin(lastCol, topCell.isValid() ? topCell.column() + topCell.columnSpan() - 1 : lastCol);
357 
358 		// Get the remaining neighboring cells.
359 		TableCell topLeftCell = table()->cellAt(firstRow - 1, startCol - 1);
360 		TableCell topRightCell = table()->cellAt(firstRow - 1, endCol + 1);
361 		TableCell bottomRightCell = table()->cellAt(firstRow, endCol + 1);
362 		TableCell bottomLeftCell = table()->cellAt(firstRow, startCol - 1);
363 
364 		// Resolve borders between neighboring cells.
365 		TableBorder topLeft, left, bottomLeft, border, topRight, right, bottomRight;
366 		resolveBordersHorizontal(topLeftCell, topCell, topRightCell, bottomLeftCell, cell, bottomRightCell, &topLeft, &left, &bottomLeft, &border, &topRight, &right, &bottomRight, table());
367 
368 		if (border.isNull())
369 			continue; // Quit early if the border is null.
370 
371 		// Set initial coordinates.
372 		start.setX(table()->columnPosition(startCol));
373 		end.setX(table()->columnPosition(endCol) + table()->columnWidth(endCol));
374 
375 		// Adjust coordinates for joining.
376 		joinHorizontal(border, topLeft, left, bottomLeft, topRight, right, bottomRight,
377 			 &start, &end, &startOffsetFactors, &endOffsetFactors);
378 
379 		// Paint the border segment.
380 		paintBorder(border, start, end, startOffsetFactors, endOffsetFactors, p);
381 	}
382 }
383 
paintCellBottomBorders(const TableCell & cell,ScPainter * p) const384 void CollapsedTablePainter::paintCellBottomBorders(const TableCell& cell, ScPainter* p) const
385 {
386 	/*
387 	 * We are going to paint the border marked # in the following setup.
388 	 *
389 	 *  +--------------------------+--------------------------+--------------------------+
390 	 *  |                          |                          |                          |
391 	 *  |                          |                          |                          |
392 	 *  |        topLeftCell   topLeft         cell        topRight   topRightCell       |
393 	 *  |                          |                          |                          |
394 	 *  |                          |                          |                          |
395 	 *  +----------left------------+######### border-#########+----------right-----------+
396 	 *  |                          |                          |                          |
397 	 *  |                          |                          |                          |
398 	 *  |     bottomLeftCell   bottomLeft   bottomCell   bottomRight  bottomRightCell    |
399 	 *  |                          |                          |                          |
400 	 *  |                          |                          |                          |
401 	 *  +--------------------------+--------------------------+--------------------------+
402 	 */
403 	// The cell ends in this row.
404 	const int lastRow = cell.row() + cell.rowSpan() - 1;
405 	// The cell ends in this column.
406 	const int lastCol = cell.column() + cell.columnSpan() - 1;
407 	// The Y position of the border segments to paint.
408 	const double borderY = table()->rowPosition(lastRow) + table()->rowHeight(lastRow);
409 
410 	// The start point of the border segment currently being painted.
411 	QPointF start(0.0, borderY);
412 	// The end point of the border segment currently being painted.
413 	QPointF end(0.0, borderY);
414 	// The start and end offset factors for the border segment currently being painted.
415 	QPointF startOffsetFactors, endOffsetFactors;
416 	// Start and end column of border segment currently being painted.
417 	int startCol, endCol;
418 
419 	for (int col = cell.column(); col <= lastCol; col += endCol - startCol + 1)
420 	{
421 		// Get the neighboring cell below and determine border segment column interval.
422 		TableCell bottomCell = table()->cellAt(lastRow + 1, col);
423 		startCol = qMax(cell.column(), bottomCell.column());
424 		endCol = qMin(lastCol, bottomCell.isValid() ? bottomCell.column() + bottomCell.columnSpan() - 1 : lastCol);
425 
426 		// Get the remaining neighboring cells.
427 		TableCell topLeftCell = table()->cellAt(lastRow, startCol - 1);
428 		TableCell topRightCell = table()->cellAt(lastRow, endCol + 1);
429 		TableCell bottomRightCell = table()->cellAt(lastRow + 1, endCol + 1);
430 		TableCell bottomLeftCell = table()->cellAt(lastRow + 1, startCol - 1);
431 
432 		// Resolve borders between neighboring cells.
433 		TableBorder topLeft, left, bottomLeft, border, topRight, right, bottomRight;
434 		resolveBordersHorizontal(topLeftCell, cell, topRightCell, bottomLeftCell, bottomCell, bottomRightCell, &topLeft, &left, &bottomLeft, &border, &topRight, &right, &bottomRight, table());
435 
436 		if (border.isNull())
437 			continue; // Quit early if the border is null.
438 
439 		// Set initial coordinates.
440 		start.setX(table()->columnPosition(startCol));
441 		end.setX(table()->columnPosition(endCol) + table()->columnWidth(endCol));
442 
443 		// Adjust coordinates for joining.
444 		joinHorizontal(border, topLeft, left, bottomLeft, topRight, right, bottomRight,
445 			 &start, &end, &startOffsetFactors, &endOffsetFactors);
446 
447 		// Paint the border segment.
448 		paintBorder(border, start, end, startOffsetFactors, endOffsetFactors, p);
449 	}
450 }
451 
paintCellFill(const TableCell & cell,ScPainter * p) const452 void CollapsedTablePainter::paintCellFill(const TableCell& cell, ScPainter* p) const
453 {
454 	QString colorName = cell.fillColor();
455 
456 	if (colorName == CommonStrings::None)
457 		return;
458 
459 	p->save();
460 
461 	QColor color;
462 	table()->SetQColor(&color, colorName, cell.fillShade());
463 	p->setBrush(color);
464 	p->setFillMode(ScPainter::Solid);
465 	p->setStrokeMode(ScPainter::None);
466 
467 	int row = cell.row();
468 	int col = cell.column();
469 	int lastRow = row + cell.rowSpan() - 1;
470 	int lastCol = col + cell.columnSpan() - 1;
471 
472 	double x = table()->columnPosition(col);
473 	double y = table()->rowPosition(row);
474 	double width = table()->columnPosition(lastCol) + table()->columnWidth(lastCol) - x;
475 	double height = table()->rowPosition(lastRow) + table()->rowHeight(lastRow) - y;
476 	p->drawRect(x, y, width, height);
477 
478 	p->restore();
479 }
480 
paintBorder(const TableBorder & border,const QPointF & start,const QPointF & end,const QPointF & startOffsetFactors,const QPointF & endOffsetFactors,ScPainter * p) const481 void CollapsedTablePainter::paintBorder(const TableBorder& border, const QPointF& start, const QPointF& end,
482 										const QPointF& startOffsetFactors, const QPointF& endOffsetFactors,
483 										ScPainter *p) const
484 {
485 	p->save();
486 	p->setStrokeMode(ScPainter::Solid);
487 	p->setFillMode(ScPainter::None);
488 
489 	QColor lineColor;
490 	QPointF lineStart, lineEnd;
491 	for (const TableBorderLine& line : border.borderLines())
492 	{
493 		// Adjust line start and ends by line width multiplied by offset factors.
494 		lineStart.setX(start.x() + line.width() * startOffsetFactors.x());
495 		lineStart.setY(start.y() + line.width() * startOffsetFactors.y());
496 		lineEnd.setX(end.x() + line.width() * endOffsetFactors.x());
497 		lineEnd.setY(end.y() + line.width() * endOffsetFactors.y());
498 		// Set line color.
499 		if (line.color() == CommonStrings::None)
500 			continue;
501 		table()->SetQColor(&lineColor, line.color(), line.shade());
502 		// Draw line.
503 		p->setPen(lineColor, line.width(), line.style(), Qt::FlatCap, Qt::MiterJoin);
504 		p->drawLine(lineStart, lineEnd);
505 	}
506 
507 	p->restore();
508 }
509 
paintGridLine(const QPointF & start,const QPointF & end,ScPainter * p) const510 void CollapsedTablePainter::paintGridLine(const QPointF& start, const QPointF& end, ScPainter *p) const
511 {
512 	p->save();
513 	p->setPen(PrefsManager::instance().appPrefs.displayPrefs.frameNormColor,
514 		0.2 / qMax(p->zoomFactor(), 1.0), Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
515 	p->setStrokeMode(ScPainter::Solid);
516 	p->drawLine(start, end);
517 	p->restore();
518 }
519