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