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