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