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 }