File indexing completed on 2024-05-12 16:36:10
0001 /* This file is part of the KDE project 0002 Copyright (C) 1998, 1999 Torben Weis <weis@kde.org> 0003 Copyright (C) 2005-2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Library General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Library General Public License for more details. 0014 0015 You should have received a copy of the GNU Library General Public License 0016 along with this library; see the file COPYING.LIB. If not, write to 0017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 Boston, MA 02110-1301, USA. 0019 */ 0020 0021 // Local 0022 #include "Selection.h" 0023 0024 #include <KoCanvasBase.h> 0025 #include <KoCanvasController.h> 0026 #include <KoViewConverter.h> 0027 0028 #include "SheetsDebug.h" 0029 #include "Cell.h" 0030 #include "CellStorage.h" 0031 #include "RowColumnFormat.h" 0032 #include "RowFormatStorage.h" 0033 #include "Sheet.h" 0034 0035 #include "commands/DataManipulators.h" 0036 0037 #include "ui/CellEditor.h" 0038 0039 using namespace Calligra::Sheets; 0040 0041 // TODO 0042 // - Allow resizing of all ranges in a normal selection; not just the last one. 0043 // - Get rid of anchor and marker. They are the corners of the active element. 0044 0045 0046 /*************************************************************************** 0047 class Selection::Private 0048 ****************************************************************************/ 0049 0050 class Q_DECL_HIDDEN Selection::Private 0051 { 0052 public: 0053 Private() { 0054 activeSheet = 0; 0055 originSheet = 0; 0056 anchor = QPoint(1, 1); 0057 cursor = QPoint(1, 1); 0058 marker = QPoint(1, 1); 0059 0060 colors.push_back(Qt::red); 0061 colors.push_back(Qt::blue); 0062 colors.push_back(Qt::magenta); 0063 colors.push_back(Qt::darkRed); 0064 colors.push_back(Qt::darkGreen); 0065 colors.push_back(Qt::darkMagenta); 0066 colors.push_back(Qt::darkCyan); 0067 colors.push_back(Qt::darkYellow); 0068 0069 multipleOccurences = false; 0070 selectionMode = MultipleCells; 0071 0072 activeElement = 1; 0073 activeSubRegionStart = 0; 0074 activeSubRegionLength = 1; 0075 0076 canvasBase = 0; 0077 referenceMode = false; 0078 } 0079 0080 Sheet* activeSheet; 0081 Sheet* originSheet; 0082 QPoint anchor; 0083 QPoint cursor; 0084 QPoint marker; 0085 QList<QColor> colors; 0086 0087 bool multipleOccurences : 1; 0088 Mode selectionMode : 2; 0089 0090 // For reference selections this selection represents all references in a 0091 // formula. The user can place the text cursor at any reference while 0092 // editing the formula. Such a reference may not just be a contiguous range, 0093 // but a non-contiguous sub-region. 0094 // (Even though the text delimiter that separates ranges in a sub-region, 0095 // ';', is also used as delimiter for function arguments. Functions, that 0096 // accept two or more adjacent references as arguments cannot cope with 0097 // non-contiguous references for this reason. In this case it's up to the 0098 // user to select references, that serve the function's needs.) 0099 // That's what the next three variables are for. 0100 // For 'normal' selections these variables are actually superfluous, but may 0101 // be used in conjunction with the reference selection where appropriate. 0102 int activeElement; // the active range in a referenced sub-region 0103 int activeSubRegionStart; // the start of a referenced sub-region 0104 int activeSubRegionLength; // the length of a referenced sub-region 0105 0106 KoCanvasBase* canvasBase; 0107 bool referenceMode : 1; 0108 Region formerSelection; // for reference selection mode 0109 Region oldSelection; // for select all 0110 }; 0111 0112 /*************************************************************************** 0113 class Selection 0114 ****************************************************************************/ 0115 0116 Selection::Selection(KoCanvasBase* canvasBase) 0117 : KoToolSelection(0) 0118 , Region(1, 1) 0119 , d(new Private()) 0120 { 0121 d->canvasBase = canvasBase; 0122 } 0123 0124 Selection::Selection(const Selection& selection) 0125 : KoToolSelection(selection.parent()) 0126 , Region() 0127 , d(new Private()) 0128 { 0129 d->activeSheet = selection.d->activeSheet; 0130 d->originSheet = selection.d->originSheet; 0131 d->activeElement = cells().count(); 0132 d->activeSubRegionStart = 0; 0133 d->activeSubRegionLength = cells().count(); 0134 d->canvasBase = selection.d->canvasBase; 0135 } 0136 0137 Selection::~Selection() 0138 { 0139 delete d; 0140 } 0141 0142 KoCanvasBase* Selection::canvas() const 0143 { 0144 return d->canvasBase; 0145 } 0146 0147 void Selection::initialize(const QPoint& point, Sheet* sheet) 0148 { 0149 if (!isValid(point)) 0150 return; 0151 0152 if (!d->activeSheet) 0153 return; 0154 0155 if (!sheet) { 0156 if (d->originSheet) { 0157 sheet = d->originSheet; 0158 } else { 0159 sheet = d->activeSheet; 0160 } 0161 } 0162 0163 Region changedRegion(*this); 0164 changedRegion.add(extendToMergedAreas(QRect(d->anchor, d->marker))); 0165 0166 // for the case of a merged cell 0167 QPoint topLeft(point); 0168 Cell cell(d->activeSheet, point); 0169 if (cell.isPartOfMerged()) { 0170 cell = cell.masterCell(); 0171 topLeft = QPoint(cell.column(), cell.row()); 0172 } 0173 0174 d->anchor = topLeft; 0175 d->cursor = point; 0176 d->marker = topLeft; 0177 0178 fixSubRegionDimension(); // TODO remove this sanity check 0179 int index = d->activeSubRegionStart + d->activeSubRegionLength; 0180 if (insert(index, topLeft, sheet/*, true*/)) { 0181 // if the point was inserted 0182 clearSubRegion(); 0183 // Sets: 0184 // d->activeElement = d->activeSubRegionStart + 1; 0185 // d->activeSubRegionLength = 0; 0186 } else { 0187 warnSheets << "Unable to insert" << topLeft << "in" << sheet->sheetName(); 0188 } 0189 Element* element = cells()[d->activeSubRegionStart]; 0190 // we end up with one element in the subregion 0191 d->activeSubRegionLength = 1; 0192 if (element && element->type() == Element::Point) { 0193 Point* point = static_cast<Point*>(element); 0194 point->setColor(d->colors[cells().size() % d->colors.size()]); 0195 } else if (element && element->type() == Element::Range) { 0196 Range* range = static_cast<Range*>(element); 0197 range->setColor(d->colors[cells().size() % d->colors.size()]); 0198 } 0199 0200 if (changedRegion == *this) { 0201 emitChanged(Region(topLeft, sheet)); 0202 return; 0203 } 0204 changedRegion.add(topLeft, sheet); 0205 0206 emitChanged(changedRegion); 0207 } 0208 0209 void Selection::initialize(const QRect& range, Sheet* sheet) 0210 { 0211 if (!isValid(range) || (range == QRect(0, 0, 1, 1))) 0212 return; 0213 0214 if (!d->activeSheet) 0215 return; 0216 0217 if (d->selectionMode == SingleCell) { 0218 initialize(range.bottomRight(), sheet); 0219 return; 0220 } 0221 0222 if (!sheet) { 0223 if (d->originSheet) { 0224 sheet = d->originSheet; 0225 } else { 0226 sheet = d->activeSheet; 0227 } 0228 } 0229 0230 Region changedRegion(*this); 0231 changedRegion.add(extendToMergedAreas(QRect(d->anchor, d->marker))); 0232 0233 // for the case of a merged cell 0234 QPoint topLeft(range.topLeft()); 0235 Cell cell(d->activeSheet, topLeft); 0236 if (cell.isPartOfMerged()) { 0237 cell = cell.masterCell(); 0238 topLeft = QPoint(cell.column(), cell.row()); 0239 } 0240 0241 // for the case of a merged cell 0242 QPoint bottomRight(range.bottomRight()); 0243 cell = Cell(d->activeSheet, bottomRight); 0244 if (cell.isPartOfMerged()) { 0245 cell = cell.masterCell(); 0246 bottomRight = QPoint(cell.column(), cell.row()); 0247 } 0248 0249 d->anchor = topLeft; 0250 d->cursor = bottomRight; 0251 d->marker = bottomRight; 0252 0253 fixSubRegionDimension(); // TODO remove this sanity check 0254 int index = d->activeSubRegionStart + d->activeSubRegionLength; 0255 if (insert(index, QRect(topLeft, bottomRight), sheet/*, true*/)) { 0256 // if the range was inserted 0257 clearSubRegion(); 0258 // Sets: 0259 // d->activeElement = d->activeSubRegionStart + 1; 0260 // d->activeSubRegionLength = 0; 0261 } else { 0262 warnSheets << "Unable to insert" << topLeft << "in" << sheet->sheetName(); 0263 } 0264 Element* element = cells()[d->activeSubRegionStart]; 0265 // we end up with one element in the subregion 0266 d->activeSubRegionLength = 1; 0267 if (element && element->type() == Element::Point) { 0268 Point* point = static_cast<Point*>(element); 0269 point->setColor(d->colors[cells().size() % d->colors.size()]); 0270 } else if (element && element->type() == Element::Range) { 0271 Range* range = static_cast<Range*>(element); 0272 range->setColor(d->colors[cells().size() % d->colors.size()]); 0273 } 0274 0275 if (changedRegion == *this) { 0276 return; 0277 } 0278 changedRegion.add(QRect(topLeft, bottomRight), sheet); 0279 0280 emitChanged(changedRegion); 0281 } 0282 0283 void Selection::initialize(const Region& region, Sheet* sheet) 0284 { 0285 if (!region.isValid()) 0286 return; 0287 0288 if (d->selectionMode == SingleCell) { 0289 if (!cells().isEmpty()) 0290 initialize(region.firstRange().bottomRight(), sheet); 0291 return; 0292 } 0293 0294 if (!sheet) { 0295 if (d->originSheet) { 0296 sheet = d->originSheet; 0297 } else { 0298 sheet = d->activeSheet; 0299 } 0300 } 0301 0302 Region changedRegion(*this); 0303 changedRegion.add(extendToMergedAreas(QRect(d->anchor, d->marker))); 0304 0305 // TODO Stefan: handle subregion insertion 0306 // TODO Stefan: handle obscured cells correctly 0307 Region::clear(); // all elements; no residuum 0308 Element* element = add(region); 0309 if (element && element->type() == Element::Point) { 0310 Point* point = static_cast<Point*>(element); 0311 point->setColor(d->colors[cells().size() % d->colors.size()]); 0312 } else if (element && element->type() == Element::Range) { 0313 Range* range = static_cast<Range*>(element); 0314 range->setColor(d->colors[cells().size() % d->colors.size()]); 0315 } 0316 0317 // for the case of a merged cell 0318 QPoint topLeft(cells().last()->rect().topLeft()); 0319 Cell cell(d->activeSheet, topLeft); 0320 if (cell.isPartOfMerged()) { 0321 cell = cell.masterCell(); 0322 topLeft = QPoint(cell.column(), cell.row()); 0323 } 0324 0325 // for the case of a merged cell 0326 QPoint bottomRight(cells().last()->rect().bottomRight()); 0327 cell = Cell(d->activeSheet, bottomRight); 0328 if (cell.isPartOfMerged()) { 0329 cell = cell.masterCell(); 0330 bottomRight = QPoint(cell.column(), cell.row()); 0331 } 0332 0333 d->anchor = topLeft; 0334 d->cursor = topLeft; 0335 d->marker = bottomRight; 0336 0337 d->activeElement = cells().count(); 0338 d->activeSubRegionStart = 0; 0339 d->activeSubRegionLength = cells().count(); 0340 0341 if (changedRegion == *this) { 0342 return; 0343 } 0344 changedRegion.add(region); 0345 0346 emitChanged(changedRegion); 0347 } 0348 0349 void Selection::update() 0350 { 0351 emitChanged(*this); 0352 } 0353 0354 void Selection::update(const QPoint& point) 0355 { 0356 if (d->selectionMode == SingleCell) { 0357 initialize(point); 0358 d->activeElement = 1; 0359 d->activeSubRegionStart = 0; 0360 d->activeSubRegionLength = 1; 0361 return; 0362 } 0363 0364 // A length of 0 means inserting at the position d->activeSubRegionStart. 0365 if (d->activeSubRegionLength == 0) { 0366 extend(point); 0367 return; 0368 } 0369 0370 if (cells().isEmpty()) { 0371 initialize(point); 0372 d->activeElement = 1; 0373 d->activeSubRegionStart = 0; 0374 d->activeSubRegionLength = 1; 0375 return; 0376 } 0377 0378 // Take the last range, if pointing beyond the sub-region's end. 0379 const int subRegionEnd = d->activeSubRegionStart + d->activeSubRegionLength; 0380 const bool atEnd = d->activeElement >= subRegionEnd; 0381 if (atEnd) { 0382 // d->activeSubRegionLength == 0 is already excluded. 0383 d->activeElement = subRegionEnd - 1; 0384 } 0385 0386 Sheet* sheet = cells()[d->activeElement]->sheet(); 0387 if (sheet != d->activeSheet) { 0388 extend(point); 0389 d->activeElement = cells().count(); 0390 d->activeSubRegionStart = cells().count() - 1; 0391 d->activeSubRegionLength = 1; 0392 return; 0393 } 0394 0395 // for the case of a merged cell 0396 QPoint topLeft(point); 0397 Cell cell(d->activeSheet, point); 0398 if (cell.isPartOfMerged()) { 0399 cell = cell.masterCell(); 0400 topLeft = QPoint(cell.column(), cell.row()); 0401 } 0402 0403 if (topLeft == d->marker) { 0404 return; 0405 } 0406 0407 QRect area1 = cells()[d->activeElement]->rect(); 0408 QRect newRange = extendToMergedAreas(QRect(d->anchor, topLeft)); 0409 0410 // If the updated range is bigger, it may cover already existing ranges. 0411 // These get removed, if multiple occurrences are not allowed. Store the old 0412 // amount of ranges, to figure out how many ranges have been removed later. 0413 const int count = cells().count(); 0414 // The update may have shrunk the range, which would be contained in 0415 // the former range. Remove the latter before inserting the new range. 0416 delete cells().takeAt(d->activeElement); 0417 insert(d->activeElement, newRange, sheet, d->multipleOccurences); 0418 const int delta = cells().count() - count; 0419 d->activeSubRegionLength += delta; 0420 if (atEnd) { 0421 d->activeElement = d->activeSubRegionStart + d->activeSubRegionLength; 0422 } else { 0423 d->activeElement += delta; 0424 } 0425 0426 QRect area2 = newRange; 0427 Region changedRegion; 0428 0429 bool newLeft = area1.left() != area2.left(); 0430 bool newTop = area1.top() != area2.top(); 0431 bool newRight = area1.right() != area2.right(); 0432 bool newBottom = area1.bottom() != area2.bottom(); 0433 0434 /* first, calculate some numbers that we'll use a few times */ 0435 int farLeft = qMin(area1.left(), area2.left()); 0436 int innerLeft = qMax(area1.left(), area2.left()); 0437 0438 int farTop = qMin(area1.top(), area2.top()); 0439 int innerTop = qMax(area1.top(), area2.top()); 0440 0441 int farRight = qMax(area1.right(), area2.right()); 0442 int innerRight = qMin(area1.right(), area2.right()); 0443 0444 int farBottom = qMax(area1.bottom(), area2.bottom()); 0445 int innerBottom = qMin(area1.bottom(), area2.bottom()); 0446 0447 if (newLeft) { 0448 changedRegion.add(QRect(QPoint(farLeft, innerTop), 0449 QPoint(innerLeft - 1, innerBottom))); 0450 if (newTop) { 0451 changedRegion.add(QRect(QPoint(farLeft, farTop), 0452 QPoint(innerLeft - 1, innerTop - 1))); 0453 } 0454 if (newBottom) { 0455 changedRegion.add(QRect(QPoint(farLeft, innerBottom + 1), 0456 QPoint(innerLeft - 1, farBottom))); 0457 } 0458 } 0459 0460 if (newTop) { 0461 changedRegion.add(QRect(QPoint(innerLeft, farTop), 0462 QPoint(innerRight, innerTop - 1))); 0463 } 0464 0465 if (newRight) { 0466 changedRegion.add(QRect(QPoint(innerRight + 1, innerTop), 0467 QPoint(farRight, innerBottom))); 0468 if (newTop) { 0469 changedRegion.add(QRect(QPoint(innerRight + 1, farTop), 0470 QPoint(farRight, innerTop - 1))); 0471 } 0472 if (newBottom) { 0473 changedRegion.add(QRect(QPoint(innerRight + 1, innerBottom + 1), 0474 QPoint(farRight, farBottom))); 0475 } 0476 } 0477 0478 if (newBottom) { 0479 changedRegion.add(QRect(QPoint(innerLeft, innerBottom + 1), 0480 QPoint(innerRight, farBottom))); 0481 } 0482 0483 d->marker = topLeft; 0484 d->cursor = point; 0485 0486 emitChanged(changedRegion); 0487 } 0488 0489 void Selection::extend(const QPoint& point, Sheet* sheet) 0490 { 0491 if (!isValid(point)) 0492 return; 0493 0494 if (isEmpty() || d->selectionMode == SingleCell) { 0495 initialize(point, sheet); 0496 return; 0497 } 0498 0499 debugSheets ; 0500 0501 if (!sheet) { 0502 if (d->originSheet) { 0503 sheet = d->originSheet; 0504 } else { 0505 sheet = d->activeSheet; 0506 } 0507 } 0508 0509 Region changedRegion = Region(extendToMergedAreas(QRect(d->marker, d->marker))); 0510 0511 // for the case of a merged cell 0512 QPoint topLeft(point); 0513 Cell cell(d->activeSheet, point); 0514 if (cell.isPartOfMerged()) { 0515 cell = cell.masterCell(); 0516 topLeft = QPoint(cell.column(), cell.row()); 0517 } 0518 0519 if (d->multipleOccurences) { 0520 const int subRegionEnd = d->activeSubRegionStart + d->activeSubRegionLength; 0521 const bool prepend = d->activeSubRegionLength == 0; 0522 const bool atEnd = d->activeElement == subRegionEnd; 0523 // Insert the new location after the active element, if possible. 0524 const int index = d->activeElement + ((prepend || atEnd) ? 0 : 1); 0525 insert(index, topLeft, sheet, true); 0526 ++d->activeSubRegionLength; 0527 ++d->activeElement; 0528 d->anchor = topLeft; 0529 d->marker = topLeft; 0530 } else { 0531 // TODO Replace for normal selection and resizing of any range. 0532 // The new point may split an existing range. Anyway, the new 0533 // location/range is appended and the last element becomes active. 0534 const int count = cells().count(); 0535 eor(topLeft, sheet); 0536 d->activeSubRegionLength += cells().count() - count; 0537 d->activeElement = cells().count() - 1; 0538 d->anchor = cells()[d->activeElement]->rect().topLeft(); 0539 d->marker = cells()[d->activeElement]->rect().bottomRight(); 0540 } 0541 d->cursor = d->marker; 0542 0543 changedRegion.add(topLeft, sheet); 0544 changedRegion.add(*this); 0545 0546 emitChanged(changedRegion); 0547 } 0548 0549 void Selection::extend(const QRect& range, Sheet* sheet) 0550 { 0551 if (!isValid(range) || (range == QRect(0, 0, 1, 1))) 0552 return; 0553 0554 if (isEmpty() || d->selectionMode == SingleCell) { 0555 initialize(range, sheet); 0556 return; 0557 } 0558 0559 if (!sheet) { 0560 if (d->originSheet) { 0561 sheet = d->originSheet; 0562 } else { 0563 sheet = d->activeSheet; 0564 } 0565 } 0566 0567 // for the case of a merged cell 0568 QPoint topLeft(range.topLeft()); 0569 Cell cell(d->activeSheet, topLeft); 0570 if (cell.isPartOfMerged()) { 0571 cell = cell.masterCell(); 0572 topLeft = QPoint(cell.column(), cell.row()); 0573 } 0574 0575 // for the case of a merged cell 0576 QPoint bottomRight(range.bottomRight()); 0577 cell = Cell(d->activeSheet, bottomRight); 0578 if (cell.isPartOfMerged()) { 0579 cell = cell.masterCell(); 0580 bottomRight = QPoint(cell.column(), cell.row()); 0581 } 0582 0583 const QRect newRange = extendToMergedAreas(QRect(topLeft, bottomRight)); 0584 0585 Element* element = 0; 0586 if (d->multipleOccurences) { 0587 const int subRegionEnd = d->activeSubRegionStart + d->activeSubRegionLength; 0588 const bool prepend = d->activeSubRegionLength == 0; 0589 const bool atEnd = d->activeElement == subRegionEnd; 0590 // Insert the new location after the active element, if possible. 0591 const int index = d->activeElement + ((prepend || atEnd) ? 0 : 1); 0592 insert(index, newRange, sheet, true); 0593 ++d->activeSubRegionLength; 0594 ++d->activeElement; 0595 d->anchor = newRange.topLeft(); 0596 d->marker = newRange.bottomRight(); 0597 } else { 0598 const int count = cells().count(); 0599 element = add(newRange, sheet); 0600 d->activeSubRegionLength += cells().count() - count; 0601 d->activeElement = cells().count() - 1; 0602 d->anchor = cells()[d->activeElement]->rect().topLeft(); 0603 d->marker = cells()[d->activeElement]->rect().bottomRight(); 0604 } 0605 d->cursor = d->marker; 0606 0607 if (element && element->type() == Element::Point) { 0608 Point* point = static_cast<Point*>(element); 0609 point->setColor(d->colors[cells().size() % d->colors.size()]); 0610 } else if (element && element->type() == Element::Range) { 0611 Range* range = static_cast<Range*>(element); 0612 range->setColor(d->colors[cells().size() % d->colors.size()]); 0613 } 0614 0615 emitChanged(*this); 0616 } 0617 0618 void Selection::extend(const Region& region) 0619 { 0620 if (!region.isValid()) 0621 return; 0622 0623 uint count = cells().count(); 0624 ConstIterator end(region.constEnd()); 0625 for (ConstIterator it = region.constBegin(); it != end; ++it) { 0626 Element *element = *it; 0627 if (!element) continue; 0628 if (element->type() == Element::Point) { 0629 Point* point = static_cast<Point*>(element); 0630 extend(point->pos(), element->sheet()); 0631 } else { 0632 extend(element->rect(), element->sheet()); 0633 } 0634 } 0635 0636 d->activeSubRegionLength += cells().count() - count; 0637 0638 emitChanged(*this); 0639 } 0640 0641 Selection::Element* Selection::eor(const QPoint& point, Sheet* sheet) 0642 { 0643 // The selection always has to contain one location/range at least. 0644 if (isSingular()) { 0645 return Region::add(point, sheet); 0646 } 0647 return Region::eor(point, sheet); 0648 } 0649 0650 const QPoint& Selection::anchor() const 0651 { 0652 return d->anchor; 0653 } 0654 0655 const QPoint& Selection::cursor() const 0656 { 0657 return d->cursor; 0658 } 0659 0660 const QPoint& Selection::marker() const 0661 { 0662 return d->marker; 0663 } 0664 0665 bool Selection::isSingular() const 0666 { 0667 return Region::isSingular(); 0668 } 0669 0670 QString Selection::name(Sheet* sheet) const 0671 { 0672 return Region::name(sheet ? sheet : d->originSheet); 0673 } 0674 0675 void Selection::setActiveSheet(Sheet* sheet) 0676 { 0677 if (d->activeSheet == sheet) { 0678 return; 0679 } 0680 d->activeSheet = sheet; 0681 emit activeSheetChanged(sheet); 0682 } 0683 0684 Sheet* Selection::activeSheet() const 0685 { 0686 return d->activeSheet; 0687 } 0688 0689 void Selection::setOriginSheet(Sheet* sheet) 0690 { 0691 d->originSheet = sheet; 0692 } 0693 0694 Sheet* Selection::originSheet() const 0695 { 0696 return d->originSheet; 0697 } 0698 0699 int Selection::setActiveElement(const Cell &cell) 0700 { 0701 for (int index = 0; index < cells().count(); ++index) { 0702 if (cells()[index]->sheet() != cell.sheet()) { 0703 continue; 0704 } 0705 QRect range = cells()[index]->rect(); 0706 const QPoint point = cell.cellPosition(); 0707 if (range.topLeft() == point || range.bottomRight() == point) { 0708 d->anchor = range.topLeft(); 0709 d->cursor = range.bottomRight(); 0710 d->marker = range.bottomRight(); 0711 d->activeElement = index; 0712 // Only adjust the sub-region, if index is out of bounds. 0713 if (index < d->activeSubRegionStart) { 0714 d->activeSubRegionStart = index; 0715 } 0716 if (index > d->activeSubRegionStart + d->activeSubRegionLength) { 0717 d->activeSubRegionStart = index; 0718 d->activeSubRegionLength = 1; 0719 } 0720 return index; 0721 } 0722 } 0723 return -1; 0724 } 0725 0726 Calligra::Sheets::Region::Element* Selection::activeElement() const 0727 { 0728 return (d->activeElement == cells().count()) ? 0 : cells()[d->activeElement]; 0729 } 0730 0731 void Selection::clear() 0732 { 0733 d->activeElement = 0; 0734 d->activeSubRegionStart = 0; 0735 d->activeSubRegionLength = 0; 0736 Region::clear(); 0737 // If this is the normal, not the reference mode, one element must survive. 0738 if (!referenceSelection()) { 0739 initialize(QPoint(1, 1), d->activeSheet); 0740 } 0741 } 0742 0743 void Selection::clearSubRegion() 0744 { 0745 if (isEmpty()) { 0746 return; 0747 } 0748 for (int index = 0; index < d->activeSubRegionLength; ++index) { 0749 delete cells().takeAt(d->activeSubRegionStart); 0750 } 0751 d->activeSubRegionLength = 0; 0752 d->activeElement = d->activeSubRegionStart + 1; // point behind the last 0753 } 0754 0755 void Selection::fixSubRegionDimension() 0756 { 0757 if (d->activeSubRegionStart > cells().count()) { 0758 debugSheets << "start position" << d->activeSubRegionStart << "exceeds list" << cells().count(); 0759 d->activeSubRegionStart = 0; 0760 d->activeSubRegionLength = cells().count(); 0761 return; 0762 } 0763 if (d->activeSubRegionStart + d->activeSubRegionLength > cells().count()) { 0764 debugSheets << "subregion (" << d->activeSubRegionStart << ".." 0765 << d->activeSubRegionStart + d->activeSubRegionLength 0766 << ") exceeds list" << cells().count(); 0767 d->activeSubRegionLength = cells().count() - d->activeSubRegionStart; 0768 return; 0769 } 0770 } 0771 0772 void Selection::setActiveSubRegion(int start, int length, int active) 0773 { 0774 // Set the active sub-region. 0775 d->activeSubRegionStart = qBound(0, start, cells().count()); 0776 d->activeSubRegionLength = qBound(0, length, cells().count() - d->activeSubRegionStart); 0777 0778 // Set the active element. 0779 d->activeElement = qBound(d->activeSubRegionStart, active, d->activeSubRegionStart + d->activeSubRegionLength); 0780 0781 if (isEmpty()) { 0782 return; 0783 } 0784 0785 // Set the anchor, marker and cursor according to the active element. 0786 const int subRegionEnd = d->activeSubRegionStart + d->activeSubRegionLength; 0787 const bool atEnd = d->activeElement == subRegionEnd; 0788 const int index = qBound(0, d->activeElement - (atEnd ? 1 : 0), cells().count() - 1); 0789 const QRect range = cells()[index]->rect(); 0790 d->anchor = range.topLeft(); 0791 d->marker = range.bottomRight(); 0792 d->cursor = d->marker; 0793 } 0794 0795 QString Selection::activeSubRegionName() const 0796 { 0797 QStringList names; 0798 int end = d->activeSubRegionStart + d->activeSubRegionLength; 0799 for (int index = d->activeSubRegionStart; index < end; ++index) { 0800 names += cells()[index]->name(d->originSheet); 0801 } 0802 return names.isEmpty() ? "" : names.join(";"); 0803 } 0804 0805 void Selection::setSelectionMode(Mode mode) 0806 { 0807 d->selectionMode = mode; 0808 } 0809 0810 const QList<QColor>& Selection::colors() const 0811 { 0812 return d->colors; 0813 } 0814 0815 void Selection::selectAll() 0816 { 0817 if (!isAllSelected()) { 0818 d->oldSelection = *this; 0819 initialize(QRect(QPoint(KS_colMax, KS_rowMax), QPoint(1, 1))); 0820 } else { 0821 initialize(d->oldSelection); 0822 d->oldSelection.clear(); 0823 } 0824 } 0825 0826 void Selection::startReferenceSelection() 0827 { 0828 // former selection exists - we are in ref mode already, even though it's suspended 0829 if (!d->formerSelection.isEmpty()) { 0830 setReferenceSelectionMode(true); 0831 return; 0832 } 0833 d->formerSelection = *this; 0834 clear(); // all elements; no residuum; 0835 setOriginSheet(activeSheet()); 0836 // It is important to enable this AFTER we set the rect! 0837 d->referenceMode = true; 0838 d->multipleOccurences = true; 0839 // Visual cue to indicate that the user can drag-select the selection selection 0840 d->canvasBase->canvasWidget()->setCursor(Qt::CrossCursor); 0841 } 0842 0843 void Selection::endReferenceSelection(bool saveChanges) 0844 { 0845 // The reference selection may be temporarily disabled. 0846 // The stored selection reliably indicates the reference selection mode. 0847 if (d->formerSelection.isEmpty()) { 0848 return; 0849 } 0850 if (originSheet() != activeSheet()) { 0851 emit visibleSheetRequested(originSheet()); 0852 } 0853 d->referenceMode = false; 0854 d->multipleOccurences = false; 0855 // While entering a formula the choose mode is turned on and off. 0856 // Clear the choice. Otherwise, cell references will stay highlighted. 0857 if (!isEmpty()) { 0858 emit changed(*this); 0859 clear(); // all elements; no residuum 0860 } 0861 if (saveChanges) { 0862 initialize(d->formerSelection); 0863 } 0864 d->formerSelection.clear(); 0865 // The normal selection does not support the replacments of sub-regions. 0866 // Reset the active sub-region to the whole region. 0867 // TODO Why not allow that? Would make resizing of all ranges in a 0868 // non-contiguous selection possible! 0869 setActiveSubRegion(0, cells().count()); 0870 d->canvasBase->canvasWidget()->setCursor(Qt::ArrowCursor); 0871 } 0872 0873 void Selection::setReferenceSelectionMode(bool enable) 0874 { 0875 d->referenceMode = enable; 0876 d->multipleOccurences = enable; 0877 d->canvasBase->canvasWidget()->setCursor(enable ? Qt::CrossCursor : Qt::ArrowCursor); 0878 } 0879 0880 bool Selection::referenceSelectionMode() const 0881 { 0882 return d->referenceMode; 0883 } 0884 0885 bool Selection::referenceSelection() const 0886 { 0887 return (!d->formerSelection.isEmpty()); 0888 } 0889 0890 void Selection::emitAboutToModify() 0891 { 0892 emit aboutToModify(*this); 0893 } 0894 0895 void Selection::emitModified() 0896 { 0897 emit modified(*this); 0898 } 0899 0900 void Selection::emitRefreshSheetViews() 0901 { 0902 emit refreshSheetViews(); 0903 } 0904 0905 void Selection::emitVisibleSheetRequested(Sheet* sheet) 0906 { 0907 emit visibleSheetRequested(sheet); 0908 } 0909 0910 void Selection::emitCloseEditor(bool saveChanges, bool expandMatrix) 0911 { 0912 emit closeEditor(saveChanges, expandMatrix); 0913 } 0914 0915 void Selection::emitRequestFocusEditor() 0916 { 0917 emit requestFocusEditor(); 0918 } 0919 0920 QRect Selection::extendToMergedAreas(const QRect& _area) const 0921 { 0922 if (!d->activeSheet) 0923 return _area; 0924 0925 QRect area = normalized(_area); 0926 Cell cell(d->activeSheet, area.left(), area.top()); 0927 0928 if (Region::Range(area).isColumn() || Region::Range(area).isRow()) { 0929 return area; 0930 } else if (!(cell.isPartOfMerged()) && 0931 (cell.mergedXCells() + 1) >= area.width() && 0932 (cell.mergedYCells() + 1) >= area.height()) { 0933 /* if just a single cell is selected, we need to merge even when 0934 the obscuring isn't forced. But only if this is the cell that 0935 is doing the obscuring -- we still want to be able to click on a cell 0936 that is being obscured. 0937 */ 0938 area.setWidth(cell.mergedXCells() + 1); 0939 area.setHeight(cell.mergedYCells() + 1); 0940 } else { 0941 int top = area.top(); 0942 int left = area.left(); 0943 int bottom = area.bottom(); 0944 int right = area.right(); 0945 for (int x = area.left(); x <= area.right(); x++) 0946 for (int y = area.top(); y <= area.bottom(); y++) { 0947 cell = Cell(d->activeSheet, x, y); 0948 if (cell.doesMergeCells()) { 0949 right = qMax(right, cell.mergedXCells() + x); 0950 bottom = qMax(bottom, cell.mergedYCells() + y); 0951 } else if (cell.isPartOfMerged()) { 0952 cell = cell.masterCell(); 0953 left = qMin(left, cell.column()); 0954 top = qMin(top, cell.row()); 0955 bottom = qMax(bottom, cell.row() + cell.mergedYCells()); 0956 right = qMax(right, cell.column() + cell.mergedXCells()); 0957 } 0958 } 0959 0960 area.setCoords(left, top, right, bottom); 0961 } 0962 return area; 0963 } 0964 0965 Calligra::Sheets::Region::Point* Selection::createPoint(const QPoint& point) const 0966 { 0967 return new Point(point); 0968 } 0969 0970 Calligra::Sheets::Region::Point* Selection::createPoint(const QString& string) const 0971 { 0972 return new Point(string); 0973 } 0974 0975 Calligra::Sheets::Region::Point* Selection::createPoint(const Region::Point& point) const 0976 { 0977 return new Point(point); 0978 } 0979 0980 Calligra::Sheets::Region::Range* Selection::createRange(const QRect& rect) const 0981 { 0982 return new Range(rect); 0983 } 0984 0985 Calligra::Sheets::Region::Range* Selection::createRange(const Calligra::Sheets::Region::Point& tl, const Calligra::Sheets::Region::Point& br) const 0986 { 0987 return new Range(tl, br); 0988 } 0989 0990 Calligra::Sheets::Region::Range* Selection::createRange(const QString& string) const 0991 { 0992 return new Range(string); 0993 } 0994 0995 Calligra::Sheets::Region::Range* Selection::createRange(const Region::Range& range) const 0996 { 0997 return new Range(range); 0998 } 0999 1000 void Selection::emitChanged(const Region& region) 1001 { 1002 Sheet * const sheet = d->activeSheet; 1003 if(!sheet) // no sheet no update needed 1004 return; 1005 Region extendedRegion; 1006 ConstIterator end(region.constEnd()); 1007 for (ConstIterator it = region.constBegin(); it != end; ++it) { 1008 Element* element = *it; 1009 QRect area = element->rect(); 1010 1011 const ColumnFormat *col; 1012 //look at if column is hiding. 1013 //if it's hiding refreshing column+1 (or column -1 ) 1014 int left = area.left(); 1015 int right = area.right(); 1016 int top = area.top(); 1017 int bottom = area.bottom(); 1018 1019 // a merged cells is selected 1020 if (element->type() == Region::Element::Point) { 1021 Cell cell(sheet, left, top); 1022 if (cell.doesMergeCells()) { 1023 // extend to the merged region 1024 // prevents artefacts of the selection rectangle 1025 right += cell.mergedXCells(); 1026 bottom += cell.mergedYCells(); 1027 } 1028 } 1029 1030 if (right < KS_colMax) { 1031 do { 1032 right++; 1033 col = sheet->columnFormat(right); 1034 } while (col->isHiddenOrFiltered() && right != KS_colMax); 1035 } 1036 if (left > 1) { 1037 do { 1038 left--; 1039 col = sheet->columnFormat(left); 1040 } while (col->isHiddenOrFiltered() && left != 1); 1041 } 1042 1043 if (bottom < KS_rowMax) { 1044 do { 1045 bottom++; 1046 int lastHidden; 1047 if (sheet->rowFormats()->isHiddenOrFiltered(bottom, &lastHidden)) { 1048 bottom = lastHidden; 1049 } else { 1050 break; 1051 } 1052 } while (bottom != KS_rowMax); 1053 } 1054 1055 if (top > 1) { 1056 do { 1057 top--; 1058 int firstHidden; 1059 if (sheet->rowFormats()->isHiddenOrFiltered(top, 0, &firstHidden)) { 1060 top = firstHidden; 1061 } else { 1062 break; 1063 } 1064 } while (top != 1); 1065 } 1066 1067 area.setLeft(left); 1068 area.setRight(right); 1069 area.setTop(top); 1070 area.setBottom(bottom); 1071 1072 extendedRegion.add(area, element->sheet()); 1073 } 1074 1075 const QList<Cell> masterCells = sheet->cellStorage()->masterCells(extendedRegion); 1076 for (int i = 0; i < masterCells.count(); ++i) 1077 extendedRegion.add(masterCells[i].cellPosition(), sheet); 1078 1079 emit changed(extendedRegion); 1080 } 1081 1082 void Selection::scrollToCursor() 1083 { 1084 const QPoint location = cursor(); 1085 Sheet *const sheet = activeSheet(); 1086 1087 // Adjust the maximum accessed column and row for the scrollbars. 1088 emit updateAccessedCellRange(sheet, location); 1089 1090 // The cell geometry expanded by some pixels in each direction. 1091 const Cell cell = Cell(sheet, location).masterCell(); 1092 const double xpos = sheet->columnPosition(cell.cellPosition().x()); 1093 const double ypos = sheet->rowPosition(cell.cellPosition().y()); 1094 const double pixelWidth = canvas()->viewConverter()->viewToDocumentX(1); 1095 const double pixelHeight = canvas()->viewConverter()->viewToDocumentY(1); 1096 QRectF rect(xpos, ypos, cell.width(), cell.height()); 1097 rect.adjust(-2*pixelWidth, -2*pixelHeight, +2*pixelWidth, +2*pixelHeight); 1098 rect = rect & QRectF(QPointF(0.0, 0.0), sheet->documentSize()); 1099 1100 // Scroll to cell. 1101 canvas()->canvasController()->ensureVisible(canvas()->viewConverter()->documentToView(rect), true); 1102 } 1103 1104 void Selection::dump() const 1105 { 1106 debugSheets << *this; 1107 debugSheets << "d->activeElement:" << d->activeElement; 1108 debugSheets << "d->activeSubRegionStart:" << d->activeSubRegionStart; 1109 debugSheets << "d->activeSubRegionLength:" << d->activeSubRegionLength; 1110 } 1111 1112 /*************************************************************************** 1113 class Point 1114 ****************************************************************************/ 1115 1116 Selection::Point::Point(const QPoint& point) 1117 : Region::Point(point), 1118 m_color(Qt::black) 1119 { 1120 } 1121 1122 Selection::Point::Point(const QString& string) 1123 : Region::Point(string), 1124 m_color(Qt::black) 1125 { 1126 } 1127 1128 Selection::Point::Point(const Region::Point& point) 1129 : Region::Point(point), 1130 m_color(Qt::black) 1131 { 1132 } 1133 1134 /*************************************************************************** 1135 class Range 1136 ****************************************************************************/ 1137 1138 Selection::Range::Range(const QRect& range) 1139 : Region::Range(range), 1140 m_color(Qt::black) 1141 { 1142 } 1143 1144 Selection::Range::Range(const Calligra::Sheets::Region::Point& tl, const Calligra::Sheets::Region::Point& br) 1145 : Region::Range(tl, br), 1146 m_color(Qt::black) 1147 { 1148 } 1149 1150 Selection::Range::Range(const QString& string) 1151 : Region::Range(string), 1152 m_color(Qt::black) 1153 { 1154 } 1155 1156 Selection::Range::Range(const Region::Range& range) 1157 : Region::Range(range), 1158 m_color(Qt::black) 1159 { 1160 }