1 /* This file is part of the KDE project
2 Copyright 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18 */
19
20 // Local
21 #include "SheetView.h"
22
23 #include <QCache>
24 #include <QRect>
25 #include <QPainter>
26 #include <QPainterPath>
27 #ifdef CALLIGRA_SHEETS_MT
28 #include <QMutex>
29 #include <QMutexLocker>
30 #include <QReadWriteLock>
31 #include <QReadLocker>
32 #include <QWriteLocker>
33 #endif
34
35 #include <KoViewConverter.h>
36
37 #include "CellView.h"
38 #include "calligra_sheets_limits.h"
39 #include "PointStorage.h"
40 #include "RectStorage.h"
41 #include "Region.h"
42 #include "RowColumnFormat.h"
43 #include "RowFormatStorage.h"
44 #include "Sheet.h"
45
46 using namespace Calligra::Sheets;
47
48 struct CellPaintData
49 {
CellPaintDataCellPaintData50 CellPaintData(const CellView &cellView, const Cell &cell, const QPointF &coordinate)
51 : cellView(cellView)
52 , cell(cell)
53 , coordinate(coordinate)
54 {}
55 CellView cellView;
56 Cell cell;
57 QPointF coordinate;
58 };
59
60 class Q_DECL_HIDDEN SheetView::Private
61 {
62 public:
Private()63 Private()
64 #ifdef CALLIGRA_SHEETS_MT
65 : cacheMutex(QMutex::Recursive)
66 #endif
67 {}
68 const Sheet* sheet;
69 const KoViewConverter* viewConverter;
70 QRect visibleRect;
71 QCache<QPoint, CellView> cache;
72 #ifdef CALLIGRA_SHEETS_MT
73 QMutex cacheMutex;
74 #endif
75 QRegion cachedArea;
76 CellView* defaultCellView;
77 // The maximum accessed cell range used for the scrollbar ranges.
78 QSize accessedCellRange;
79 FusionStorage* obscuredInfo;
80 QSize obscuredRange; // size of the bounding box of obscuredInfo
81 #ifdef CALLIGRA_SHEETS_MT
82 QReadWriteLock obscuredLock;
83 #endif
84
85 PointStorage<bool> highlightedCells;
86 QPoint activeHighlight;
87 #ifdef CALLIGRA_SHEETS_MT
88 QReadWriteLock highlightLock;
89 #endif
90 QColor highlightColor;
91 QColor highlightMaskColor;
92 QColor activeHighlightColor;
93 public:
94 Cell cellToProcess(int col, int row, QPointF& coordinate, QSet<Cell>& processedMergedCells, const QRect& visRect);
95 #ifdef CALLIGRA_SHEETS_MT
96 CellView cellViewToProcess(Cell& cell, QPointF& coordinate, QSet<Cell>& processedObscuredCells,
97 SheetView* sheetView, const QRect& visRect);
98 #else
99 const CellView& cellViewToProcess(Cell& cell, QPointF& coordinate, QSet<Cell>& processedObscuredCells,
100 SheetView* sheetView, const QRect& visRect);
101 #endif
102 };
103
cellToProcess(int col,int row,QPointF & coordinate,QSet<Cell> & processedMergedCells,const QRect & visRect)104 Cell SheetView::Private::cellToProcess(int col, int row, QPointF& coordinate,
105 QSet<Cell>& processedMergedCells,
106 const QRect& visRect)
107 {
108 Cell cell(sheet, col, row);
109 if (cell.isPartOfMerged()) {
110 cell = cell.masterCell();
111 // if the rect of visible cells contains this master cell, it was already painted
112 if (visRect.contains(cell.cellPosition())) {
113 coordinate.setY(coordinate.y() + sheet->rowFormats()->rowHeight(row));
114 return Cell(); // next row
115 }
116 // if the out of bounds master cell was already painted, there's nothing more to do
117 if (processedMergedCells.contains(cell)) {
118 coordinate.setY(coordinate.y() + sheet->rowFormats()->rowHeight(row));
119 return Cell(); // next row
120 }
121 processedMergedCells.insert(cell);
122 // take the coordinate of the master cell
123 if (sheet->layoutDirection() == Qt::RightToLeft) {
124 for (int c = cell.column()+1; c <= col; ++c)
125 coordinate.setX(coordinate.x() + sheet->columnFormat(c)->width());
126 } else {
127 for (int c = cell.column(); c < col; ++c)
128 coordinate.setX(coordinate.x() - sheet->columnFormat(c)->width());
129 }
130 for (int r = cell.row(); r < row; ++r)
131 coordinate.setY(coordinate.y() - sheet->rowFormats()->rowHeight(r));
132 }
133 return cell;
134 }
135
136 #ifdef CALLIGRA_SHEETS_MT
cellViewToProcess(Cell & cell,QPointF & coordinate,QSet<Cell> & processedObscuredCells,SheetView * sheetView,const QRect & visRect)137 CellView SheetView::Private::cellViewToProcess(Cell& cell, QPointF& coordinate,
138 QSet<Cell>& processedObscuredCells, SheetView* sheetView, const QRect& visRect)
139 #else
140 const CellView& SheetView::Private::cellViewToProcess(Cell& cell, QPointF& coordinate,
141 QSet<Cell>& processedObscuredCells, SheetView* sheetView, const QRect& visRect)
142 #endif
143 {
144 const int col = cell.column();
145 const int row = cell.row();
146 const QPoint cellPos = cell.cellPosition();
147 #ifdef CALLIGRA_SHEETS_MT
148 CellView cellView = sheetView->cellView(col, row);
149 #else
150 const CellView& cellView = sheetView->cellView(col, row);
151 #endif
152 if (sheetView->isObscured(cellPos)) {
153 // if the rect of visible cells contains the obscuring cell, it was already painted
154 if (visRect.contains(sheetView->obscuringCell(cellPos))) {
155 coordinate.setY(coordinate.y() + sheet->rowFormats()->rowHeight(row));
156 cell = Cell();
157 return cellView; // next row
158 }
159 cell = Cell(sheet, sheetView->obscuringCell(cellPos));
160 if (processedObscuredCells.contains(cell)) {
161 coordinate.setY(coordinate.y() + sheet->rowFormats()->rowHeight(row));
162 cell = Cell();
163 return cellView; // next row
164 }
165 processedObscuredCells.insert(cell);
166 // take the coordinate of the obscuring cell
167 if (sheet->layoutDirection() == Qt::RightToLeft) {
168 for (int c = cell.column()+1; c <= col; ++c)
169 coordinate.setX(coordinate.x() + sheet->columnFormat(c)->width());
170 } else {
171 for (int c = cell.column(); c < col; ++c)
172 coordinate.setX(coordinate.x() - sheet->columnFormat(c)->width());
173 }
174 for (int r = cell.row(); r < row; ++r)
175 coordinate.setY(coordinate.y() - sheet->rowFormats()->rowHeight(r));
176 // use the CellView of the obscuring cell
177 return sheetView->cellView(cell.column(), cell.row());
178 }
179 return cellView;
180 }
181
182
SheetView(const Sheet * sheet)183 SheetView::SheetView(const Sheet* sheet)
184 : QObject(const_cast<Sheet*>(sheet))
185 , d(new Private)
186 {
187 d->sheet = sheet;
188 d->viewConverter = 0;
189 d->visibleRect = QRect(1, 1, 0, 0);
190 d->cache.setMaxCost(10000);
191 d->defaultCellView = createDefaultCellView();
192 d->accessedCellRange = sheet->usedArea().size().expandedTo(QSize(256, 256));
193 d->obscuredInfo = new FusionStorage(sheet->map());
194 d->obscuredRange = QSize(0, 0);
195 d->highlightMaskColor = QColor(0, 0, 0, 128);
196 d->activeHighlightColor = QColor(255, 127, 0, 128);
197 }
198
~SheetView()199 SheetView::~SheetView()
200 {
201 delete d->defaultCellView;
202 delete d->obscuredInfo;
203 delete d;
204 }
205
sheet() const206 const Sheet* SheetView::sheet() const
207 {
208 return d->sheet;
209 }
210
setViewConverter(const KoViewConverter * viewConverter)211 void SheetView::setViewConverter(const KoViewConverter* viewConverter)
212 {
213 Q_ASSERT(viewConverter);
214 d->viewConverter = viewConverter;
215 }
216
viewConverter() const217 const KoViewConverter* SheetView::viewConverter() const
218 {
219 Q_ASSERT(d->viewConverter);
220 return d->viewConverter;
221 }
222
223 #ifdef CALLIGRA_SHEETS_MT
cellView(const QPoint & pos)224 CellView SheetView::cellView(const QPoint& pos)
225 #else
226 const CellView& SheetView::cellView(const QPoint& pos)
227 #endif
228 {
229 return cellView(pos.x(), pos.y());
230 }
231
232 #ifdef CALLIGRA_SHEETS_MT
cellView(int col,int row)233 CellView SheetView::cellView(int col, int row)
234 #else
235 const CellView& SheetView::cellView(int col, int row)
236 #endif
237 {
238 Q_ASSERT(1 <= col && col <= KS_colMax);
239 Q_ASSERT(1 <= row && col <= KS_rowMax);
240 #ifdef CALLIGRA_SHEETS_MT
241 QMutexLocker ml(&d->cacheMutex);
242 #endif
243 CellView *v = d->cache.object(QPoint(col, row));
244 if (!v) {
245 v = createCellView(col, row);
246 d->cache.insert(QPoint(col, row), v);
247 d->cachedArea += QRect(col, row, 1, 1);
248 }
249 #ifdef CALLIGRA_SHEETS_MT
250 // create a copy as long as the mutex is locked
251 CellView cellViewCopy = *v;
252 return cellViewCopy;
253 #else
254 return *v;
255 #endif
256 }
257
setPaintCellRange(const QRect & rect)258 void SheetView::setPaintCellRange(const QRect& rect)
259 {
260 #ifdef CALLIGRA_SHEETS_MT
261 QMutexLocker ml(&d->cacheMutex);
262 #endif
263 d->visibleRect = rect & QRect(1, 1, KS_colMax, KS_rowMax);
264 d->cache.setMaxCost(2 * rect.width() * rect.height());
265 }
266
paintCellRange() const267 QRect SheetView::paintCellRange() const
268 {
269 return d->visibleRect;
270 }
271
invalidateRegion(const Region & region)272 void SheetView::invalidateRegion(const Region& region)
273 {
274 QRegion qregion;
275 Region::ConstIterator end(region.constEnd());
276 for (Region::ConstIterator it(region.constBegin()); it != end; ++it) {
277 qregion += (*it)->rect();
278 }
279 // reduce to the cached area
280 qregion &= d->cachedArea;
281 QVector<QRect> rects = qregion.rects();
282 for (int i = 0; i < rects.count(); ++i)
283 invalidateRange(rects[i]);
284 }
285
invalidate()286 void SheetView::invalidate()
287 {
288 #ifdef CALLIGRA_SHEETS_MT
289 QMutexLocker ml(&d->cacheMutex);
290 #endif
291 delete d->defaultCellView;
292 d->defaultCellView = createDefaultCellView();
293 d->cache.clear();
294 d->cachedArea = QRegion();
295 delete d->obscuredInfo;
296 d->obscuredInfo = new FusionStorage(d->sheet->map());
297 d->obscuredRange = QSize(0, 0);
298 }
299
paintCells(QPainter & painter,const QRectF & paintRect,const QPointF & topLeft,CanvasBase *,const QRect & visibleRect)300 void SheetView::paintCells(QPainter& painter, const QRectF& paintRect, const QPointF& topLeft, CanvasBase*, const QRect& visibleRect)
301 {
302 const QRect& visRect = visibleRect.isValid() ? visibleRect : d->visibleRect;
303 // paintRect: the canvas area, that should be painted; in document coordinates;
304 // no layout direction consideration; scrolling offset applied;
305 // independent from painter transformations
306 // topLeft: the document coordinate of the top left cell's top left corner;
307 // no layout direction consideration; independent from painter
308 // transformations
309
310 // NOTE Stefan: The painting is split into several steps. In each of these all cells in
311 // d->visibleRect are traversed. This may appear suboptimal at the first look, but
312 // ensures that the borders are not erased by the background of adjacent cells.
313
314 // debugSheets << "paintRect:" << paintRect;
315 // debugSheets << "topLeft:" << topLeft;
316
317 QRegion clipRect(painter.clipRegion());
318 // 0. Paint the sheet background
319 if (!sheet()->backgroundImage().isNull()) {
320 //TODO support all the different properties
321 Sheet::BackgroundImageProperties properties = sheet()->backgroundImageProperties();
322 if( properties.repeat == Sheet::BackgroundImageProperties::Repeat ) {
323 const int firstCol = visRect.left();
324 const int firstRow = visRect.top();
325 const int firstColPosition = d->sheet->columnPosition(firstCol);
326 const int firstRowPosition = d->sheet->rowPosition(firstRow);
327
328 const int imageWidth = sheet()->backgroundImage().rect().width();
329 const int imageHeight = sheet()->backgroundImage().rect().height();
330
331 int xBackground = firstColPosition - (firstColPosition % imageWidth);
332 int yBackground = firstRowPosition - (firstRowPosition % imageHeight);
333
334 const int lastCol = visRect.right();
335 const int lastRow = visRect.bottom();
336 const int lastColPosition = d->sheet->columnPosition(lastCol);
337 const int lastRowPosition = d->sheet->rowPosition(lastRow);
338
339 while( xBackground < lastColPosition ) {
340 int y = yBackground;
341 while( y < lastRowPosition ) {
342 painter.drawImage(QRect(xBackground, y, imageWidth, imageHeight), sheet()->backgroundImage());
343 y += imageHeight;
344 }
345 xBackground += imageWidth;
346 }
347 }
348 }
349
350 // 1. Paint the cell background
351
352 // Handle right-to-left layout.
353 // In an RTL sheet the cells have to be painted at their opposite horizontal
354 // location on the canvas, meaning that column A will be the rightmost column
355 // on screen, column B will be to the left of it and so on. Here we change
356 // the horizontal coordinate at which we start painting the cell in case the
357 // sheet's direction is RTL.
358 const bool rightToLeft = sheet()->layoutDirection() == Qt::RightToLeft;
359 const QPointF startCoordinate(rightToLeft ? paintRect.width() - topLeft.x() : topLeft.x(), topLeft.y());
360 QPointF coordinate(startCoordinate);
361 // debugSheets << "start coordinate:" << coordinate;
362 QSet<Cell> processedMergedCells;
363 QSet<Cell> processedObscuredCells;
364 QList<CellPaintData> cached_cells;
365 for (int col = visRect.left(); col <= visRect.right(); ++col) {
366 if (d->sheet->columnFormat(col)->isHiddenOrFiltered())
367 continue;
368 if (rightToLeft)
369 coordinate.setX(coordinate.x() - d->sheet->columnFormat(col)->width());
370 // debugSheets <<"coordinate:" << coordinate;
371 for (int row = visRect.top(); row <= visRect.bottom(); ++row) {
372 int lastHiddenRow;
373 if (d->sheet->rowFormats()->isHiddenOrFiltered(row, &lastHiddenRow)) {
374 row = lastHiddenRow;
375 continue;
376 }
377 // save the coordinate
378 const QPointF savedCoordinate = coordinate;
379 // figure out, if any and which cell has to be painted (may be a master cell)
380 Cell cell = d->cellToProcess(col, row, coordinate, processedMergedCells, visRect);
381 if (!cell)
382 continue;
383 // figure out, which CellView to use (may be one for an obscuring cell)
384 CellPaintData cpd(d->cellViewToProcess(cell, coordinate, processedObscuredCells, this, visRect), cell, coordinate);
385 if (!cell)
386 continue;
387 cpd.cellView.paintCellBackground(painter, clipRect, coordinate);
388 cached_cells.append(cpd);
389 // restore coordinate
390 coordinate = savedCoordinate;
391 coordinate.setY(coordinate.y() + d->sheet->rowFormats()->rowHeight(row));
392 }
393 coordinate.setY(topLeft.y());
394 if (!rightToLeft)
395 coordinate.setX(coordinate.x() + d->sheet->columnFormat(col)->width());
396 }
397
398 // 2. Paint the cell content including markers (formula, comment, ...)
399 for (QList<CellPaintData>::ConstIterator it(cached_cells.constBegin()); it != cached_cells.constEnd(); ++it) {
400 it->cellView.paintCellContents(paintRect, painter, clipRect, it->coordinate, it->cell, this);
401 }
402
403 // 3. Paint the default borders
404 coordinate = startCoordinate;
405 processedMergedCells.clear();
406 for (int col = visRect.left(); col <= visRect.right(); ++col) {
407 if (d->sheet->columnFormat(col)->isHiddenOrFiltered())
408 continue;
409 if (rightToLeft)
410 coordinate.setX(coordinate.x() - d->sheet->columnFormat(col)->width());
411 for (int row = visRect.top(); row <= visRect.bottom(); ++row) {
412 int lastHiddenRow;
413 if (d->sheet->rowFormats()->isHiddenOrFiltered(row, &lastHiddenRow)) {
414 row = lastHiddenRow;
415 continue;
416 }
417 // For borders even cells, that are merged in, need to be traversed.
418 // Think of a merged cell with a set border and one its neighbours has a thicker border.
419 // but: also the master cell of a merged cell always needs to be processed
420 const QPointF savedCoordinate = coordinate;
421 Cell cell = d->cellToProcess(col, row, coordinate, processedMergedCells, visRect);
422 if (!!cell && (cell.column() != col || cell.row() != row)) {
423 const CellView cellView = this->cellView(cell.cellPosition());
424 cellView.paintDefaultBorders(painter, clipRect, paintRect, coordinate,
425 CellView::LeftBorder | CellView::RightBorder |
426 CellView::TopBorder | CellView::BottomBorder,
427 visRect, cell, this);
428 }
429 coordinate = savedCoordinate;
430 const CellView cellView = this->cellView(col, row);
431 cellView.paintDefaultBorders(painter, clipRect, paintRect, coordinate,
432 CellView::LeftBorder | CellView::RightBorder |
433 CellView::TopBorder | CellView::BottomBorder,
434 visRect, Cell(d->sheet, col, row), this);
435 coordinate.setY(coordinate.y() + d->sheet->rowFormats()->rowHeight(row));
436 }
437 coordinate.setY(topLeft.y());
438 if (!rightToLeft)
439 coordinate.setX(coordinate.x() + d->sheet->columnFormat(col)->width());
440 }
441
442 // 4. Paint the custom borders, diagonal lines and page borders
443 coordinate = startCoordinate;
444 processedMergedCells.clear();
445 processedObscuredCells.clear();
446 for (int col = visRect.left(); col <= visRect.right(); ++col) {
447 if (d->sheet->columnFormat(col)->isHiddenOrFiltered())
448 continue;
449 if (rightToLeft)
450 coordinate.setX(coordinate.x() - d->sheet->columnFormat(col)->width());
451 for (int row = visRect.top(); row <= visRect.bottom(); ++row) {
452 int lastHiddenRow;
453 if (d->sheet->rowFormats()->isHiddenOrFiltered(row, &lastHiddenRow)) {
454 row = lastHiddenRow;
455 continue;
456 }
457 // For borders even cells, that are merged in, need to be traversed.
458 // Think of a merged cell with a set border and one its neighbours has a thicker border.
459 // but: also the master cell of a merged cell always needs to be processed
460 const QPointF savedCoordinate = coordinate;
461 Cell cell = d->cellToProcess(col, row, coordinate, processedMergedCells, visRect);
462 if (!!cell && (cell.column() != col || cell.row() != row)) {
463 const CellView cellView = this->cellView(cell.cellPosition());
464 cellView.paintCellBorders(paintRect, painter, clipRect, coordinate,
465 visRect,
466 cell, this);
467 }
468 coordinate = savedCoordinate;
469 Cell theCell(sheet(), col, row);
470 const CellView cellView = d->cellViewToProcess(theCell, coordinate, processedObscuredCells, this, visRect);
471 if (!!theCell && (theCell.column() != col || theCell.row() != row)) {
472 cellView.paintCellBorders(paintRect, painter, clipRect, coordinate,
473 visRect,
474 theCell, this);
475 }
476 const CellView cellView2 = this->cellView(col, row);
477 coordinate = savedCoordinate;
478 cellView2.paintCellBorders(paintRect, painter, clipRect, coordinate,
479 visRect,
480 Cell(sheet(), col, row), this);
481 coordinate.setY(coordinate.y() + d->sheet->rowFormats()->rowHeight(row));
482 }
483 coordinate.setY(topLeft.y());
484 if (!rightToLeft)
485 coordinate.setX(coordinate.x() + d->sheet->columnFormat(col)->width());
486 }
487
488 // 5. Paint cell highlighting
489 if (hasHighlightedCells()) {
490 QPointF active = activeHighlight();
491 QPainterPath p;
492 const CellPaintData* activeData = 0;
493 for (QList<CellPaintData>::ConstIterator it(cached_cells.constBegin()); it != cached_cells.constEnd(); ++it) {
494 if (isHighlighted(it->cell.cellPosition())) {
495 p.addRect(it->coordinate.x(), it->coordinate.y(), it->cellView.cellWidth(), it->cellView.cellHeight());
496 if (it->cell.cellPosition() == active) {
497 activeData = &*it;
498 }
499 }
500 }
501 painter.setPen(Qt::NoPen);
502 if (d->highlightColor.isValid()) {
503 painter.setBrush(QBrush(d->highlightColor));
504 painter.drawPath(p);
505 }
506 if (d->highlightMaskColor.isValid()) {
507 QPainterPath base;
508 base.addRect(painter.clipPath().boundingRect().adjusted(-5, -5, 5, 5));
509 p = base.subtracted(p);
510 painter.setBrush(QBrush(d->highlightMaskColor));
511 painter.drawPath(p);
512 }
513
514 if (activeData && d->activeHighlightColor.isValid()) {
515 painter.setBrush(QBrush(d->activeHighlightColor));
516 painter.setPen(QPen(Qt::black, 0));
517 painter.drawRect(QRectF(activeData->coordinate.x(), activeData->coordinate.y(), activeData->cellView.cellWidth(), activeData->cellView.cellHeight()));
518 }
519 }
520 }
521
invalidateRange(const QRect & range)522 void SheetView::invalidateRange(const QRect& range)
523 {
524 #ifdef CALLIGRA_SHEETS_MT
525 QMutexLocker ml(&d->cacheMutex);
526 #endif
527 QRegion obscuredRegion;
528 const int right = range.right();
529 for (int col = range.left(); col <= right; ++col) {
530 const int bottom = range.bottom();
531 for (int row = range.top(); row <= bottom; ++row) {
532 const QPoint p(col, row);
533 if (!d->cache.contains(p))
534 continue;
535 if (obscuresCells(p) || isObscured(p)) {
536 obscuredRegion += obscuredArea(p);
537 obscureCells(p, 0, 0);
538 }
539 d->cache.remove(p);
540 }
541 }
542 d->cachedArea -= range;
543 obscuredRegion &= d->cachedArea;
544 foreach (const QRect& rect, obscuredRegion.rects()) {
545 invalidateRange(rect);
546 }
547 }
548
obscureCells(const QPoint & position,int numXCells,int numYCells)549 void SheetView::obscureCells(const QPoint &position, int numXCells, int numYCells)
550 {
551 #ifdef CALLIGRA_SHEETS_MT
552 QWriteLocker(&d->obscuredLock);
553 #endif
554 // Start by un-obscuring cells that we might be obscuring right now
555 const QPair<QRectF, bool> pair = d->obscuredInfo->containedPair(position);
556 if (!pair.first.isNull())
557 d->obscuredInfo->insert(Region(pair.first.toRect()), false);
558 // Obscure the cells
559 if (numXCells != 0 || numYCells != 0)
560 d->obscuredInfo->insert(Region(position.x(), position.y(), numXCells + 1, numYCells + 1), true);
561
562 QRect obscuredArea = d->obscuredInfo->usedArea();
563 QSize newObscuredRange(obscuredArea.right(), obscuredArea.bottom());
564 if (newObscuredRange != d->obscuredRange) {
565 d->obscuredRange = newObscuredRange;
566 emit obscuredRangeChanged(d->obscuredRange);
567 }
568 }
569
obscuringCell(const QPoint & obscuredCell) const570 QPoint SheetView::obscuringCell(const QPoint &obscuredCell) const
571 {
572 #ifdef CALLIGRA_SHEETS_MT
573 QReadLocker(&d->obscuredLock);
574 #endif
575 const QPair<QRectF, bool> pair = d->obscuredInfo->containedPair(obscuredCell);
576 if (pair.first.isNull())
577 return obscuredCell;
578 if (pair.second == false)
579 return obscuredCell;
580 return pair.first.toRect().topLeft();
581 }
582
obscuredRange(const QPoint & obscuringCell) const583 QSize SheetView::obscuredRange(const QPoint &obscuringCell) const
584 {
585 #ifdef CALLIGRA_SHEETS_MT
586 QReadLocker(&d->obscuredLock);
587 #endif
588 const QPair<QRectF, bool> pair = d->obscuredInfo->containedPair(obscuringCell);
589 if (pair.first.isNull())
590 return QSize(0, 0);
591 if (pair.second == false)
592 return QSize(0, 0);
593 // Not the master cell?
594 if (pair.first.toRect().topLeft() != obscuringCell)
595 return QSize(0, 0);
596 return pair.first.toRect().size() - QSize(1, 1);
597 }
598
obscuredArea(const QPoint & cell) const599 QRect SheetView::obscuredArea(const QPoint &cell) const
600 {
601 #ifdef CALLIGRA_SHEETS_MT
602 QReadLocker(&d->obscuredLock);
603 #endif
604 const QPair<QRectF, bool> pair = d->obscuredInfo->containedPair(cell);
605 if (pair.first.isNull())
606 return QRect(cell, QSize(1, 1));
607 if (pair.second == false)
608 return QRect(cell, QSize(1, 1));
609 // Not the master cell?
610 return pair.first.toRect();
611 }
612
isObscured(const QPoint & cell) const613 bool SheetView::isObscured(const QPoint &cell) const
614 {
615 #ifdef CALLIGRA_SHEETS_MT
616 QReadLocker(&d->obscuredLock);
617 #endif
618 const QPair<QRectF, bool> pair = d->obscuredInfo->containedPair(cell);
619 if (pair.first.isNull())
620 return false;
621 if (pair.second == false)
622 return false;
623 // master cell?
624 if (pair.first.toRect().topLeft() == cell)
625 return false;
626 return true;
627 }
628
obscuresCells(const QPoint & cell) const629 bool SheetView::obscuresCells(const QPoint &cell) const
630 {
631 #ifdef CALLIGRA_SHEETS_MT
632 QReadLocker(&d->obscuredLock);
633 #endif
634 const QPair<QRectF, bool> pair = d->obscuredInfo->containedPair(cell);
635 if (pair.first.isNull())
636 return false;
637 if (pair.second == false)
638 return false;
639 // master cell?
640 if (pair.first.toRect().topLeft() != cell)
641 return false;
642 return true;
643 }
644
totalObscuredRange() const645 QSize SheetView::totalObscuredRange() const
646 {
647 #ifdef CALLIGRA_SHEETS_MT
648 QReadLocker(&d->obscuredLock);
649 #endif
650 return d->obscuredRange;
651 }
652
653 #ifdef CALLIGRA_SHEETS_MT
defaultCellView() const654 CellView SheetView::defaultCellView() const
655 #else
656 const CellView& SheetView::defaultCellView() const
657 #endif
658 {
659 return *d->defaultCellView;
660 }
661
updateAccessedCellRange(const QPoint & location)662 void SheetView::updateAccessedCellRange(const QPoint& location)
663 {
664 const QSize cellRange = d->accessedCellRange.expandedTo(QSize(location.x(), location.y()));
665 if (d->accessedCellRange != cellRange || location.isNull()) {
666 d->accessedCellRange = cellRange;
667 const int col = qMin(KS_colMax, cellRange.width() + 10);
668 const int row = qMin(KS_rowMax, cellRange.height() + 10);
669 const double width = sheet()->columnPosition(col) + sheet()->columnFormat(col)->width();
670 const double height = sheet()->rowPosition(row) + sheet()->rowFormats()->rowHeight(row);
671 emit visibleSizeChanged(QSizeF(width, height));
672 }
673 }
674
createDefaultCellView()675 CellView* SheetView::createDefaultCellView()
676 {
677 return new CellView(this);
678 }
679
createCellView(int col,int row)680 CellView* SheetView::createCellView(int col, int row)
681 {
682 return new CellView(this, col, row);
683 }
684
isHighlighted(const QPoint & cell) const685 bool SheetView::isHighlighted(const QPoint &cell) const
686 {
687 #ifdef CALLIGRA_SHEETS_MT
688 QReadLocker(&d->highlightLock);
689 #endif
690 return d->highlightedCells.lookup(cell.x(), cell.y());
691 }
692
setHighlighted(const QPoint & cell,bool isHighlighted)693 void SheetView::setHighlighted(const QPoint &cell, bool isHighlighted)
694 {
695 #ifdef CALLIGRA_SHEETS_MT
696 QWriteLocker(&d->highlightLock);
697 #endif
698 bool oldHadHighlights = d->highlightedCells.count() > 0;
699 bool oldVal;
700 if (isHighlighted) {
701 oldVal = d->highlightedCells.insert(cell.x(), cell.y(), true);
702 } else {
703 oldVal = d->highlightedCells.take(cell.x(), cell.y());
704 }
705 if (oldHadHighlights != (d->highlightedCells.count() > 0)) {
706 invalidate();
707 } else if (oldVal != isHighlighted) {
708 invalidateRegion(Region(cell));
709 }
710 }
711
hasHighlightedCells() const712 bool SheetView::hasHighlightedCells() const
713 {
714 #ifdef CALLIGRA_SHEETS_MT
715 QReadLocker(&d->highlightLock);
716 #endif
717 return d->highlightedCells.count() > 0;
718 }
719
clearHighlightedCells()720 void SheetView::clearHighlightedCells()
721 {
722 #ifdef CALLIGRA_SHEETS_MT
723 QWriteLocker(&d->highlightLock);
724 #endif
725 d->activeHighlight = QPoint();
726 if (d->highlightedCells.count()) {
727 d->highlightedCells.clear();
728 invalidate();
729 }
730 }
731
activeHighlight() const732 QPoint SheetView::activeHighlight() const
733 {
734 return d->activeHighlight;
735 }
736
setActiveHighlight(const QPoint & cell)737 void SheetView::setActiveHighlight(const QPoint &cell)
738 {
739 QPoint oldVal = d->activeHighlight;
740 d->activeHighlight = cell;
741 if (oldVal != cell) {
742 Region r;
743 if (!oldVal.isNull()) r.add(oldVal);
744 if (!cell.isNull()) r.add(cell);
745 invalidateRegion(r);
746 }
747 }
748
setHighlightColor(const QColor & color)749 void SheetView::setHighlightColor(const QColor &color)
750 {
751 d->highlightColor = color;
752 if (hasHighlightedCells()) {
753 invalidate();
754 }
755 }
756
setHighlightMaskColor(const QColor & color)757 void SheetView::setHighlightMaskColor(const QColor &color)
758 {
759 d->highlightMaskColor = color;
760 if (hasHighlightedCells()) {
761 invalidate();
762 }
763 }
764
setActiveHighlightColor(const QColor & color)765 void SheetView::setActiveHighlightColor(const QColor &color)
766 {
767 d->activeHighlightColor = color;
768 if (hasHighlightedCells()) {
769 invalidate();
770 }
771 }
772