1 /* This file is part of the KDE project
2    Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
3    Copyright 2005-2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU Library General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 
20 // Local
21 #include "Region.h"
22 
23 #include <QRegExp>
24 #include <QStringList>
25 
26 #include "SheetsDebug.h"
27 #include "Cell.h"
28 #include "calligra_sheets_limits.h"
29 #include "Map.h"
30 #include "NamedAreaManager.h"
31 #include "Sheet.h"
32 #include "Util.h"
33 
34 namespace Calligra
35 {
36 namespace Sheets
37 {
38 
39 class Q_DECL_HIDDEN Region::Private : public QSharedData
40 {
41 public:
Private()42     Private()
43             : map(0),
44             cells(QList<Element*>()) {
45     }
46 
47     const Map* map;
48     mutable QList<Element*> cells;
49 };
50 
51 
52 /***************************************************************************
53   class Region
54 ****************************************************************************/
55 
Region()56 Region::Region()
57 {
58     d = new Private();
59 }
60 
Region(const QString & string,const Map * map,Sheet * fallbackSheet)61 Region::Region(const QString& string, const Map* map, Sheet* fallbackSheet)
62 {
63     d = new Private();
64     d->map = map;
65 
66     if (string.isEmpty()) {
67         return;
68     }
69     // FIXME Stefan: Does not respect quoted names!
70     QStringList substrings = string.split(';');
71     QStringList::ConstIterator end = substrings.constEnd();
72     for (QStringList::ConstIterator it = substrings.constBegin(); it != end; ++it) {
73         QString sRegion = *it;
74 
75         // check for a named area first
76         const Region namedAreaRegion = map ? map->namedAreaManager()->namedArea(sRegion) : Region();
77         if (namedAreaRegion.isValid()) {
78             ConstIterator end(namedAreaRegion.d->cells.constEnd());
79             for (ConstIterator it = namedAreaRegion.d->cells.constBegin(); it != end; ++it) {
80                 Element *element = *it;
81                 if (element->type() == Element::Point) {
82                     Point* point = static_cast<Point*>(element);
83                     d->cells.append(createPoint(*point));
84                 } else {
85                     Range* range = static_cast<Range*>(element);
86                     d->cells.append(createRange(*range));
87                 }
88             }
89             continue;
90         }
91 
92         // Single cell or cell range
93         int delimiterPos = sRegion.indexOf(':');
94         if (delimiterPos > -1) {
95             // range
96             QString sUL = sRegion.left(delimiterPos);
97             QString sLR = sRegion.mid(delimiterPos + 1);
98 
99             Sheet* firstSheet = map ? filterSheetName(sUL) : 0;
100             Sheet* lastSheet = map ? filterSheetName(sLR) : 0;
101             // TODO: lastSheet is silently ignored if it is different from firstSheet
102 
103             // Still has the sheet name separator?
104             if (sUL.contains('!') || sLR.contains('!'))
105                 return;
106 
107             if (!firstSheet)
108                 firstSheet = fallbackSheet;
109             if (!lastSheet)
110                 lastSheet = fallbackSheet;
111 
112             Point ul(sUL);
113             Point lr(sLR);
114 
115             if (ul.isValid() && lr.isValid()) {
116                 Range* range = createRange(ul, lr);
117                 if (firstSheet) range->setSheet(firstSheet);
118                 d->cells.append(range);
119             } else if (ul.isValid()) {
120                 Point* point = createPoint(ul);
121                 if (firstSheet) point->setSheet(firstSheet);
122                 d->cells.append(point);
123             } else { // lr.isValid()
124                 Point* point = createPoint(lr);
125                 if (firstSheet) point->setSheet(firstSheet);
126                 d->cells.append(point);
127             }
128         } else {
129             // single cell
130             Sheet* sheet = map ? filterSheetName(sRegion) : 0;
131             // Still has the sheet name separator?
132             if (sRegion.contains('!'))
133                 return;
134             if (!sheet)
135                 sheet = fallbackSheet;
136             Point* point = createPoint(sRegion);
137             if(sheet) point->setSheet(sheet);
138             d->cells.append(point);
139         }
140     }
141 }
142 
Region(const QRect & rect,Sheet * sheet)143 Region::Region(const QRect& rect, Sheet* sheet)
144 {
145     d = new Private();
146 
147     Q_ASSERT(!rect.isNull());
148     if (rect.isNull()) {
149         errorSheets << "Region::Region(const QRect&): QRect is empty!" << endl;
150         return;
151     }
152     add(rect, sheet);
153 }
154 
Region(const QPoint & point,Sheet * sheet)155 Region::Region(const QPoint& point, Sheet* sheet)
156 {
157     d = new Private();
158 
159     Q_ASSERT(!point.isNull());
160     if (point.isNull()) {
161         errorSheets << "Region::Region(const QPoint&): QPoint is empty!" << endl;
162         return;
163     }
164     add(point, sheet);
165 }
166 
Region(const Region & list)167 Region::Region(const Region& list)
168 {
169     d = new Private();
170     d->map = list.d->map;
171     d->cells.reserve(list.d->cells.size());
172     ConstIterator end(list.d->cells.constEnd());
173     for (ConstIterator it = list.d->cells.constBegin(); it != end; ++it) {
174         Element *element = *it;
175         if (element->type() == Element::Point) {
176             Point* point = static_cast<Point*>(element);
177             d->cells.append(createPoint(*point));
178         } else {
179             Range* range = static_cast<Range*>(element);
180             d->cells.append(createRange(*range));
181         }
182     }
183 }
184 
Region(int x,int y,Sheet * sheet)185 Region::Region(int x, int y, Sheet* sheet)
186 {
187     d = new Private();
188 
189     Q_ASSERT(isValid(QPoint(x, y)));
190     if (!isValid(QPoint(x, y))) {
191         errorSheets << "Region::Region(" << x << ", " << y << "): Coordinates are invalid!" << endl;
192         return;
193     }
194     add(QPoint(x, y), sheet);
195 }
196 
Region(int x,int y,int width,int height,Sheet * sheet)197 Region::Region(int x, int y, int width, int height, Sheet* sheet)
198 {
199     d = new Private();
200 
201     Q_ASSERT(isValid(QRect(x, y, width, height)));
202     if (!isValid(QRect(x, y, width, height))) {
203         errorSheets << "Region::Region(" << x << ", " << y << ", " << width << ", " << height << "): Dimensions are invalid!" << endl;
204         return;
205     }
206     add(QRect(x, y, width, height), sheet);
207 }
208 
209 
~Region()210 Region::~Region()
211 {
212     qDeleteAll(d->cells);
213 }
214 
rects() const215 QVector<QRect> Region::rects() const
216 {
217     QVector<QRect> cellRects;
218     foreach(Element *element, d->cells) {
219         cellRects.append(element->rect());
220     }
221     return cellRects;
222 }
223 
map() const224 const Map* Region::map() const
225 {
226     Q_ASSERT(d->map);
227     return d->map;
228 }
229 
setMap(const Map * map)230 void Region::setMap(const Map* map)
231 {
232     d->map = map;
233 }
234 
isValid() const235 bool Region::isValid() const
236 {
237     if (d->cells.isEmpty())
238         return false;
239     ConstIterator end = d->cells.constEnd();
240     for (ConstIterator it = d->cells.constBegin(); it != end; ++it) {
241         if (!(*it)->isValid())
242             return false;
243     }
244     return true;
245 }
246 
isSingular() const247 bool Region::isSingular() const
248 {
249     if (d->cells.isEmpty() || d->cells.count() > 1 || (*d->cells.constBegin())->type() != Element::Point) {
250         return false;
251     }
252     return true;
253 }
254 
isContiguous() const255 bool Region::isContiguous() const
256 {
257     if (d->cells.count() != 1 || !isValid()) {
258         return false;
259     }
260     return true;
261 }
262 
name(Sheet * originSheet) const263 QString Region::name(Sheet* originSheet) const
264 {
265     QStringList names;
266     ConstIterator endOfList(d->cells.constEnd());
267     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
268         Element *element = *it;
269         names += element->name(originSheet);
270     }
271     return names.isEmpty() ? "" : names.join(";");
272 }
273 
add(const QPoint & point,Sheet * sheet)274 Region::Element* Region::add(const QPoint& point, Sheet* sheet)
275 {
276     return insert(d->cells.count(), point, sheet, false);
277 }
278 
add(const QRect & range,Sheet * sheet)279 Region::Element* Region::add(const QRect& range, Sheet* sheet)
280 {
281     const QRect normalizedRange = normalized(range);
282     if (normalizedRange.width() == 0 || normalizedRange.height() == 0) {
283         return 0;
284     }
285     if (normalizedRange.size() == QSize(1, 1)) {
286         return add(normalizedRange.topLeft(), sheet);
287     }
288     return insert(d->cells.count(), normalizedRange, sheet, false);
289 }
290 
add(const Region & region,Sheet * sheet)291 Region::Element* Region::add(const Region& region, Sheet* sheet)
292 {
293     ConstIterator endOfList(region.d->cells.constEnd());
294     for (ConstIterator it = region.d->cells.constBegin(); it != endOfList; ++it) {
295         add((*it)->rect(), (*it)->sheet() ? (*it)->sheet() : sheet);
296     }
297     return d->cells.isEmpty() ? 0 : d->cells.last();
298 }
299 
sub(const QPoint & point,Sheet * sheet)300 void Region::sub(const QPoint& point, Sheet* sheet)
301 {
302     // TODO Stefan: Improve!
303     Iterator endOfList(d->cells.end());
304     for (Iterator it = d->cells.begin(); it != endOfList; ++it) {
305         Element *element = *it;
306         if (element->sheet() != sheet) {
307             continue;
308         }
309         if (element->rect() == QRect(point, point)) {
310             delete element;
311             d->cells.removeAll(element);
312             break;
313         }
314     }
315 }
316 
sub(const QRect & range,Sheet * sheet)317 void Region::sub(const QRect& range, Sheet* sheet)
318 {
319     const QRect normalizedRange = normalized(range);
320     // TODO Stefan: Improve!
321     Iterator endOfList(d->cells.end());
322     for (Iterator it = d->cells.begin(); it != endOfList; ++it) {
323         Element *element = *it;
324         if (element->sheet() != sheet) {
325             continue;
326         }
327         if (element->rect() == normalizedRange) {
328             delete element;
329             d->cells.removeAll(element);
330             break;
331         }
332     }
333 }
334 
sub(const Region & region)335 void Region::sub(const Region& region)
336 {
337     ConstIterator endOfList(region.constEnd());
338     for (ConstIterator it = region.constBegin(); it != endOfList; ++it) {
339         Element *element = *it;
340         if (element->type() == Element::Point) {
341             Point* point = static_cast<Point*>(element);
342             sub(Region(point->pos()));
343         } else {
344             sub(Region(element->rect()));
345         }
346     }
347 }
348 
intersected(const Region & region) const349 Region Region::intersected(const Region& region) const
350 {
351   // Special case 1: one of the regions is empty
352   if (region.isEmpty()) return region;
353   if (isEmpty()) return Region();
354 
355   // Special case 2: If the region contains more elements than this one, do this operation in reverse (optimization)
356   if (region.cells().size() > cells().size())
357     return region.intersected (*this);
358 
359   // Most common case: the region contains only one rectangle
360   Region result;
361   QVector<QRect> rects = region.rects();
362   if (rects.size() == 1) {
363     QRect rect = rects[0];
364     Sheet *s = region.cells()[0]->sheet();
365     // intersect each element with the rectangle
366     foreach(Element *element, d->cells) {
367       if (element->sheet() != s) continue;
368       if (element->type() == Element::Point) {
369         Point* point = static_cast<Point*>(element);
370         if (rect.contains (point->pos()))
371           result.add (point->pos(), s);
372       } else {
373         QRect rect2 = element->rect();
374         if (rect2.intersects (rect))
375           result.add (rect2.intersected (rect), s);
376       }
377     }
378     return result;
379   }
380 
381     // Generic case. TODO: optimize this better - generating a ton of single-cell regions is slow
382     ConstIterator end(region.constEnd());
383     for (ConstIterator it = region.constBegin(); it != end; ++it) {
384         Element *element = *it;
385         if (element->type() == Element::Point) {
386             Point* point = static_cast<Point*>(element);
387             if(contains(point->pos(), element->sheet()))
388                 result.add(point->pos(), element->sheet());
389         } else {
390             QRect rect = element->rect();
391             for(int c = rect.top(); c <= rect.bottom(); ++c) {
392                 for(int r = rect.left(); r <= rect.right(); ++r) {
393                     QPoint p(r,c);
394                     if(contains(p, element->sheet()))
395                         result.add(p, element->sheet());
396                 }
397             }
398         }
399     }
400     return result;
401 }
402 
intersectedWithRow(int row) const403 Region Region::intersectedWithRow(int row) const
404 {
405     Region result;
406     ConstIterator end(constEnd());
407     for (ConstIterator it = constBegin(); it != end; ++it) {
408         Element *element = *it;
409         if (element->type() == Element::Point) {
410             Point* point = static_cast<Point*>(element);
411             if (point->pos().y() == row)
412                 result.add(point->pos(), point->sheet());
413         } else {
414             QRect rect = element->rect();
415             if (rect.top() <= row && rect.bottom() >= row) {
416                 result.add(QRect(rect.left(), row, rect.width(), 1), element->sheet());
417             }
418         }
419     }
420     return result;
421 }
422 
eor(const QPoint & point,Sheet * sheet)423 Region::Element* Region::eor(const QPoint& point, Sheet* sheet)
424 {
425     bool containsPoint = false;
426 
427     int index = 0;
428     while (index < d->cells.count()) {
429         if (!d->cells[index]->contains(point)) {
430             ++index;
431             continue;
432         }
433         containsPoint = true;
434         int x = point.x();
435         int y = point.y();
436         QRect fullRange = d->cells[index]->rect();
437         delete d->cells.takeAt(index);
438 
439         // top range
440         int left = fullRange.left();
441         int top = fullRange.top();
442         int width = fullRange.width();
443         int height = y - top;
444         if (height > 0) {
445             insert(index, QRect(left, top, width, height), sheet);
446         }
447         // left range
448         left = fullRange.left();
449         top = y;
450         width = qMax(0, x - left);
451         height = 1;
452         if (width > 0) {
453             insert(index, QRect(left, top, width, height), sheet);
454         }
455         // right range
456         left = qMin(x + 1, fullRange.right());
457         top = y;
458         width = qMax(0, fullRange.right() - x);
459         height = 1;
460         if (width > 0) {
461             insert(index, QRect(left, top, width, height), sheet);
462         }
463         // bottom range
464         left = fullRange.left();
465         top = y + 1;
466         width = fullRange.width();
467         height = qMax(0, fullRange.bottom() - y);
468         if (height > 0) {
469             insert(index, QRect(left, top, width, height), sheet);
470         }
471         return d->cells[index];
472     }
473 
474     if (!containsPoint) {
475         return add(point, sheet);
476     }
477     return 0;
478 }
479 
insert(int pos,const QPoint & point,Sheet * sheet,bool multi)480 Region::Element* Region::insert(int pos, const QPoint& point, Sheet* sheet, bool multi)
481 {
482     if (point.x() < 1 || point.y() < 1) {
483         return 0;
484     }
485     // Keep boundaries.
486     pos = qBound(0, pos, cells().count());
487 
488     bool containsPoint = false;
489 //   bool adjacentPoint = false;
490 //   QRect neighbour;
491 
492     // we don't have to check for occurrences?
493     if (multi) {
494         Point* rpoint = createPoint(point);
495         rpoint->setSheet(sheet);
496         d->cells.insert(pos, rpoint);
497         return d->cells[pos];
498     }
499 
500     ConstIterator endOfList(d->cells.constEnd());
501     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
502         Element *element = *it;
503         if (sheet && sheet != element->sheet()) {
504             continue;
505         }
506         if (element->contains(point)) {
507             containsPoint = true;
508             break;
509         }
510         /*    else
511             {
512               neighbour = element->rect();
513               neighbour.setTopLeft(neighbour.topLeft() - QPoint(1,1));
514               neighbour.setBottomRight(neighbour.bottomRight() + QPoint(1,1));
515               if (neighbour.contains(point))
516               {
517                 adjacentPoint = true; // TODO Stefan: Implement!
518                 break;
519               }
520             }*/
521     }
522     if (!containsPoint) {
523         Point* rpoint = createPoint(point);
524         rpoint->setSheet(sheet);
525         d->cells.insert(pos, rpoint);
526         return d->cells[pos];
527     }
528     return 0;
529 }
530 
insert(int pos,const QRect & range,Sheet * sheet,bool multi)531 Region::Element* Region::insert(int pos, const QRect& range, Sheet* sheet, bool multi)
532 {
533     // Keep boundaries.
534     pos = qBound(0, pos, cells().count());
535 
536     const QRect normalizedRange = normalized(range);
537     if (normalizedRange.size() == QSize(1, 1)) {
538         return insert(pos, normalizedRange.topLeft(), sheet);
539     }
540 
541     if (multi) {
542         Range* rrange = createRange(normalizedRange);
543         rrange->setSheet(sheet);
544         d->cells.insert(pos, rrange);
545         return d->cells[pos];
546     }
547 
548     bool containsRange = false;
549 
550     for (int index = 0; index < d->cells.count(); ++index) {
551         if (sheet && sheet != d->cells[index]->sheet()) {
552             continue;
553         }
554         if (d->cells[index]->contains(normalizedRange)) {
555             containsRange = true;
556         } else if (normalizedRange.contains(d->cells[index]->rect())) {
557             delete d->cells.takeAt(index--);
558             continue;
559         }
560     }
561     if (!containsRange) {
562         // Keep boundaries.
563         pos = qBound(0, pos, cells().count());
564 
565         Range* rrange = createRange(normalizedRange);
566         rrange->setSheet(sheet);
567         d->cells.insert(pos, rrange);
568         return d->cells[pos];
569     }
570     return 0;
571 }
572 
columnsSelected() const573 QSet<int> Region::columnsSelected() const
574 {
575     QSet<int> result;
576     ConstIterator endOfList(d->cells.constEnd());
577     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
578         if ((*it)->isColumn()) {
579             const QRect range = (*it)->rect();
580             const int right = range.right();
581             for (int col = range.left(); col <= right; ++col) {
582                 result << col;
583             }
584         }
585     }
586     return result;
587 }
588 
rowsSelected() const589 QSet<int> Region::rowsSelected() const
590 {
591     QSet<int> result;
592     ConstIterator endOfList(d->cells.constEnd());
593     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
594         if ((*it)->isRow()) {
595             const QRect range = (*it)->rect();
596             const int bottom = range.bottom();
597             for (int row = range.top(); row <= bottom; ++row) {
598                 result << row;
599             }
600         }
601     }
602     return result;
603 }
604 
columnsAffected() const605 QSet<int> Region::columnsAffected() const
606 {
607     QSet<int> result;
608     ConstIterator endOfList(d->cells.constEnd());
609     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
610         const QRect range = (*it)->rect();
611         const int right = range.right();
612         for (int col = range.left(); col <= right; ++col) {
613             result << col;
614         }
615     }
616     return result;
617 }
618 
rowsAffected() const619 QSet<int> Region::rowsAffected() const
620 {
621     QSet<int> result;
622     ConstIterator endOfList(d->cells.constEnd());
623     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
624         const QRect range = (*it)->rect();
625         const int bottom = range.bottom();
626         for (int row = range.top(); row <= bottom; ++row) {
627             result << row;
628         }
629     }
630     return result;
631 }
632 
isColumnSelected(uint col) const633 bool Region::isColumnSelected(uint col) const
634 {
635     ConstIterator endOfList(d->cells.constEnd());
636     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
637         Element *element = *it;
638         QRect region = element->rect();
639         if ((col == 0 || ((int)col >= region.left() && (int)col <= region.right())) &&
640                 region.top() == 1 && region.bottom() == KS_rowMax) {
641             return true;
642         }
643     }
644     return false;
645 }
646 
isRowSelected(uint row) const647 bool Region::isRowSelected(uint row) const
648 {
649     ConstIterator endOfList(d->cells.constEnd());
650     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
651         Element *element = *it;
652         QRect region = element->rect();
653         if ((row == 0 || ((int)row >= region.top() && (int)row <= region.bottom())) &&
654                 region.left() == 1 && region.right() == KS_colMax) {
655             return true;
656         }
657     }
658     return false;
659 }
660 
isColumnOrRowSelected() const661 bool Region::isColumnOrRowSelected() const
662 {
663     ConstIterator endOfList(d->cells.constEnd());
664     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
665         Element *element = *it;
666         QRect region = element->rect();
667         if ((region.top() == 1 && region.bottom() == KS_rowMax) ||
668                 (region.left() == 1 && region.right() == KS_colMax)) {
669             return true;
670         }
671     }
672     return false;
673 }
674 
isAllSelected() const675 bool Region::isAllSelected() const
676 {
677     if (d->cells.count() != 1)
678         return false;
679     Q_ASSERT(d->cells.first());
680     return d->cells.first()->isAll();
681 }
682 
contains(const QPoint & point,Sheet * sheet) const683 bool Region::contains(const QPoint& point, Sheet* sheet) const
684 {
685     if (d->cells.isEmpty()) {
686         return false;
687     }
688     ConstIterator endOfList(d->cells.constEnd());
689     for (ConstIterator it = d->cells.constBegin(); it != endOfList; ++it) {
690         Element *element = *it;
691         if (element->contains(point)) {
692             if (sheet && element->sheet() != sheet) {
693                 return false;
694             }
695             return true;
696         }
697     }
698     return false;
699 }
700 
isEmpty() const701 bool Region::isEmpty() const
702 {
703     return d->cells.isEmpty();
704 }
705 
clear()706 void Region::clear()
707 {
708     qDeleteAll(d->cells);
709     d->cells.clear();
710 }
711 
firstRange() const712 QRect Region::firstRange() const
713 {
714     if (!isValid())
715         return QRect();
716     return d->cells.value(0)->rect();
717 }
718 
lastRange() const719 QRect Region::lastRange() const
720 {
721     if (!isValid())
722         return QRect();
723     return d->cells.value(d->cells.count() - 1)->rect();
724 }
725 
firstSheet() const726 Sheet* Region::firstSheet() const
727 {
728     if (!isValid())
729         return 0;
730     return d->cells.value(0)->sheet();
731 }
732 
lastSheet() const733 Sheet* Region::lastSheet() const
734 {
735     if (!isValid())
736         return 0;
737     return d->cells.value(d->cells.count() - 1)->sheet();
738 }
739 
boundingRect() const740 QRect Region::boundingRect() const
741 {
742     int left   = KS_colMax;
743     int right  = 1;
744     int top    = KS_rowMax;
745     int bottom = 1;
746     Region::ConstIterator endOfList = cells().constEnd();
747     for (Region::ConstIterator it = cells().constBegin(); it != endOfList; ++it) {
748         QRect range = (*it)->rect();
749         if (range.left() < left) {
750             left = range.left();
751         }
752         if (range.right() > right) {
753             right = range.right();
754         }
755         if (range.top() < top) {
756             top = range.top();
757         }
758         if (range.bottom() > bottom) {
759             bottom = range.bottom();
760         }
761     }
762     return QRect(left, top, right - left + 1, bottom - top + 1);
763 }
764 
normalized(const QRect & rect)765 QRect Region::normalized(const QRect& rect)
766 {
767     QRect normalizedRect(rect);
768     if (rect.left() > rect.right()) {
769         normalizedRect.setLeft(rect.right());
770         normalizedRect.setRight(rect.left());
771     }
772     if (rect.top() > rect.bottom()) {
773         normalizedRect.setTop(rect.bottom());
774         normalizedRect.setBottom(rect.top());
775     }
776     if (rect.right() > KS_colMax) {
777         normalizedRect.setRight(KS_colMax);
778     }
779     if (rect.bottom() > KS_rowMax) {
780         normalizedRect.setBottom(KS_rowMax);
781     }
782     return normalizedRect;
783 }
784 
constBegin() const785 Region::ConstIterator Region::constBegin() const
786 {
787     return d->cells.constBegin();
788 }
789 
constEnd() const790 Region::ConstIterator Region::constEnd() const
791 {
792     return d->cells.constEnd();
793 }
794 
isValid(const QPoint & point)795 bool Region::isValid(const QPoint& point)
796 {
797     if (point.x() < 1 || point.y() < 1 ||
798             point.x() > KS_colMax ||  point.y() > KS_rowMax)
799         return false;
800     else
801         return true;
802 }
803 
isValid(const QRect & rect)804 bool Region::isValid(const QRect& rect)
805 {
806     if (!isValid(rect.topLeft()) || !isValid(rect.bottomRight()) ||
807             rect.width() == 0 || rect.height() == 0)
808         return false;
809     else
810         return true;
811 }
812 
cells() const813 QList<Region::Element*>& Region::cells() const
814 {
815     return d->cells;
816 }
817 
operator ==(const Region & other) const818 bool Region::operator==(const Region& other) const
819 {
820     if (d->cells.count() != other.d->cells.count())
821         return false;
822     ConstIterator endOfList(d->cells.constEnd());
823     ConstIterator endOfOtherList(other.d->cells.constEnd());
824     ConstIterator it = d->cells.constBegin();
825     ConstIterator it2 = other.d->cells.constBegin();
826     while (it != endOfList && it2 != endOfOtherList) {
827         if ((*it)->sheet() != (*it2)->sheet())
828             return false;
829         if ((*it++)->rect() != (*it2++)->rect())
830             return false;
831     }
832     return true;
833 }
834 
operator =(const Region & other)835 void Region::operator=(const Region& other)
836 {
837     d->map = other.d->map;
838     clear();
839     ConstIterator end(other.d->cells.constEnd());
840     for (ConstIterator it = other.d->cells.constBegin(); it != end; ++it) {
841         Element *element = *it;
842         if (element->type() == Element::Point) {
843             Point* point = static_cast<Point*>(element);
844             d->cells.append(createPoint(*point));
845         } else {
846             Range* range = static_cast<Range*>(element);
847             d->cells.append(createRange(*range));
848         }
849     }
850 }
851 
filterSheetName(QString & sRegion)852 Sheet* Region::filterSheetName(QString& sRegion)
853 {
854     Sheet* sheet = 0;
855     int delimiterPos = sRegion.lastIndexOf('!');
856     if (delimiterPos < 0)
857         delimiterPos = sRegion.lastIndexOf('.');
858     if (delimiterPos > -1) {
859         QString sheetName = sRegion.left(delimiterPos);
860         sheet = d->map->findSheet(sheetName);
861         // try again without apostrophes
862         while(!sheet && sheetName.count() > 2 && sheetName[0] == '\'' && sheetName[sheetName.count()-1] == '\'') {
863             sheetName = sheetName.mid(1, sheetName.count() - 2);
864             sheet = d->map->findSheet(sheetName);
865         }
866         // remove the sheet name, incl. '!', from the string
867         if (sheet)
868             sRegion = sRegion.right(sRegion.length() - delimiterPos - 1);
869     }
870     return sheet;
871 }
872 
createPoint(const QPoint & point) const873 Region::Point* Region::createPoint(const QPoint& point) const
874 {
875     return new Point(point);
876 }
877 
createPoint(const QString & string) const878 Region::Point* Region::createPoint(const QString& string) const
879 {
880     return new Point(string);
881 }
882 
createPoint(const Point & point) const883 Region::Point* Region::createPoint(const Point& point) const
884 {
885     return new Point(point);
886 }
887 
createRange(const QRect & rect) const888 Region::Range* Region::createRange(const QRect& rect) const
889 {
890     return new Range(rect);
891 }
892 
createRange(const Point & tl,const Point & br) const893 Region::Range* Region::createRange(const Point& tl, const Point& br) const
894 {
895     return new Range(tl, br);
896 }
897 
createRange(const QString & string) const898 Region::Range* Region::createRange(const QString& string) const
899 {
900     return new Range(string);
901 }
902 
createRange(const Range & range) const903 Region::Range* Region::createRange(const Range& range) const
904 {
905     return new Range(range);
906 }
907 
908 /***************************************************************************
909   class Element
910 ****************************************************************************/
911 
Element()912 Region::Element::Element()
913         : m_sheet(0)
914 {
915 }
916 
~Element()917 Region::Element::~Element()
918 {
919 }
920 
921 
922 /***************************************************************************
923   class Point
924 ****************************************************************************/
firstNonCharPos(const QString & s,int pos=0)925 static int firstNonCharPos(const QString& s, int pos = 0)
926 {
927     int result = -1;
928     const QChar *data = s.constData();
929     int i = 0;
930     while (!data->isNull()) {
931         if (i >= pos) {
932             char c = data->unicode();
933             if (c < 'A' || c > 'z' || (c < 'a' && c > 'Z')) {
934                 result = i;
935                 break;
936             }
937         }
938         ++data;
939         ++i;
940     }
941     return result;
942 }
943 
Point(const QPoint & point)944 Region::Point::Point(const QPoint& point)
945         : Region::Element()
946         , m_point(point)
947         , m_fixedColumn(false)
948         , m_fixedRow(false)
949 {
950     if (m_point.x() > KS_colMax)
951         m_point.setX(KS_colMax);
952     if (m_point.y() > KS_rowMax)
953         m_point.setY(KS_rowMax);
954 }
955 
Point(const QString & string)956 Region::Point::Point(const QString& string)
957         : Region::Element()
958         , m_fixedColumn(false)
959         , m_fixedRow(false)
960 {
961     const uint length = string.length();
962     if (length == 0)
963         return;
964 
965     uint p = 0;
966 
967     // Fixed ?
968     if (string[0] == QChar('$', 0)) {
969         m_fixedColumn = true;
970         p++;
971     }
972 
973     // Malformed ?
974     if (p == length)
975         return;
976 
977     if ((string[p] < QChar('A',0) || string[p] > QChar('Z',0)) && (string[p] < QChar('a', 0) || string[p] > QChar('z', 0)))
978         return;
979 
980     //default is error
981     int x = -1;
982     //search for the first character != text
983     int result = firstNonCharPos(string, p);
984 
985     //get the column number for the character between actual position and the first non text character
986     if (result != -1)
987         x = Util::decodeColumnLabelText(string.mid(p, result - p));     // x is defined now
988     else  // If there isn't any, then this is not a point -> return
989         return;
990     p = result;
991 
992     //limit the x-value
993     //Q_ASSERT(x >= 1 && x <= KS_colMax);
994     if (x < 1)
995         return;
996     if (x > KS_colMax)
997         x = KS_colMax;
998 
999     // 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 
~Point()1031 Region::Point::~Point()
1032 {
1033 }
1034 
name(Sheet * originSheet) const1035 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 
contains(const QPoint & point) const1054 bool Region::Point::contains(const QPoint& point) const
1055 {
1056     return (m_point == point);
1057 }
1058 
contains(const QRect & range) const1059 bool Region::Point::contains(const QRect& range) const
1060 {
1061     return (range.width() == 1) && (range.height() == 1) && (range.topLeft() == m_point);
1062 }
1063 
cell() const1064 Cell Region::Point::cell() const
1065 {
1066     return Cell(m_sheet, m_point);
1067 }
1068 
1069 /***************************************************************************
1070   class Range
1071 ****************************************************************************/
1072 
Range(const QRect & rect)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 
Range(const Calligra::Sheets::Region::Point & ul,const Calligra::Sheets::Region::Point & lr)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 
Range(const QString & sRange)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 
~Range()1126 Region::Range::~Range()
1127 {
1128 }
1129 
isColumn() const1130 bool Region::Range::isColumn() const
1131 {
1132     return (m_range.top() == 1 && m_range.bottom() == KS_rowMax);
1133 }
1134 
isRow() const1135 bool Region::Range::isRow() const
1136 {
1137     return (m_range.left() == 1 && m_range.right() == KS_colMax);
1138 }
1139 
isAll() const1140 bool Region::Range::isAll() const
1141 {
1142     return (m_range == QRect(1, 1, KS_colMax, KS_rowMax));
1143 }
1144 
contains(const QPoint & point) const1145 bool Region::Range::contains(const QPoint& point) const
1146 {
1147     return m_range.contains(point);
1148 }
1149 
contains(const QRect & range) const1150 bool Region::Range::contains(const QRect& range) const
1151 {
1152     return m_range.contains(normalized(range));
1153 }
1154 
name(Sheet * originSheet) const1155 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
1183