File indexing completed on 2024-04-28 16:21:27

0001 /* This file is part of the KDE project
0002    Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
0003    Copyright 2005-2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
0004 
0005    This program is free software; you can redistribute it and/or modify
0006    it under the terms of the GNU Library General Public License as published by
0007    the Free Software Foundation; either version 2 of the License, or
0008    (at your option) any later version.
0009 
0010    This program 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
0013    GNU 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 program; if not, write to the Free Software
0017    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0018 */
0019 
0020 // Local
0021 #include "Region.h"
0022 
0023 #include <QRegExp>
0024 #include <QStringList>
0025 
0026 #include "SheetsDebug.h"
0027 #include "Cell.h"
0028 #include "calligra_sheets_limits.h"
0029 #include "Map.h"
0030 #include "NamedAreaManager.h"
0031 #include "Sheet.h"
0032 #include "Util.h"
0033 
0034 namespace Calligra
0035 {
0036 namespace Sheets
0037 {
0038 
0039 class Q_DECL_HIDDEN Region::Private : public QSharedData
0040 {
0041 public:
0042     Private()
0043             : map(0),
0044             cells(QList<Element*>()) {
0045     }
0046 
0047     const Map* map;
0048     mutable QList<Element*> cells;
0049 };
0050 
0051 
0052 /***************************************************************************
0053   class Region
0054 ****************************************************************************/
0055 
0056 Region::Region()
0057 {
0058     d = new Private();
0059 }
0060 
0061 Region::Region(const QString& string, const Map* map, Sheet* fallbackSheet)
0062 {
0063     d = new Private();
0064     d->map = map;
0065 
0066     if (string.isEmpty()) {
0067         return;
0068     }
0069     // FIXME Stefan: Does not respect quoted names!
0070     QStringList substrings = string.split(';');
0071     QStringList::ConstIterator end = substrings.constEnd();
0072     for (QStringList::ConstIterator it = substrings.constBegin(); it != end; ++it) {
0073         QString sRegion = *it;
0074 
0075         // check for a named area first
0076         const Region namedAreaRegion = map ? map->namedAreaManager()->namedArea(sRegion) : Region();
0077         if (namedAreaRegion.isValid()) {
0078             ConstIterator end(namedAreaRegion.d->cells.constEnd());
0079             for (ConstIterator it = namedAreaRegion.d->cells.constBegin(); it != end; ++it) {
0080                 Element *element = *it;
0081                 if (element->type() == Element::Point) {
0082                     Point* point = static_cast<Point*>(element);
0083                     d->cells.append(createPoint(*point));
0084                 } else {
0085                     Range* range = static_cast<Range*>(element);
0086                     d->cells.append(createRange(*range));
0087                 }
0088             }
0089             continue;
0090         }
0091 
0092         // Single cell or cell range
0093         int delimiterPos = sRegion.indexOf(':');
0094         if (delimiterPos > -1) {
0095             // range
0096             QString sUL = sRegion.left(delimiterPos);
0097             QString sLR = sRegion.mid(delimiterPos + 1);
0098 
0099             Sheet* firstSheet = map ? filterSheetName(sUL) : 0;
0100             Sheet* lastSheet = map ? filterSheetName(sLR) : 0;
0101             // TODO: lastSheet is silently ignored if it is different from firstSheet
0102 
0103             // Still has the sheet name separator?
0104             if (sUL.contains('!') || sLR.contains('!'))
0105                 return;
0106 
0107             if (!firstSheet)
0108                 firstSheet = fallbackSheet;
0109             if (!lastSheet)
0110                 lastSheet = fallbackSheet;
0111 
0112             Point ul(sUL);
0113             Point lr(sLR);
0114 
0115             if (ul.isValid() && lr.isValid()) {
0116                 Range* range = createRange(ul, lr);
0117                 if (firstSheet) range->setSheet(firstSheet);
0118                 d->cells.append(range);
0119             } else if (ul.isValid()) {
0120                 Point* point = createPoint(ul);
0121                 if (firstSheet) point->setSheet(firstSheet);
0122                 d->cells.append(point);
0123             } else { // lr.isValid()
0124                 Point* point = createPoint(lr);
0125                 if (firstSheet) point->setSheet(firstSheet);
0126                 d->cells.append(point);
0127             }
0128         } else {
0129             // single cell
0130             Sheet* sheet = map ? filterSheetName(sRegion) : 0;
0131             // Still has the sheet name separator?
0132             if (sRegion.contains('!'))
0133                 return;
0134             if (!sheet)
0135                 sheet = fallbackSheet;
0136             Point* point = createPoint(sRegion);
0137             if(sheet) point->setSheet(sheet);
0138             d->cells.append(point);
0139         }
0140     }
0141 }
0142 
0143 Region::Region(const QRect& rect, Sheet* sheet)
0144 {
0145     d = new Private();
0146 
0147     Q_ASSERT(!rect.isNull());
0148     if (rect.isNull()) {
0149         errorSheets << "Region::Region(const QRect&): QRect is empty!" << endl;
0150         return;
0151     }
0152     add(rect, sheet);
0153 }
0154 
0155 Region::Region(const QPoint& point, Sheet* sheet)
0156 {
0157     d = new Private();
0158 
0159     Q_ASSERT(!point.isNull());
0160     if (point.isNull()) {
0161         errorSheets << "Region::Region(const QPoint&): QPoint is empty!" << endl;
0162         return;
0163     }
0164     add(point, sheet);
0165 }
0166 
0167 Region::Region(const Region& list)
0168 {
0169     d = new Private();
0170     d->map = list.d->map;
0171     d->cells.reserve(list.d->cells.size());
0172     ConstIterator end(list.d->cells.constEnd());
0173     for (ConstIterator it = list.d->cells.constBegin(); it != end; ++it) {
0174         Element *element = *it;
0175         if (element->type() == Element::Point) {
0176             Point* point = static_cast<Point*>(element);
0177             d->cells.append(createPoint(*point));
0178         } else {
0179             Range* range = static_cast<Range*>(element);
0180             d->cells.append(createRange(*range));
0181         }
0182     }
0183 }
0184 
0185 Region::Region(int x, int y, Sheet* sheet)
0186 {
0187     d = new Private();
0188 
0189     Q_ASSERT(isValid(QPoint(x, y)));
0190     if (!isValid(QPoint(x, y))) {
0191         errorSheets << "Region::Region(" << x << ", " << y << "): Coordinates are invalid!" << endl;
0192         return;
0193     }
0194     add(QPoint(x, y), sheet);
0195 }
0196 
0197 Region::Region(int x, int y, int width, int height, Sheet* sheet)
0198 {
0199     d = new Private();
0200 
0201     Q_ASSERT(isValid(QRect(x, y, width, height)));
0202     if (!isValid(QRect(x, y, width, height))) {
0203         errorSheets << "Region::Region(" << x << ", " << y << ", " << width << ", " << height << "): Dimensions are invalid!" << endl;
0204         return;
0205     }
0206     add(QRect(x, y, width, height), sheet);
0207 }
0208 
0209 
0210 Region::~Region()
0211 {
0212     qDeleteAll(d->cells);
0213 }
0214 
0215 QVector<QRect> Region::rects() const
0216 {
0217     QVector<QRect> cellRects;
0218     foreach(Element *element, d->cells) {
0219         cellRects.append(element->rect());
0220     }
0221     return cellRects;
0222 }
0223 
0224 const Map* Region::map() const
0225 {
0226     Q_ASSERT(d->map);
0227     return d->map;
0228 }
0229 
0230 void Region::setMap(const Map* map)
0231 {
0232     d->map = map;
0233 }
0234 
0235 bool Region::isValid() const
0236 {
0237     if (d->cells.isEmpty())
0238         return false;
0239     ConstIterator end = d->cells.constEnd();
0240     for (ConstIterator it = d->cells.constBegin(); it != end; ++it) {
0241         if (!(*it)->isValid())
0242             return false;
0243     }
0244     return true;
0245 }
0246 
0247 bool Region::isSingular() const
0248 {
0249     if (d->cells.isEmpty() || d->cells.count() > 1 || (*d->cells.constBegin())->type() != Element::Point) {
0250         return false;
0251     }
0252     return true;
0253 }
0254 
0255 bool Region::isContiguous() const
0256 {
0257     if (d->cells.count() != 1 || !isValid()) {
0258         return false;
0259     }
0260     return true;
0261 }
0262 
0263 QString Region::name(Sheet* originSheet) const
0264 {
0265     QStringList names;
0266     ConstIterator endOfList(d->cells.constEnd());
0267     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
0268         Element *element = *it;
0269         names += element->name(originSheet);
0270     }
0271     return names.isEmpty() ? "" : names.join(";");
0272 }
0273 
0274 Region::Element* Region::add(const QPoint& point, Sheet* sheet)
0275 {
0276     return insert(d->cells.count(), point, sheet, false);
0277 }
0278 
0279 Region::Element* Region::add(const QRect& range, Sheet* sheet)
0280 {
0281     const QRect normalizedRange = normalized(range);
0282     if (normalizedRange.width() == 0 || normalizedRange.height() == 0) {
0283         return 0;
0284     }
0285     if (normalizedRange.size() == QSize(1, 1)) {
0286         return add(normalizedRange.topLeft(), sheet);
0287     }
0288     return insert(d->cells.count(), normalizedRange, sheet, false);
0289 }
0290 
0291 Region::Element* Region::add(const Region& region, Sheet* sheet)
0292 {
0293     ConstIterator endOfList(region.d->cells.constEnd());
0294     for (ConstIterator it = region.d->cells.constBegin(); it != endOfList; ++it) {
0295         add((*it)->rect(), (*it)->sheet() ? (*it)->sheet() : sheet);
0296     }
0297     return d->cells.isEmpty() ? 0 : d->cells.last();
0298 }
0299 
0300 void Region::sub(const QPoint& point, Sheet* sheet)
0301 {
0302     // TODO Stefan: Improve!
0303     Iterator endOfList(d->cells.end());
0304     for (Iterator it = d->cells.begin(); it != endOfList; ++it) {
0305         Element *element = *it;
0306         if (element->sheet() != sheet) {
0307             continue;
0308         }
0309         if (element->rect() == QRect(point, point)) {
0310             delete element;
0311             d->cells.removeAll(element);
0312             break;
0313         }
0314     }
0315 }
0316 
0317 void Region::sub(const QRect& range, Sheet* sheet)
0318 {
0319     const QRect normalizedRange = normalized(range);
0320     // TODO Stefan: Improve!
0321     Iterator endOfList(d->cells.end());
0322     for (Iterator it = d->cells.begin(); it != endOfList; ++it) {
0323         Element *element = *it;
0324         if (element->sheet() != sheet) {
0325             continue;
0326         }
0327         if (element->rect() == normalizedRange) {
0328             delete element;
0329             d->cells.removeAll(element);
0330             break;
0331         }
0332     }
0333 }
0334 
0335 void Region::sub(const Region& region)
0336 {
0337     ConstIterator endOfList(region.constEnd());
0338     for (ConstIterator it = region.constBegin(); it != endOfList; ++it) {
0339         Element *element = *it;
0340         if (element->type() == Element::Point) {
0341             Point* point = static_cast<Point*>(element);
0342             sub(Region(point->pos()));
0343         } else {
0344             sub(Region(element->rect()));
0345         }
0346     }
0347 }
0348 
0349 Region Region::intersected(const Region& region) const
0350 {
0351   // Special case 1: one of the regions is empty
0352   if (region.isEmpty()) return region;
0353   if (isEmpty()) return Region();
0354 
0355   // Special case 2: If the region contains more elements than this one, do this operation in reverse (optimization)
0356   if (region.cells().size() > cells().size())
0357     return region.intersected (*this);
0358 
0359   // Most common case: the region contains only one rectangle
0360   Region result;
0361   QVector<QRect> rects = region.rects();
0362   if (rects.size() == 1) {
0363     QRect rect = rects[0];
0364     Sheet *s = region.cells()[0]->sheet();
0365     // intersect each element with the rectangle
0366     foreach(Element *element, d->cells) {
0367       if (element->sheet() != s) continue;
0368       if (element->type() == Element::Point) {
0369         Point* point = static_cast<Point*>(element);
0370         if (rect.contains (point->pos()))
0371           result.add (point->pos(), s);
0372       } else {
0373         QRect rect2 = element->rect();
0374         if (rect2.intersects (rect))
0375           result.add (rect2.intersected (rect), s);
0376       }
0377     }
0378     return result;
0379   }
0380 
0381     // Generic case. TODO: optimize this better - generating a ton of single-cell regions is slow
0382     ConstIterator end(region.constEnd());
0383     for (ConstIterator it = region.constBegin(); it != end; ++it) {
0384         Element *element = *it;
0385         if (element->type() == Element::Point) {
0386             Point* point = static_cast<Point*>(element);
0387             if(contains(point->pos(), element->sheet()))
0388                 result.add(point->pos(), element->sheet());
0389         } else {
0390             QRect rect = element->rect();
0391             for(int c = rect.top(); c <= rect.bottom(); ++c) {
0392                 for(int r = rect.left(); r <= rect.right(); ++r) {
0393                     QPoint p(r,c);
0394                     if(contains(p, element->sheet()))
0395                         result.add(p, element->sheet());
0396                 }
0397             }
0398         }
0399     }
0400     return result;
0401 }
0402 
0403 Region Region::intersectedWithRow(int row) const
0404 {
0405     Region result;
0406     ConstIterator end(constEnd());
0407     for (ConstIterator it = constBegin(); it != end; ++it) {
0408         Element *element = *it;
0409         if (element->type() == Element::Point) {
0410             Point* point = static_cast<Point*>(element);
0411             if (point->pos().y() == row)
0412                 result.add(point->pos(), point->sheet());
0413         } else {
0414             QRect rect = element->rect();
0415             if (rect.top() <= row && rect.bottom() >= row) {
0416                 result.add(QRect(rect.left(), row, rect.width(), 1), element->sheet());
0417             }
0418         }
0419     }
0420     return result;
0421 }
0422 
0423 Region::Element* Region::eor(const QPoint& point, Sheet* sheet)
0424 {
0425     bool containsPoint = false;
0426 
0427     int index = 0;
0428     while (index < d->cells.count()) {
0429         if (!d->cells[index]->contains(point)) {
0430             ++index;
0431             continue;
0432         }
0433         containsPoint = true;
0434         int x = point.x();
0435         int y = point.y();
0436         QRect fullRange = d->cells[index]->rect();
0437         delete d->cells.takeAt(index);
0438 
0439         // top range
0440         int left = fullRange.left();
0441         int top = fullRange.top();
0442         int width = fullRange.width();
0443         int height = y - top;
0444         if (height > 0) {
0445             insert(index, QRect(left, top, width, height), sheet);
0446         }
0447         // left range
0448         left = fullRange.left();
0449         top = y;
0450         width = qMax(0, x - left);
0451         height = 1;
0452         if (width > 0) {
0453             insert(index, QRect(left, top, width, height), sheet);
0454         }
0455         // right range
0456         left = qMin(x + 1, fullRange.right());
0457         top = y;
0458         width = qMax(0, fullRange.right() - x);
0459         height = 1;
0460         if (width > 0) {
0461             insert(index, QRect(left, top, width, height), sheet);
0462         }
0463         // bottom range
0464         left = fullRange.left();
0465         top = y + 1;
0466         width = fullRange.width();
0467         height = qMax(0, fullRange.bottom() - y);
0468         if (height > 0) {
0469             insert(index, QRect(left, top, width, height), sheet);
0470         }
0471         return d->cells[index];
0472     }
0473 
0474     if (!containsPoint) {
0475         return add(point, sheet);
0476     }
0477     return 0;
0478 }
0479 
0480 Region::Element* Region::insert(int pos, const QPoint& point, Sheet* sheet, bool multi)
0481 {
0482     if (point.x() < 1 || point.y() < 1) {
0483         return 0;
0484     }
0485     // Keep boundaries.
0486     pos = qBound(0, pos, cells().count());
0487 
0488     bool containsPoint = false;
0489 //   bool adjacentPoint = false;
0490 //   QRect neighbour;
0491 
0492     // we don't have to check for occurrences?
0493     if (multi) {
0494         Point* rpoint = createPoint(point);
0495         rpoint->setSheet(sheet);
0496         d->cells.insert(pos, rpoint);
0497         return d->cells[pos];
0498     }
0499 
0500     ConstIterator endOfList(d->cells.constEnd());
0501     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
0502         Element *element = *it;
0503         if (sheet && sheet != element->sheet()) {
0504             continue;
0505         }
0506         if (element->contains(point)) {
0507             containsPoint = true;
0508             break;
0509         }
0510         /*    else
0511             {
0512               neighbour = element->rect();
0513               neighbour.setTopLeft(neighbour.topLeft() - QPoint(1,1));
0514               neighbour.setBottomRight(neighbour.bottomRight() + QPoint(1,1));
0515               if (neighbour.contains(point))
0516               {
0517                 adjacentPoint = true; // TODO Stefan: Implement!
0518                 break;
0519               }
0520             }*/
0521     }
0522     if (!containsPoint) {
0523         Point* rpoint = createPoint(point);
0524         rpoint->setSheet(sheet);
0525         d->cells.insert(pos, rpoint);
0526         return d->cells[pos];
0527     }
0528     return 0;
0529 }
0530 
0531 Region::Element* Region::insert(int pos, const QRect& range, Sheet* sheet, bool multi)
0532 {
0533     // Keep boundaries.
0534     pos = qBound(0, pos, cells().count());
0535 
0536     const QRect normalizedRange = normalized(range);
0537     if (normalizedRange.size() == QSize(1, 1)) {
0538         return insert(pos, normalizedRange.topLeft(), sheet);
0539     }
0540 
0541     if (multi) {
0542         Range* rrange = createRange(normalizedRange);
0543         rrange->setSheet(sheet);
0544         d->cells.insert(pos, rrange);
0545         return d->cells[pos];
0546     }
0547 
0548     bool containsRange = false;
0549 
0550     for (int index = 0; index < d->cells.count(); ++index) {
0551         if (sheet && sheet != d->cells[index]->sheet()) {
0552             continue;
0553         }
0554         if (d->cells[index]->contains(normalizedRange)) {
0555             containsRange = true;
0556         } else if (normalizedRange.contains(d->cells[index]->rect())) {
0557             delete d->cells.takeAt(index--);
0558             continue;
0559         }
0560     }
0561     if (!containsRange) {
0562         // Keep boundaries.
0563         pos = qBound(0, pos, cells().count());
0564 
0565         Range* rrange = createRange(normalizedRange);
0566         rrange->setSheet(sheet);
0567         d->cells.insert(pos, rrange);
0568         return d->cells[pos];
0569     }
0570     return 0;
0571 }
0572 
0573 QSet<int> Region::columnsSelected() const
0574 {
0575     QSet<int> result;
0576     ConstIterator endOfList(d->cells.constEnd());
0577     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
0578         if ((*it)->isColumn()) {
0579             const QRect range = (*it)->rect();
0580             const int right = range.right();
0581             for (int col = range.left(); col <= right; ++col) {
0582                 result << col;
0583             }
0584         }
0585     }
0586     return result;
0587 }
0588 
0589 QSet<int> Region::rowsSelected() const
0590 {
0591     QSet<int> result;
0592     ConstIterator endOfList(d->cells.constEnd());
0593     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
0594         if ((*it)->isRow()) {
0595             const QRect range = (*it)->rect();
0596             const int bottom = range.bottom();
0597             for (int row = range.top(); row <= bottom; ++row) {
0598                 result << row;
0599             }
0600         }
0601     }
0602     return result;
0603 }
0604 
0605 QSet<int> Region::columnsAffected() const
0606 {
0607     QSet<int> result;
0608     ConstIterator endOfList(d->cells.constEnd());
0609     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
0610         const QRect range = (*it)->rect();
0611         const int right = range.right();
0612         for (int col = range.left(); col <= right; ++col) {
0613             result << col;
0614         }
0615     }
0616     return result;
0617 }
0618 
0619 QSet<int> Region::rowsAffected() const
0620 {
0621     QSet<int> result;
0622     ConstIterator endOfList(d->cells.constEnd());
0623     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
0624         const QRect range = (*it)->rect();
0625         const int bottom = range.bottom();
0626         for (int row = range.top(); row <= bottom; ++row) {
0627             result << row;
0628         }
0629     }
0630     return result;
0631 }
0632 
0633 bool Region::isColumnSelected(uint col) const
0634 {
0635     ConstIterator endOfList(d->cells.constEnd());
0636     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
0637         Element *element = *it;
0638         QRect region = element->rect();
0639         if ((col == 0 || ((int)col >= region.left() && (int)col <= region.right())) &&
0640                 region.top() == 1 && region.bottom() == KS_rowMax) {
0641             return true;
0642         }
0643     }
0644     return false;
0645 }
0646 
0647 bool Region::isRowSelected(uint row) const
0648 {
0649     ConstIterator endOfList(d->cells.constEnd());
0650     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
0651         Element *element = *it;
0652         QRect region = element->rect();
0653         if ((row == 0 || ((int)row >= region.top() && (int)row <= region.bottom())) &&
0654                 region.left() == 1 && region.right() == KS_colMax) {
0655             return true;
0656         }
0657     }
0658     return false;
0659 }
0660 
0661 bool Region::isColumnOrRowSelected() const
0662 {
0663     ConstIterator endOfList(d->cells.constEnd());
0664     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
0665         Element *element = *it;
0666         QRect region = element->rect();
0667         if ((region.top() == 1 && region.bottom() == KS_rowMax) ||
0668                 (region.left() == 1 && region.right() == KS_colMax)) {
0669             return true;
0670         }
0671     }
0672     return false;
0673 }
0674 
0675 bool Region::isAllSelected() const
0676 {
0677     if (d->cells.count() != 1)
0678         return false;
0679     Q_ASSERT(d->cells.first());
0680     return d->cells.first()->isAll();
0681 }
0682 
0683 bool Region::contains(const QPoint& point, Sheet* sheet) const
0684 {
0685     if (d->cells.isEmpty()) {
0686         return false;
0687     }
0688     ConstIterator endOfList(d->cells.constEnd());
0689     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
0690         Element *element = *it;
0691         if (element->contains(point)) {
0692             if (sheet && element->sheet() != sheet) {
0693                 return false;
0694             }
0695             return true;
0696         }
0697     }
0698     return false;
0699 }
0700 
0701 bool Region::isEmpty() const
0702 {
0703     return d->cells.isEmpty();
0704 }
0705 
0706 void Region::clear()
0707 {
0708     qDeleteAll(d->cells);
0709     d->cells.clear();
0710 }
0711 
0712 QRect Region::firstRange() const
0713 {
0714     if (!isValid())
0715         return QRect();
0716     return d->cells.value(0)->rect();
0717 }
0718 
0719 QRect Region::lastRange() const
0720 {
0721     if (!isValid())
0722         return QRect();
0723     return d->cells.value(d->cells.count() - 1)->rect();
0724 }
0725 
0726 Sheet* Region::firstSheet() const
0727 {
0728     if (!isValid())
0729         return 0;
0730     return d->cells.value(0)->sheet();
0731 }
0732 
0733 Sheet* Region::lastSheet() const
0734 {
0735     if (!isValid())
0736         return 0;
0737     return d->cells.value(d->cells.count() - 1)->sheet();
0738 }
0739 
0740 QRect Region::boundingRect() const
0741 {
0742     int left   = KS_colMax;
0743     int right  = 1;
0744     int top    = KS_rowMax;
0745     int bottom = 1;
0746     Region::ConstIterator endOfList = cells().constEnd();
0747     for (Region::ConstIterator it = cells().constBegin(); it != endOfList; ++it) {
0748         QRect range = (*it)->rect();
0749         if (range.left() < left) {
0750             left = range.left();
0751         }
0752         if (range.right() > right) {
0753             right = range.right();
0754         }
0755         if (range.top() < top) {
0756             top = range.top();
0757         }
0758         if (range.bottom() > bottom) {
0759             bottom = range.bottom();
0760         }
0761     }
0762     return QRect(left, top, right - left + 1, bottom - top + 1);
0763 }
0764 
0765 QRect Region::normalized(const QRect& rect)
0766 {
0767     QRect normalizedRect(rect);
0768     if (rect.left() > rect.right()) {
0769         normalizedRect.setLeft(rect.right());
0770         normalizedRect.setRight(rect.left());
0771     }
0772     if (rect.top() > rect.bottom()) {
0773         normalizedRect.setTop(rect.bottom());
0774         normalizedRect.setBottom(rect.top());
0775     }
0776     if (rect.right() > KS_colMax) {
0777         normalizedRect.setRight(KS_colMax);
0778     }
0779     if (rect.bottom() > KS_rowMax) {
0780         normalizedRect.setBottom(KS_rowMax);
0781     }
0782     return normalizedRect;
0783 }
0784 
0785 Region::ConstIterator Region::constBegin() const
0786 {
0787     return d->cells.constBegin();
0788 }
0789 
0790 Region::ConstIterator Region::constEnd() const
0791 {
0792     return d->cells.constEnd();
0793 }
0794 
0795 bool Region::isValid(const QPoint& point)
0796 {
0797     if (point.x() < 1 || point.y() < 1 ||
0798             point.x() > KS_colMax ||  point.y() > KS_rowMax)
0799         return false;
0800     else
0801         return true;
0802 }
0803 
0804 bool Region::isValid(const QRect& rect)
0805 {
0806     if (!isValid(rect.topLeft()) || !isValid(rect.bottomRight()) ||
0807             rect.width() == 0 || rect.height() == 0)
0808         return false;
0809     else
0810         return true;
0811 }
0812 
0813 QList<Region::Element*>& Region::cells() const
0814 {
0815     return d->cells;
0816 }
0817 
0818 bool Region::operator==(const Region& other) const
0819 {
0820     if (d->cells.count() != other.d->cells.count())
0821         return false;
0822     ConstIterator endOfList(d->cells.constEnd());
0823     ConstIterator endOfOtherList(other.d->cells.constEnd());
0824     ConstIterator it = d->cells.constBegin();
0825     ConstIterator it2 = other.d->cells.constBegin();
0826     while (it != endOfList && it2 != endOfOtherList) {
0827         if ((*it)->sheet() != (*it2)->sheet())
0828             return false;
0829         if ((*it++)->rect() != (*it2++)->rect())
0830             return false;
0831     }
0832     return true;
0833 }
0834 
0835 void Region::operator=(const Region& other)
0836 {
0837     d->map = other.d->map;
0838     clear();
0839     ConstIterator end(other.d->cells.constEnd());
0840     for (ConstIterator it = other.d->cells.constBegin(); it != end; ++it) {
0841         Element *element = *it;
0842         if (element->type() == Element::Point) {
0843             Point* point = static_cast<Point*>(element);
0844             d->cells.append(createPoint(*point));
0845         } else {
0846             Range* range = static_cast<Range*>(element);
0847             d->cells.append(createRange(*range));
0848         }
0849     }
0850 }
0851 
0852 Sheet* Region::filterSheetName(QString& sRegion)
0853 {
0854     Sheet* sheet = 0;
0855     int delimiterPos = sRegion.lastIndexOf('!');
0856     if (delimiterPos < 0)
0857         delimiterPos = sRegion.lastIndexOf('.');
0858     if (delimiterPos > -1) {
0859         QString sheetName = sRegion.left(delimiterPos);
0860         sheet = d->map->findSheet(sheetName);
0861         // try again without apostrophes
0862         while(!sheet && sheetName.count() > 2 && sheetName[0] == '\'' && sheetName[sheetName.count()-1] == '\'') {
0863             sheetName = sheetName.mid(1, sheetName.count() - 2);
0864             sheet = d->map->findSheet(sheetName);
0865         }
0866         // remove the sheet name, incl. '!', from the string
0867         if (sheet)
0868             sRegion = sRegion.right(sRegion.length() - delimiterPos - 1);
0869     }
0870     return sheet;
0871 }
0872 
0873 Region::Point* Region::createPoint(const QPoint& point) const
0874 {
0875     return new Point(point);
0876 }
0877 
0878 Region::Point* Region::createPoint(const QString& string) const
0879 {
0880     return new Point(string);
0881 }
0882 
0883 Region::Point* Region::createPoint(const Point& point) const
0884 {
0885     return new Point(point);
0886 }
0887 
0888 Region::Range* Region::createRange(const QRect& rect) const
0889 {
0890     return new Range(rect);
0891 }
0892 
0893 Region::Range* Region::createRange(const Point& tl, const Point& br) const
0894 {
0895     return new Range(tl, br);
0896 }
0897 
0898 Region::Range* Region::createRange(const QString& string) const
0899 {
0900     return new Range(string);
0901 }
0902 
0903 Region::Range* Region::createRange(const Range& range) const
0904 {
0905     return new Range(range);
0906 }
0907 
0908 /***************************************************************************
0909   class Element
0910 ****************************************************************************/
0911 
0912 Region::Element::Element()
0913         : m_sheet(0)
0914 {
0915 }
0916 
0917 Region::Element::~Element()
0918 {
0919 }
0920 
0921 
0922 /***************************************************************************
0923   class Point
0924 ****************************************************************************/
0925 static int firstNonCharPos(const QString& s, int pos = 0)
0926 {
0927     int result = -1;
0928     const QChar *data = s.constData();
0929     int i = 0;
0930     while (!data->isNull()) {
0931         if (i >= pos) {
0932             char c = data->unicode();
0933             if (c < 'A' || c > 'z' || (c < 'a' && c > 'Z')) {
0934                 result = i;
0935                 break;
0936             }
0937         }
0938         ++data;
0939         ++i;
0940     }
0941     return result;
0942 }
0943 
0944 Region::Point::Point(const QPoint& point)
0945         : Region::Element()
0946         , m_point(point)
0947         , m_fixedColumn(false)
0948         , m_fixedRow(false)
0949 {
0950     if (m_point.x() > KS_colMax)
0951         m_point.setX(KS_colMax);
0952     if (m_point.y() > KS_rowMax)
0953         m_point.setY(KS_rowMax);
0954 }
0955 
0956 Region::Point::Point(const QString& string)
0957         : Region::Element()
0958         , m_fixedColumn(false)
0959         , m_fixedRow(false)
0960 {
0961     const uint length = string.length();
0962     if (length == 0)
0963         return;
0964 
0965     uint p = 0;
0966 
0967     // Fixed ?
0968     if (string[0] == QChar('$', 0)) {
0969         m_fixedColumn = true;
0970         p++;
0971     }
0972 
0973     // Malformed ?
0974     if (p == length)
0975         return;
0976 
0977     if ((string[p] < QChar('A',0) || string[p] > QChar('Z',0)) && (string[p] < QChar('a', 0) || string[p] > QChar('z', 0)))
0978         return;
0979 
0980     //default is error
0981     int x = -1;
0982     //search for the first character != text
0983     int result = firstNonCharPos(string, p);
0984 
0985     //get the column number for the character between actual position and the first non text character
0986     if (result != -1)
0987         x = Util::decodeColumnLabelText(string.mid(p, result - p));     // x is defined now
0988     else  // If there isn't any, then this is not a point -> return
0989         return;
0990     p = result;
0991 
0992     //limit the x-value
0993     //Q_ASSERT(x >= 1 && x <= KS_colMax);
0994     if (x < 1)
0995         return;
0996     if (x > KS_colMax)
0997         x = KS_colMax;
0998 
0999     // Malformed ?
1000     if (p == length)
1001         return;
1002 
1003     if (string[p] == QChar('$', 0)) {
1004         m_fixedRow = true;
1005         p++;
1006     }
1007 
1008     // Malformed ?
1009     if (p == length)
1010         return;
1011 
1012     uint p2 = p;
1013     while (p < length) {
1014         if (!string[p++].isDigit())
1015             return;
1016     }
1017 
1018     bool ok;
1019     int y = string.mid(p2, p - p2).toInt(&ok);
1020 
1021     //limit the y-value
1022     //Q_ASSERT(y >= 1 && y <= KS_rowMax);
1023     if (!ok || y < 1)
1024         return;
1025     if (y > KS_rowMax)
1026         y = KS_rowMax;
1027 
1028     m_point = QPoint(x, y);
1029 }
1030 
1031 Region::Point::~Point()
1032 {
1033 }
1034 
1035 QString Region::Point::name(Sheet* originSheet) const
1036 {
1037     QString name;
1038     if (m_sheet && m_sheet != originSheet) {
1039         name.append(m_sheet->sheetName());
1040         name.replace('\'', "''");
1041         if (name.contains('!') || name.contains(' ') || name.contains(';') || name.contains('$'))
1042             name = '\'' + name + '\'';
1043         name.append('!');
1044     }
1045     if (m_fixedColumn)
1046         name.append('$');
1047     name.append(Cell::columnName(m_point.x()));
1048     if (m_fixedRow)
1049         name.append('$');
1050     name.append(QString::number(m_point.y()));
1051     return name;
1052 }
1053 
1054 bool Region::Point::contains(const QPoint& point) const
1055 {
1056     return (m_point == point);
1057 }
1058 
1059 bool Region::Point::contains(const QRect& range) const
1060 {
1061     return (range.width() == 1) && (range.height() == 1) && (range.topLeft() == m_point);
1062 }
1063 
1064 Cell Region::Point::cell() const
1065 {
1066     return Cell(m_sheet, m_point);
1067 }
1068 
1069 /***************************************************************************
1070   class Range
1071 ****************************************************************************/
1072 
1073 Region::Range::Range(const QRect& rect)
1074         : Region::Element()
1075         , m_range(rect)
1076         , m_fixedTop(false)
1077         , m_fixedLeft(false)
1078         , m_fixedBottom(false)
1079         , m_fixedRight(false)
1080 {
1081     if (m_range.right() > KS_colMax)
1082         m_range.setRight(KS_colMax);
1083     if (m_range.bottom() > KS_rowMax)
1084         m_range.setBottom(KS_rowMax);
1085 }
1086 
1087 Region::Range::Range(const Calligra::Sheets::Region::Point& ul, const Calligra::Sheets::Region::Point& lr)
1088         : Region::Element()
1089         , m_fixedTop(false)
1090         , m_fixedLeft(false)
1091         , m_fixedBottom(false)
1092         , m_fixedRight(false)
1093 {
1094     if (!ul.isValid() || !lr.isValid())
1095         return;
1096     m_range = QRect(ul.pos(), lr.pos());
1097     m_fixedTop    = ul.isRowFixed();
1098     m_fixedLeft   = ul.isColumnFixed();
1099     m_fixedBottom = lr.isRowFixed();
1100     m_fixedRight  = lr.isColumnFixed();
1101 }
1102 
1103 Region::Range::Range(const QString& sRange)
1104         : Region::Element()
1105         , m_fixedTop(false)
1106         , m_fixedLeft(false)
1107         , m_fixedBottom(false)
1108         , m_fixedRight(false)
1109 {
1110     int delimiterPos = sRange.indexOf(':');
1111     if (delimiterPos == -1)
1112         return;
1113 
1114     Region::Point ul(sRange.left(delimiterPos));
1115     Region::Point lr(sRange.mid(delimiterPos + 1));
1116 
1117     if (!ul.isValid() || !lr.isValid())
1118         return;
1119     m_range = QRect(ul.pos(), lr.pos());
1120     m_fixedTop    = ul.isRowFixed();
1121     m_fixedLeft   = ul.isColumnFixed();
1122     m_fixedBottom = lr.isRowFixed();
1123     m_fixedRight  = lr.isColumnFixed();
1124 }
1125 
1126 Region::Range::~Range()
1127 {
1128 }
1129 
1130 bool Region::Range::isColumn() const
1131 {
1132     return (m_range.top() == 1 && m_range.bottom() == KS_rowMax);
1133 }
1134 
1135 bool Region::Range::isRow() const
1136 {
1137     return (m_range.left() == 1 && m_range.right() == KS_colMax);
1138 }
1139 
1140 bool Region::Range::isAll() const
1141 {
1142     return (m_range == QRect(1, 1, KS_colMax, KS_rowMax));
1143 }
1144 
1145 bool Region::Range::contains(const QPoint& point) const
1146 {
1147     return m_range.contains(point);
1148 }
1149 
1150 bool Region::Range::contains(const QRect& range) const
1151 {
1152     return m_range.contains(normalized(range));
1153 }
1154 
1155 QString Region::Range::name(Sheet* originSheet) const
1156 {
1157     QString name;
1158     if (m_sheet && m_sheet != originSheet) {
1159         name.append(m_sheet->sheetName());
1160         name.replace('\'', "''");
1161         if (name.contains('!') || name.contains(' ') || name.contains(';') || name.contains('$'))
1162             name = '\'' + name + '\'';
1163         name.append('!');
1164     }
1165     if (m_fixedLeft)
1166         name.append('$');
1167     name.append(Cell::columnName(m_range.left()));
1168     if (m_fixedTop)
1169         name.append('$');
1170     name.append(QString::number(m_range.top()));
1171     name.append(':');
1172     if (m_fixedRight)
1173         name.append('$');
1174     name.append(Cell::columnName(m_range.right()));
1175     if (m_fixedBottom)
1176         name.append('$');
1177     name.append(QString::number(m_range.bottom()));
1178     return name;
1179 }
1180 
1181 } // namespace Sheets
1182 } // namespace Calligra