File indexing completed on 2024-05-12 16:36:10
0001 /* This file is part of the KDE project 0002 Copyright 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LIB. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 Boston, MA 02110-1301, USA. 0018 */ 0019 0020 // Local 0021 #include "SheetView.h" 0022 0023 #include <QCache> 0024 #include <QRect> 0025 #include <QPainter> 0026 #include <QPainterPath> 0027 #ifdef CALLIGRA_SHEETS_MT 0028 #include <QMutex> 0029 #include <QMutexLocker> 0030 #include <QReadWriteLock> 0031 #include <QReadLocker> 0032 #include <QWriteLocker> 0033 #endif 0034 0035 #include <KoViewConverter.h> 0036 0037 #include "CellView.h" 0038 #include "calligra_sheets_limits.h" 0039 #include "PointStorage.h" 0040 #include "RectStorage.h" 0041 #include "Region.h" 0042 #include "RowColumnFormat.h" 0043 #include "RowFormatStorage.h" 0044 #include "Sheet.h" 0045 0046 using namespace Calligra::Sheets; 0047 0048 struct CellPaintData 0049 { 0050 CellPaintData(const CellView &cellView, const Cell &cell, const QPointF &coordinate) 0051 : cellView(cellView) 0052 , cell(cell) 0053 , coordinate(coordinate) 0054 {} 0055 CellView cellView; 0056 Cell cell; 0057 QPointF coordinate; 0058 }; 0059 0060 class Q_DECL_HIDDEN SheetView::Private 0061 { 0062 public: 0063 Private() 0064 #ifdef CALLIGRA_SHEETS_MT 0065 : cacheMutex(QMutex::Recursive) 0066 #endif 0067 {} 0068 const Sheet* sheet; 0069 const KoViewConverter* viewConverter; 0070 QRect visibleRect; 0071 QCache<QPoint, CellView> cache; 0072 #ifdef CALLIGRA_SHEETS_MT 0073 QMutex cacheMutex; 0074 #endif 0075 QRegion cachedArea; 0076 CellView* defaultCellView; 0077 // The maximum accessed cell range used for the scrollbar ranges. 0078 QSize accessedCellRange; 0079 FusionStorage* obscuredInfo; 0080 QSize obscuredRange; // size of the bounding box of obscuredInfo 0081 #ifdef CALLIGRA_SHEETS_MT 0082 QReadWriteLock obscuredLock; 0083 #endif 0084 0085 PointStorage<bool> highlightedCells; 0086 QPoint activeHighlight; 0087 #ifdef CALLIGRA_SHEETS_MT 0088 QReadWriteLock highlightLock; 0089 #endif 0090 QColor highlightColor; 0091 QColor highlightMaskColor; 0092 QColor activeHighlightColor; 0093 public: 0094 Cell cellToProcess(int col, int row, QPointF& coordinate, QSet<Cell>& processedMergedCells, const QRect& visRect); 0095 #ifdef CALLIGRA_SHEETS_MT 0096 CellView cellViewToProcess(Cell& cell, QPointF& coordinate, QSet<Cell>& processedObscuredCells, 0097 SheetView* sheetView, const QRect& visRect); 0098 #else 0099 const CellView& cellViewToProcess(Cell& cell, QPointF& coordinate, QSet<Cell>& processedObscuredCells, 0100 SheetView* sheetView, const QRect& visRect); 0101 #endif 0102 }; 0103 0104 Cell SheetView::Private::cellToProcess(int col, int row, QPointF& coordinate, 0105 QSet<Cell>& processedMergedCells, 0106 const QRect& visRect) 0107 { 0108 Cell cell(sheet, col, row); 0109 if (cell.isPartOfMerged()) { 0110 cell = cell.masterCell(); 0111 // if the rect of visible cells contains this master cell, it was already painted 0112 if (visRect.contains(cell.cellPosition())) { 0113 coordinate.setY(coordinate.y() + sheet->rowFormats()->rowHeight(row)); 0114 return Cell(); // next row 0115 } 0116 // if the out of bounds master cell was already painted, there's nothing more to do 0117 if (processedMergedCells.contains(cell)) { 0118 coordinate.setY(coordinate.y() + sheet->rowFormats()->rowHeight(row)); 0119 return Cell(); // next row 0120 } 0121 processedMergedCells.insert(cell); 0122 // take the coordinate of the master cell 0123 if (sheet->layoutDirection() == Qt::RightToLeft) { 0124 for (int c = cell.column()+1; c <= col; ++c) 0125 coordinate.setX(coordinate.x() + sheet->columnFormat(c)->width()); 0126 } else { 0127 for (int c = cell.column(); c < col; ++c) 0128 coordinate.setX(coordinate.x() - sheet->columnFormat(c)->width()); 0129 } 0130 for (int r = cell.row(); r < row; ++r) 0131 coordinate.setY(coordinate.y() - sheet->rowFormats()->rowHeight(r)); 0132 } 0133 return cell; 0134 } 0135 0136 #ifdef CALLIGRA_SHEETS_MT 0137 CellView SheetView::Private::cellViewToProcess(Cell& cell, QPointF& coordinate, 0138 QSet<Cell>& processedObscuredCells, SheetView* sheetView, const QRect& visRect) 0139 #else 0140 const CellView& SheetView::Private::cellViewToProcess(Cell& cell, QPointF& coordinate, 0141 QSet<Cell>& processedObscuredCells, SheetView* sheetView, const QRect& visRect) 0142 #endif 0143 { 0144 const int col = cell.column(); 0145 const int row = cell.row(); 0146 const QPoint cellPos = cell.cellPosition(); 0147 #ifdef CALLIGRA_SHEETS_MT 0148 CellView cellView = sheetView->cellView(col, row); 0149 #else 0150 const CellView& cellView = sheetView->cellView(col, row); 0151 #endif 0152 if (sheetView->isObscured(cellPos)) { 0153 // if the rect of visible cells contains the obscuring cell, it was already painted 0154 if (visRect.contains(sheetView->obscuringCell(cellPos))) { 0155 coordinate.setY(coordinate.y() + sheet->rowFormats()->rowHeight(row)); 0156 cell = Cell(); 0157 return cellView; // next row 0158 } 0159 cell = Cell(sheet, sheetView->obscuringCell(cellPos)); 0160 if (processedObscuredCells.contains(cell)) { 0161 coordinate.setY(coordinate.y() + sheet->rowFormats()->rowHeight(row)); 0162 cell = Cell(); 0163 return cellView; // next row 0164 } 0165 processedObscuredCells.insert(cell); 0166 // take the coordinate of the obscuring cell 0167 if (sheet->layoutDirection() == Qt::RightToLeft) { 0168 for (int c = cell.column()+1; c <= col; ++c) 0169 coordinate.setX(coordinate.x() + sheet->columnFormat(c)->width()); 0170 } else { 0171 for (int c = cell.column(); c < col; ++c) 0172 coordinate.setX(coordinate.x() - sheet->columnFormat(c)->width()); 0173 } 0174 for (int r = cell.row(); r < row; ++r) 0175 coordinate.setY(coordinate.y() - sheet->rowFormats()->rowHeight(r)); 0176 // use the CellView of the obscuring cell 0177 return sheetView->cellView(cell.column(), cell.row()); 0178 } 0179 return cellView; 0180 } 0181 0182 0183 SheetView::SheetView(const Sheet* sheet) 0184 : QObject(const_cast<Sheet*>(sheet)) 0185 , d(new Private) 0186 { 0187 d->sheet = sheet; 0188 d->viewConverter = 0; 0189 d->visibleRect = QRect(1, 1, 0, 0); 0190 d->cache.setMaxCost(10000); 0191 d->defaultCellView = createDefaultCellView(); 0192 d->accessedCellRange = sheet->usedArea().size().expandedTo(QSize(256, 256)); 0193 d->obscuredInfo = new FusionStorage(sheet->map()); 0194 d->obscuredRange = QSize(0, 0); 0195 d->highlightMaskColor = QColor(0, 0, 0, 128); 0196 d->activeHighlightColor = QColor(255, 127, 0, 128); 0197 } 0198 0199 SheetView::~SheetView() 0200 { 0201 delete d->defaultCellView; 0202 delete d->obscuredInfo; 0203 delete d; 0204 } 0205 0206 const Sheet* SheetView::sheet() const 0207 { 0208 return d->sheet; 0209 } 0210 0211 void SheetView::setViewConverter(const KoViewConverter* viewConverter) 0212 { 0213 Q_ASSERT(viewConverter); 0214 d->viewConverter = viewConverter; 0215 } 0216 0217 const KoViewConverter* SheetView::viewConverter() const 0218 { 0219 Q_ASSERT(d->viewConverter); 0220 return d->viewConverter; 0221 } 0222 0223 #ifdef CALLIGRA_SHEETS_MT 0224 CellView SheetView::cellView(const QPoint& pos) 0225 #else 0226 const CellView& SheetView::cellView(const QPoint& pos) 0227 #endif 0228 { 0229 return cellView(pos.x(), pos.y()); 0230 } 0231 0232 #ifdef CALLIGRA_SHEETS_MT 0233 CellView SheetView::cellView(int col, int row) 0234 #else 0235 const CellView& SheetView::cellView(int col, int row) 0236 #endif 0237 { 0238 Q_ASSERT(1 <= col && col <= KS_colMax); 0239 Q_ASSERT(1 <= row && col <= KS_rowMax); 0240 #ifdef CALLIGRA_SHEETS_MT 0241 QMutexLocker ml(&d->cacheMutex); 0242 #endif 0243 CellView *v = d->cache.object(QPoint(col, row)); 0244 if (!v) { 0245 v = createCellView(col, row); 0246 d->cache.insert(QPoint(col, row), v); 0247 d->cachedArea += QRect(col, row, 1, 1); 0248 } 0249 #ifdef CALLIGRA_SHEETS_MT 0250 // create a copy as long as the mutex is locked 0251 CellView cellViewCopy = *v; 0252 return cellViewCopy; 0253 #else 0254 return *v; 0255 #endif 0256 } 0257 0258 void SheetView::setPaintCellRange(const QRect& rect) 0259 { 0260 #ifdef CALLIGRA_SHEETS_MT 0261 QMutexLocker ml(&d->cacheMutex); 0262 #endif 0263 d->visibleRect = rect & QRect(1, 1, KS_colMax, KS_rowMax); 0264 d->cache.setMaxCost(2 * rect.width() * rect.height()); 0265 } 0266 0267 QRect SheetView::paintCellRange() const 0268 { 0269 return d->visibleRect; 0270 } 0271 0272 void SheetView::invalidateRegion(const Region& region) 0273 { 0274 QRegion qregion; 0275 Region::ConstIterator end(region.constEnd()); 0276 for (Region::ConstIterator it(region.constBegin()); it != end; ++it) { 0277 qregion += (*it)->rect(); 0278 } 0279 // reduce to the cached area 0280 qregion &= d->cachedArea; 0281 QVector<QRect> rects = qregion.rects(); 0282 for (int i = 0; i < rects.count(); ++i) 0283 invalidateRange(rects[i]); 0284 } 0285 0286 void SheetView::invalidate() 0287 { 0288 #ifdef CALLIGRA_SHEETS_MT 0289 QMutexLocker ml(&d->cacheMutex); 0290 #endif 0291 delete d->defaultCellView; 0292 d->defaultCellView = createDefaultCellView(); 0293 d->cache.clear(); 0294 d->cachedArea = QRegion(); 0295 delete d->obscuredInfo; 0296 d->obscuredInfo = new FusionStorage(d->sheet->map()); 0297 d->obscuredRange = QSize(0, 0); 0298 } 0299 0300 void SheetView::paintCells(QPainter& painter, const QRectF& paintRect, const QPointF& topLeft, CanvasBase*, const QRect& visibleRect) 0301 { 0302 const QRect& visRect = visibleRect.isValid() ? visibleRect : d->visibleRect; 0303 // paintRect: the canvas area, that should be painted; in document coordinates; 0304 // no layout direction consideration; scrolling offset applied; 0305 // independent from painter transformations 0306 // topLeft: the document coordinate of the top left cell's top left corner; 0307 // no layout direction consideration; independent from painter 0308 // transformations 0309 0310 // NOTE Stefan: The painting is split into several steps. In each of these all cells in 0311 // d->visibleRect are traversed. This may appear suboptimal at the first look, but 0312 // ensures that the borders are not erased by the background of adjacent cells. 0313 0314 // debugSheets << "paintRect:" << paintRect; 0315 // debugSheets << "topLeft:" << topLeft; 0316 0317 QRegion clipRect(painter.clipRegion()); 0318 // 0. Paint the sheet background 0319 if (!sheet()->backgroundImage().isNull()) { 0320 //TODO support all the different properties 0321 Sheet::BackgroundImageProperties properties = sheet()->backgroundImageProperties(); 0322 if( properties.repeat == Sheet::BackgroundImageProperties::Repeat ) { 0323 const int firstCol = visRect.left(); 0324 const int firstRow = visRect.top(); 0325 const int firstColPosition = d->sheet->columnPosition(firstCol); 0326 const int firstRowPosition = d->sheet->rowPosition(firstRow); 0327 0328 const int imageWidth = sheet()->backgroundImage().rect().width(); 0329 const int imageHeight = sheet()->backgroundImage().rect().height(); 0330 0331 int xBackground = firstColPosition - (firstColPosition % imageWidth); 0332 int yBackground = firstRowPosition - (firstRowPosition % imageHeight); 0333 0334 const int lastCol = visRect.right(); 0335 const int lastRow = visRect.bottom(); 0336 const int lastColPosition = d->sheet->columnPosition(lastCol); 0337 const int lastRowPosition = d->sheet->rowPosition(lastRow); 0338 0339 while( xBackground < lastColPosition ) { 0340 int y = yBackground; 0341 while( y < lastRowPosition ) { 0342 painter.drawImage(QRect(xBackground, y, imageWidth, imageHeight), sheet()->backgroundImage()); 0343 y += imageHeight; 0344 } 0345 xBackground += imageWidth; 0346 } 0347 } 0348 } 0349 0350 // 1. Paint the cell background 0351 0352 // Handle right-to-left layout. 0353 // In an RTL sheet the cells have to be painted at their opposite horizontal 0354 // location on the canvas, meaning that column A will be the rightmost column 0355 // on screen, column B will be to the left of it and so on. Here we change 0356 // the horizontal coordinate at which we start painting the cell in case the 0357 // sheet's direction is RTL. 0358 const bool rightToLeft = sheet()->layoutDirection() == Qt::RightToLeft; 0359 const QPointF startCoordinate(rightToLeft ? paintRect.width() - topLeft.x() : topLeft.x(), topLeft.y()); 0360 QPointF coordinate(startCoordinate); 0361 // debugSheets << "start coordinate:" << coordinate; 0362 QSet<Cell> processedMergedCells; 0363 QSet<Cell> processedObscuredCells; 0364 QList<CellPaintData> cached_cells; 0365 for (int col = visRect.left(); col <= visRect.right(); ++col) { 0366 if (d->sheet->columnFormat(col)->isHiddenOrFiltered()) 0367 continue; 0368 if (rightToLeft) 0369 coordinate.setX(coordinate.x() - d->sheet->columnFormat(col)->width()); 0370 // debugSheets <<"coordinate:" << coordinate; 0371 for (int row = visRect.top(); row <= visRect.bottom(); ++row) { 0372 int lastHiddenRow; 0373 if (d->sheet->rowFormats()->isHiddenOrFiltered(row, &lastHiddenRow)) { 0374 row = lastHiddenRow; 0375 continue; 0376 } 0377 // save the coordinate 0378 const QPointF savedCoordinate = coordinate; 0379 // figure out, if any and which cell has to be painted (may be a master cell) 0380 Cell cell = d->cellToProcess(col, row, coordinate, processedMergedCells, visRect); 0381 if (!cell) 0382 continue; 0383 // figure out, which CellView to use (may be one for an obscuring cell) 0384 CellPaintData cpd(d->cellViewToProcess(cell, coordinate, processedObscuredCells, this, visRect), cell, coordinate); 0385 if (!cell) 0386 continue; 0387 cpd.cellView.paintCellBackground(painter, clipRect, coordinate); 0388 cached_cells.append(cpd); 0389 // restore coordinate 0390 coordinate = savedCoordinate; 0391 coordinate.setY(coordinate.y() + d->sheet->rowFormats()->rowHeight(row)); 0392 } 0393 coordinate.setY(topLeft.y()); 0394 if (!rightToLeft) 0395 coordinate.setX(coordinate.x() + d->sheet->columnFormat(col)->width()); 0396 } 0397 0398 // 2. Paint the cell content including markers (formula, comment, ...) 0399 for (QList<CellPaintData>::ConstIterator it(cached_cells.constBegin()); it != cached_cells.constEnd(); ++it) { 0400 it->cellView.paintCellContents(paintRect, painter, clipRect, it->coordinate, it->cell, this); 0401 } 0402 0403 // 3. Paint the default borders 0404 coordinate = startCoordinate; 0405 processedMergedCells.clear(); 0406 for (int col = visRect.left(); col <= visRect.right(); ++col) { 0407 if (d->sheet->columnFormat(col)->isHiddenOrFiltered()) 0408 continue; 0409 if (rightToLeft) 0410 coordinate.setX(coordinate.x() - d->sheet->columnFormat(col)->width()); 0411 for (int row = visRect.top(); row <= visRect.bottom(); ++row) { 0412 int lastHiddenRow; 0413 if (d->sheet->rowFormats()->isHiddenOrFiltered(row, &lastHiddenRow)) { 0414 row = lastHiddenRow; 0415 continue; 0416 } 0417 // For borders even cells, that are merged in, need to be traversed. 0418 // Think of a merged cell with a set border and one its neighbours has a thicker border. 0419 // but: also the master cell of a merged cell always needs to be processed 0420 const QPointF savedCoordinate = coordinate; 0421 Cell cell = d->cellToProcess(col, row, coordinate, processedMergedCells, visRect); 0422 if (!!cell && (cell.column() != col || cell.row() != row)) { 0423 const CellView cellView = this->cellView(cell.cellPosition()); 0424 cellView.paintDefaultBorders(painter, clipRect, paintRect, coordinate, 0425 CellView::LeftBorder | CellView::RightBorder | 0426 CellView::TopBorder | CellView::BottomBorder, 0427 visRect, cell, this); 0428 } 0429 coordinate = savedCoordinate; 0430 const CellView cellView = this->cellView(col, row); 0431 cellView.paintDefaultBorders(painter, clipRect, paintRect, coordinate, 0432 CellView::LeftBorder | CellView::RightBorder | 0433 CellView::TopBorder | CellView::BottomBorder, 0434 visRect, Cell(d->sheet, col, row), this); 0435 coordinate.setY(coordinate.y() + d->sheet->rowFormats()->rowHeight(row)); 0436 } 0437 coordinate.setY(topLeft.y()); 0438 if (!rightToLeft) 0439 coordinate.setX(coordinate.x() + d->sheet->columnFormat(col)->width()); 0440 } 0441 0442 // 4. Paint the custom borders, diagonal lines and page borders 0443 coordinate = startCoordinate; 0444 processedMergedCells.clear(); 0445 processedObscuredCells.clear(); 0446 for (int col = visRect.left(); col <= visRect.right(); ++col) { 0447 if (d->sheet->columnFormat(col)->isHiddenOrFiltered()) 0448 continue; 0449 if (rightToLeft) 0450 coordinate.setX(coordinate.x() - d->sheet->columnFormat(col)->width()); 0451 for (int row = visRect.top(); row <= visRect.bottom(); ++row) { 0452 int lastHiddenRow; 0453 if (d->sheet->rowFormats()->isHiddenOrFiltered(row, &lastHiddenRow)) { 0454 row = lastHiddenRow; 0455 continue; 0456 } 0457 // For borders even cells, that are merged in, need to be traversed. 0458 // Think of a merged cell with a set border and one its neighbours has a thicker border. 0459 // but: also the master cell of a merged cell always needs to be processed 0460 const QPointF savedCoordinate = coordinate; 0461 Cell cell = d->cellToProcess(col, row, coordinate, processedMergedCells, visRect); 0462 if (!!cell && (cell.column() != col || cell.row() != row)) { 0463 const CellView cellView = this->cellView(cell.cellPosition()); 0464 cellView.paintCellBorders(paintRect, painter, clipRect, coordinate, 0465 visRect, 0466 cell, this); 0467 } 0468 coordinate = savedCoordinate; 0469 Cell theCell(sheet(), col, row); 0470 const CellView cellView = d->cellViewToProcess(theCell, coordinate, processedObscuredCells, this, visRect); 0471 if (!!theCell && (theCell.column() != col || theCell.row() != row)) { 0472 cellView.paintCellBorders(paintRect, painter, clipRect, coordinate, 0473 visRect, 0474 theCell, this); 0475 } 0476 const CellView cellView2 = this->cellView(col, row); 0477 coordinate = savedCoordinate; 0478 cellView2.paintCellBorders(paintRect, painter, clipRect, coordinate, 0479 visRect, 0480 Cell(sheet(), col, row), this); 0481 coordinate.setY(coordinate.y() + d->sheet->rowFormats()->rowHeight(row)); 0482 } 0483 coordinate.setY(topLeft.y()); 0484 if (!rightToLeft) 0485 coordinate.setX(coordinate.x() + d->sheet->columnFormat(col)->width()); 0486 } 0487 0488 // 5. Paint cell highlighting 0489 if (hasHighlightedCells()) { 0490 QPointF active = activeHighlight(); 0491 QPainterPath p; 0492 const CellPaintData* activeData = 0; 0493 for (QList<CellPaintData>::ConstIterator it(cached_cells.constBegin()); it != cached_cells.constEnd(); ++it) { 0494 if (isHighlighted(it->cell.cellPosition())) { 0495 p.addRect(it->coordinate.x(), it->coordinate.y(), it->cellView.cellWidth(), it->cellView.cellHeight()); 0496 if (it->cell.cellPosition() == active) { 0497 activeData = &*it; 0498 } 0499 } 0500 } 0501 painter.setPen(Qt::NoPen); 0502 if (d->highlightColor.isValid()) { 0503 painter.setBrush(QBrush(d->highlightColor)); 0504 painter.drawPath(p); 0505 } 0506 if (d->highlightMaskColor.isValid()) { 0507 QPainterPath base; 0508 base.addRect(painter.clipPath().boundingRect().adjusted(-5, -5, 5, 5)); 0509 p = base.subtracted(p); 0510 painter.setBrush(QBrush(d->highlightMaskColor)); 0511 painter.drawPath(p); 0512 } 0513 0514 if (activeData && d->activeHighlightColor.isValid()) { 0515 painter.setBrush(QBrush(d->activeHighlightColor)); 0516 painter.setPen(QPen(Qt::black, 0)); 0517 painter.drawRect(QRectF(activeData->coordinate.x(), activeData->coordinate.y(), activeData->cellView.cellWidth(), activeData->cellView.cellHeight())); 0518 } 0519 } 0520 } 0521 0522 void SheetView::invalidateRange(const QRect& range) 0523 { 0524 #ifdef CALLIGRA_SHEETS_MT 0525 QMutexLocker ml(&d->cacheMutex); 0526 #endif 0527 QRegion obscuredRegion; 0528 const int right = range.right(); 0529 for (int col = range.left(); col <= right; ++col) { 0530 const int bottom = range.bottom(); 0531 for (int row = range.top(); row <= bottom; ++row) { 0532 const QPoint p(col, row); 0533 if (!d->cache.contains(p)) 0534 continue; 0535 if (obscuresCells(p) || isObscured(p)) { 0536 obscuredRegion += obscuredArea(p); 0537 obscureCells(p, 0, 0); 0538 } 0539 d->cache.remove(p); 0540 } 0541 } 0542 d->cachedArea -= range; 0543 obscuredRegion &= d->cachedArea; 0544 foreach (const QRect& rect, obscuredRegion.rects()) { 0545 invalidateRange(rect); 0546 } 0547 } 0548 0549 void SheetView::obscureCells(const QPoint &position, int numXCells, int numYCells) 0550 { 0551 #ifdef CALLIGRA_SHEETS_MT 0552 QWriteLocker(&d->obscuredLock); 0553 #endif 0554 // Start by un-obscuring cells that we might be obscuring right now 0555 const QPair<QRectF, bool> pair = d->obscuredInfo->containedPair(position); 0556 if (!pair.first.isNull()) 0557 d->obscuredInfo->insert(Region(pair.first.toRect()), false); 0558 // Obscure the cells 0559 if (numXCells != 0 || numYCells != 0) 0560 d->obscuredInfo->insert(Region(position.x(), position.y(), numXCells + 1, numYCells + 1), true); 0561 0562 QRect obscuredArea = d->obscuredInfo->usedArea(); 0563 QSize newObscuredRange(obscuredArea.right(), obscuredArea.bottom()); 0564 if (newObscuredRange != d->obscuredRange) { 0565 d->obscuredRange = newObscuredRange; 0566 emit obscuredRangeChanged(d->obscuredRange); 0567 } 0568 } 0569 0570 QPoint SheetView::obscuringCell(const QPoint &obscuredCell) const 0571 { 0572 #ifdef CALLIGRA_SHEETS_MT 0573 QReadLocker(&d->obscuredLock); 0574 #endif 0575 const QPair<QRectF, bool> pair = d->obscuredInfo->containedPair(obscuredCell); 0576 if (pair.first.isNull()) 0577 return obscuredCell; 0578 if (pair.second == false) 0579 return obscuredCell; 0580 return pair.first.toRect().topLeft(); 0581 } 0582 0583 QSize SheetView::obscuredRange(const QPoint &obscuringCell) const 0584 { 0585 #ifdef CALLIGRA_SHEETS_MT 0586 QReadLocker(&d->obscuredLock); 0587 #endif 0588 const QPair<QRectF, bool> pair = d->obscuredInfo->containedPair(obscuringCell); 0589 if (pair.first.isNull()) 0590 return QSize(0, 0); 0591 if (pair.second == false) 0592 return QSize(0, 0); 0593 // Not the master cell? 0594 if (pair.first.toRect().topLeft() != obscuringCell) 0595 return QSize(0, 0); 0596 return pair.first.toRect().size() - QSize(1, 1); 0597 } 0598 0599 QRect SheetView::obscuredArea(const QPoint &cell) const 0600 { 0601 #ifdef CALLIGRA_SHEETS_MT 0602 QReadLocker(&d->obscuredLock); 0603 #endif 0604 const QPair<QRectF, bool> pair = d->obscuredInfo->containedPair(cell); 0605 if (pair.first.isNull()) 0606 return QRect(cell, QSize(1, 1)); 0607 if (pair.second == false) 0608 return QRect(cell, QSize(1, 1)); 0609 // Not the master cell? 0610 return pair.first.toRect(); 0611 } 0612 0613 bool SheetView::isObscured(const QPoint &cell) const 0614 { 0615 #ifdef CALLIGRA_SHEETS_MT 0616 QReadLocker(&d->obscuredLock); 0617 #endif 0618 const QPair<QRectF, bool> pair = d->obscuredInfo->containedPair(cell); 0619 if (pair.first.isNull()) 0620 return false; 0621 if (pair.second == false) 0622 return false; 0623 // master cell? 0624 if (pair.first.toRect().topLeft() == cell) 0625 return false; 0626 return true; 0627 } 0628 0629 bool SheetView::obscuresCells(const QPoint &cell) const 0630 { 0631 #ifdef CALLIGRA_SHEETS_MT 0632 QReadLocker(&d->obscuredLock); 0633 #endif 0634 const QPair<QRectF, bool> pair = d->obscuredInfo->containedPair(cell); 0635 if (pair.first.isNull()) 0636 return false; 0637 if (pair.second == false) 0638 return false; 0639 // master cell? 0640 if (pair.first.toRect().topLeft() != cell) 0641 return false; 0642 return true; 0643 } 0644 0645 QSize SheetView::totalObscuredRange() const 0646 { 0647 #ifdef CALLIGRA_SHEETS_MT 0648 QReadLocker(&d->obscuredLock); 0649 #endif 0650 return d->obscuredRange; 0651 } 0652 0653 #ifdef CALLIGRA_SHEETS_MT 0654 CellView SheetView::defaultCellView() const 0655 #else 0656 const CellView& SheetView::defaultCellView() const 0657 #endif 0658 { 0659 return *d->defaultCellView; 0660 } 0661 0662 void SheetView::updateAccessedCellRange(const QPoint& location) 0663 { 0664 const QSize cellRange = d->accessedCellRange.expandedTo(QSize(location.x(), location.y())); 0665 if (d->accessedCellRange != cellRange || location.isNull()) { 0666 d->accessedCellRange = cellRange; 0667 const int col = qMin(KS_colMax, cellRange.width() + 10); 0668 const int row = qMin(KS_rowMax, cellRange.height() + 10); 0669 const double width = sheet()->columnPosition(col) + sheet()->columnFormat(col)->width(); 0670 const double height = sheet()->rowPosition(row) + sheet()->rowFormats()->rowHeight(row); 0671 emit visibleSizeChanged(QSizeF(width, height)); 0672 } 0673 } 0674 0675 CellView* SheetView::createDefaultCellView() 0676 { 0677 return new CellView(this); 0678 } 0679 0680 CellView* SheetView::createCellView(int col, int row) 0681 { 0682 return new CellView(this, col, row); 0683 } 0684 0685 bool SheetView::isHighlighted(const QPoint &cell) const 0686 { 0687 #ifdef CALLIGRA_SHEETS_MT 0688 QReadLocker(&d->highlightLock); 0689 #endif 0690 return d->highlightedCells.lookup(cell.x(), cell.y()); 0691 } 0692 0693 void SheetView::setHighlighted(const QPoint &cell, bool isHighlighted) 0694 { 0695 #ifdef CALLIGRA_SHEETS_MT 0696 QWriteLocker(&d->highlightLock); 0697 #endif 0698 bool oldHadHighlights = d->highlightedCells.count() > 0; 0699 bool oldVal; 0700 if (isHighlighted) { 0701 oldVal = d->highlightedCells.insert(cell.x(), cell.y(), true); 0702 } else { 0703 oldVal = d->highlightedCells.take(cell.x(), cell.y()); 0704 } 0705 if (oldHadHighlights != (d->highlightedCells.count() > 0)) { 0706 invalidate(); 0707 } else if (oldVal != isHighlighted) { 0708 invalidateRegion(Region(cell)); 0709 } 0710 } 0711 0712 bool SheetView::hasHighlightedCells() const 0713 { 0714 #ifdef CALLIGRA_SHEETS_MT 0715 QReadLocker(&d->highlightLock); 0716 #endif 0717 return d->highlightedCells.count() > 0; 0718 } 0719 0720 void SheetView::clearHighlightedCells() 0721 { 0722 #ifdef CALLIGRA_SHEETS_MT 0723 QWriteLocker(&d->highlightLock); 0724 #endif 0725 d->activeHighlight = QPoint(); 0726 if (d->highlightedCells.count()) { 0727 d->highlightedCells.clear(); 0728 invalidate(); 0729 } 0730 } 0731 0732 QPoint SheetView::activeHighlight() const 0733 { 0734 return d->activeHighlight; 0735 } 0736 0737 void SheetView::setActiveHighlight(const QPoint &cell) 0738 { 0739 QPoint oldVal = d->activeHighlight; 0740 d->activeHighlight = cell; 0741 if (oldVal != cell) { 0742 Region r; 0743 if (!oldVal.isNull()) r.add(oldVal); 0744 if (!cell.isNull()) r.add(cell); 0745 invalidateRegion(r); 0746 } 0747 } 0748 0749 void SheetView::setHighlightColor(const QColor &color) 0750 { 0751 d->highlightColor = color; 0752 if (hasHighlightedCells()) { 0753 invalidate(); 0754 } 0755 } 0756 0757 void SheetView::setHighlightMaskColor(const QColor &color) 0758 { 0759 d->highlightMaskColor = color; 0760 if (hasHighlightedCells()) { 0761 invalidate(); 0762 } 0763 } 0764 0765 void SheetView::setActiveHighlightColor(const QColor &color) 0766 { 0767 d->activeHighlightColor = color; 0768 if (hasHighlightedCells()) { 0769 invalidate(); 0770 } 0771 }