1/* This file is part of the KDE project
2 Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
3 Copyright 2006,2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library 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 GNU
13 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 library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21// Local
22#include "StyleStorage.h"
23
24#include <QCache>
25#include <QRegion>
26#include <QTimer>
27#include <QRunnable>
28#ifdef CALLIGRA_SHEETS_MT
29#include <QMutex>
30#include <QMutexLocker>
31#endif
32
33#include "Global.h"
34#include "Map.h"
35#include "OdfSavingContext.h"
36#include "RTree.h"
37#include "Style.h"
38#include "StyleManager.h"
39#include "RectStorage.h"
40
41static const int g_maximumCachedStyles = 10000;
42
43using namespace Calligra::Sheets;
44
45class KDE_NO_EXPORT StyleStorage::Private
46{
47public:
48 Private()
49#ifdef CALLIGRA_SHEETS_MT
50 : cacheMutex(QMutex::Recursive)
51#endif
52 {}
53 Map* map;
54 RTree<SharedSubStyle> tree;
55 QMap<int, bool> usedColumns; // FIXME Stefan: Use QList and qUpperBound() for insertion.
56 QMap<int, bool> usedRows;
57 QRegion usedArea;
58 QHash<Style::Key, QList<SharedSubStyle> > subStyles;
59 QMap<int, QPair<QRectF, SharedSubStyle> > possibleGarbage;
60 QCache<QPoint, Style> cache;
61 QRegion cachedArea;
62 StyleStorageLoaderJob* loader;
63#ifdef CALLIGRA_SHEETS_MT
64 QMutex cacheMutex;
65#endif
66
67 void ensureLoaded();
68};
69
70class Calligra::Sheets::StyleStorageLoaderJob : public QRunnable
71{
72public:
73 StyleStorageLoaderJob(StyleStorage* storage, const QList<QPair<QRegion, Style> >& styles);
74 virtual void run();
75 void waitForFinished();
76 bool isFinished();
77 QList<QPair<QRegion, Style> > data() const { return m_styles; }
78private:
79 StyleStorage* m_storage;
80 QList<QPair<QRegion, Style> > m_styles;
81};
82
83StyleStorageLoaderJob::StyleStorageLoaderJob(StyleStorage *storage, const QList<QPair<QRegion, Style> > &styles)
84 : m_storage(storage), m_styles(styles)
85{
86
87}
88
89void StyleStorageLoaderJob::waitForFinished()
90{
91 run();
92}
93
94bool StyleStorageLoaderJob::isFinished()
95{
96 return false;
97}
98
99void StyleStorageLoaderJob::run()
100{
101 static int total = 0;
102 kDebug(36006) << "Loading styles";
103 QTime t; t.start();
104 StyleStorage::Private* d = m_storage->d;
105 QList<QPair<QRegion, SharedSubStyle> > subStyles;
106
107 d->usedArea = QRegion();
108 d->usedColumns.clear();
109 d->usedRows.clear();
110 {
111#ifdef CALLIGRA_SHEETS_MT
112 QMutexLocker(&d->cacheMutex);
113#endif
114 d->cachedArea = QRegion();
115 d->cache.clear();
116 }
117 typedef QPair<QRegion, Style> StyleRegion;
118 foreach (const StyleRegion& styleArea, m_styles) {
119 const QRegion& reg = styleArea.first;
120 const Style& style = styleArea.second;
121 if (style.isEmpty()) continue;
122
123 // update used areas
124 QRect bound = reg.boundingRect();
125 if ((bound.top() == 1 && bound.bottom() >= KS_rowMax) || (bound.left() == 1 && bound.right() >= KS_colMax)) {
126 foreach (const QRect& rect, reg.rects()) {
127 if (rect.top() == 1 && rect.bottom() >= KS_rowMax) {
128 for (int i = rect.left(); i <= rect.right(); ++i) {
129 d->usedColumns.insert(i, true);
130 }
131 } else if (rect.left() == 1 && rect.right() >= KS_colMax) {
132 for (int i = rect.top(); i <= rect.bottom(); ++i) {
133 d->usedRows.insert(i, true);
134 }
135 } else {
136 d->usedArea += rect;
137 }
138 }
139 } else {
140 d->usedArea += reg;
141 }
142
143 // find substyles
144 foreach(const SharedSubStyle& subStyle, style.subStyles()) {
145 bool foundShared = false;
146 typedef const QList< SharedSubStyle> StoredSubStyleList;
147 StoredSubStyleList& storedSubStyles(d->subStyles.value(subStyle->type()));
148 StoredSubStyleList::ConstIterator end(storedSubStyles.end());
149 for (StoredSubStyleList::ConstIterator it(storedSubStyles.begin()); it != end; ++it) {
150 if (Style::compare(subStyle.data(), (*it).data())) {
151 // kDebug(36006) <<"[REUSING EXISTING SUBSTYLE]";
152 subStyles.append(qMakePair(reg, *it));
153 foundShared = true;
154 break;
155 }
156 }
157 if (!foundShared) {
158 // insert substyle and add to the used substyle list
159 subStyles.append(qMakePair(reg, subStyle));
160 }
161 }
162 }
163 d->tree.load(subStyles);
164 int e = t.elapsed();
165 total += e;
166 kDebug(36006) << "Time: " << e << total;
167}
168
169void StyleStorage::Private::ensureLoaded()
170{
171 if (loader) {
172 loader->waitForFinished();
173 delete loader;
174 loader = 0;
175 }
176}
177
178StyleStorage::StyleStorage(Map* map)
179 : QObject(map)
180 , d(new Private)
181{
182 d->map = map;
183 d->cache.setMaxCost(g_maximumCachedStyles);
184 d->loader = 0;
185}
186
187StyleStorage::StyleStorage(const StyleStorage& other)
188 : QObject(other.d->map)
189 , d(new Private)
190{
191 d->map = other.d->map;
192 d->tree = other.d->tree;
193 d->usedColumns = other.d->usedColumns;
194 d->usedRows = other.d->usedRows;
195 d->usedArea = other.d->usedArea;
196 d->subStyles = other.d->subStyles;
197 if (other.d->loader) {
198 d->loader = new StyleStorageLoaderJob(this, other.d->loader->data());
199 } else {
200 d->loader = 0;
201 }
202 // the other member variables are temporary stuff
203}
204
205StyleStorage::~StyleStorage()
206{
207 delete d->loader; // in a multi-threaded approach this needs more care
208 delete d;
209}
210
211Style StyleStorage::contains(const QPoint& point) const
212{
213 d->ensureLoaded();
214 if (!d->usedArea.contains(point) && !d->usedColumns.contains(point.x()) && !d->usedRows.contains(point.y()))
215 return *styleManager()->defaultStyle();
216
217 {
218#ifdef CALLIGRA_SHEETS_MT
219 QMutexLocker ml(&d->cacheMutex);
220#endif
221 // first, lookup point in the cache
222 if (d->cache.contains(point)) {
223 // kDebug(36006) <<"StyleStorage: Using cached style for" << cellName;
224 Style st = *d->cache.object(point);
225 return st;
226 }
227 }
228 // not found, lookup in the tree
229 QList<SharedSubStyle> subStyles = d->tree.contains(point);
230
231 if (subStyles.isEmpty())
232 return *styleManager()->defaultStyle();
233 Style* style = new Style();
234 (*style) = composeStyle(subStyles);
235
236 {
237#ifdef CALLIGRA_SHEETS_MT
238 QMutexLocker ml(&d->cacheMutex);
239#endif
240 // insert style into the cache
241 d->cache.insert(point, style);
242 d->cachedArea += QRect(point, point);
243 }
244 return *style;
245}
246
247Style StyleStorage::contains(const QRect& rect) const
248{
249 d->ensureLoaded();
250 QList<SharedSubStyle> subStyles = d->tree.contains(rect);
251 return composeStyle(subStyles);
252}
253
254Style StyleStorage::intersects(const QRect& rect) const
255{
256 d->ensureLoaded();
257 QList<SharedSubStyle> subStyles = d->tree.intersects(rect);
258 return composeStyle(subStyles);
259}
260
261QList< QPair<QRectF, SharedSubStyle> > StyleStorage::undoData(const Region& region) const
262{
263 d->ensureLoaded();
264 QList< QPair<QRectF, SharedSubStyle> > result;
265 Region::ConstIterator end = region.constEnd();
266 for (Region::ConstIterator it = region.constBegin(); it != end; ++it) {
267 const QRect rect = (*it)->rect();
268 QList< QPair<QRectF, SharedSubStyle> > pairs = d->tree.intersectingPairs(rect).values();
269 for (int i = 0; i < pairs.count(); ++i) {
270 // trim the rects
271 pairs[i].first = pairs[i].first.intersected(rect);
272 }
273 // Always a default subStyle first, even if there are no pairs.
274 result << qMakePair(QRectF(rect), SharedSubStyle()) << pairs;
275 }
276 return result;
277}
278
279QRect StyleStorage::usedArea() const
280{
281 d->ensureLoaded();
282 if (d->usedArea.isEmpty())
283 return QRect(1, 1, 0, 0);
284 return QRect(QPoint(1, 1), d->usedArea.boundingRect().bottomRight());
285}
286
287void StyleStorage::saveOdfCreateDefaultStyles(int& maxCols, int& maxRows, OdfSavingContext& tableContext) const
288{
289 d->ensureLoaded();
290#if 0 // TODO
291 // If we have both, column and row styles, we can take the short route.
292 if (!d->usedColumns.isEmpty() && !d->usedRows.isEmpty()) {
293 for (int i = 0; i < d->usedColumns.count(); ++i) {
294 const int col = d->usedColumns[i];
295 tableContext.columnDefaultStyles[col].insertSubStyle(contains(QRect(col, 1, 1, KS_rowMax)));
296 }
297 for (int i = 0; i < d->usedRow.count(); ++i) {
298 const int row = d->usedRow[i];
299 tableContext.rowDefaultStyles[row].insertSubStyle(contains(QRect(1, row, KS_colMax, 1)));
300 }
301 return;
302 }
303#endif
304 const QRect sheetRect(QPoint(1, 1), QPoint(KS_colMax, KS_rowMax));
305 if (d->usedColumns.count() != 0) {
306 maxCols = qMax(maxCols, (--d->usedColumns.constEnd()).key());
307 maxRows = KS_rowMax;
308 }
309 if (d->usedRows.count() != 0) {
310 maxCols = KS_colMax;
311 maxRows = qMax(maxRows, (--d->usedRows.constEnd()).key());
312 }
313 const QList< QPair<QRectF, SharedSubStyle> > pairs = d->tree.intersectingPairs(sheetRect).values();
314 for (int i = 0; i < pairs.count(); ++i) {
315 const QRect rect = pairs[i].first.toRect();
316 // column default cell styles
317 // Columns have no content. Prefer them over rows for the default cell styles.
318 if (rect.top() == 1 && rect.bottom() == maxRows) {
319 for (int col = rect.left(); col <= rect.right(); ++col) {
320 if (pairs[i].second.data()->type() == Style::DefaultStyleKey)
321 tableContext.columnDefaultStyles.remove(col);
322 else
323 tableContext.columnDefaultStyles[col].insertSubStyle(pairs[i].second);
324 }
325 }
326 // row default cell styles
327 else if (rect.left() == 1 && rect.right() == maxCols) {
328 for (int row = rect.top(); row <= rect.bottom(); ++row) {
329 if (pairs[i].second.data()->type() == Style::DefaultStyleKey)
330 tableContext.rowDefaultStyles.remove(row);
331 else
332 tableContext.rowDefaultStyles[row].insertSubStyle(pairs[i].second);
333 }
334 }
335 }
336}
337
338int StyleStorage::nextColumnStyleIndex(int column) const
339{
340 d->ensureLoaded();
341 QMap<int, bool>::iterator it = d->usedColumns.upperBound(column + 1);
342 return (it == d->usedColumns.end()) ? 0 : it.key();
343}
344
345int StyleStorage::nextRowStyleIndex(int row) const
346{
347 d->ensureLoaded();
348 QMap<int, bool>::iterator it = d->usedRows.upperBound(row + 1);
349 return (it == d->usedRows.end()) ? 0 : it.key();
350}
351
352int StyleStorage::firstColumnIndexInRow(int row) const
353{
354 d->ensureLoaded();
355 const QRect rect = (d->usedArea & QRect(QPoint(1, row), QPoint(KS_colMax, row))).boundingRect();
356 return rect.isNull() ? 0 : rect.left();
357}
358
359int StyleStorage::nextColumnIndexInRow(int column, int row) const
360{
361 d->ensureLoaded();
362 const QRect rect = (d->usedArea & QRect(QPoint(column + 1, row), QPoint(KS_colMax, row))).boundingRect();
363 return rect.isNull() ? 0 : rect.left();
364}
365
366void StyleStorage::insert(const QRect& rect, const SharedSubStyle& subStyle, bool markRegionChanged)
367{
368 d->ensureLoaded();
369// kDebug(36006) <<"StyleStorage: inserting" << SubStyle::name(subStyle->type()) <<" into" << rect;
370 // keep track of the used area
371 const bool isDefault = subStyle->type() == Style::DefaultStyleKey;
372 if (rect.top() == 1 && rect.bottom() >= KS_rowMax) {
373 for (int i = rect.left(); i <= rect.right(); ++i) {
374 if (isDefault)
375 d->usedColumns.remove(i);
376 else
377 d->usedColumns.insert(i, true);
378 }
379 if (isDefault)
380 d->usedArea -= rect;
381 } else if (rect.left() == 1 && rect.right() >= KS_colMax) {
382 for (int i = rect.top(); i <= rect.bottom(); ++i) {
383 if (isDefault)
384 d->usedRows.remove(i);
385 else
386 d->usedRows.insert(i, true);
387 }
388 if (isDefault)
389 d->usedArea -= rect;
390 } else {
391 if (isDefault)
392 d->usedArea -= rect;
393 else
394 d->usedArea += rect;
395 }
396
397 // lookup already used substyles
398 typedef const QList< SharedSubStyle> StoredSubStyleList;
399 StoredSubStyleList& storedSubStyles(d->subStyles.value(subStyle->type()));
400 StoredSubStyleList::ConstIterator end(storedSubStyles.end());
401 for (StoredSubStyleList::ConstIterator it(storedSubStyles.begin()); it != end; ++it) {
402 if (Style::compare(subStyle.data(), (*it).data())) {
403// kDebug(36006) <<"[REUSING EXISTING SUBSTYLE]";
404 d->tree.insert(rect, *it);
405 if (markRegionChanged) {
406 regionChanged(rect);
407 }
408 return;
409 }
410 }
411 // insert substyle and add to the used substyle list
412 d->tree.insert(rect, subStyle);
413 d->subStyles[subStyle->type()].append(subStyle);
414 if (markRegionChanged) {
415 regionChanged(rect);
416 }
417}
418
419void StyleStorage::insert(const Region& region, const Style& style)
420{
421 d->ensureLoaded();
422 if (style.isEmpty())
423 return;
424 foreach(const SharedSubStyle& subStyle, style.subStyles()) {
425 Region::ConstIterator end(region.constEnd());
426 for (Region::ConstIterator it(region.constBegin()); it != end; ++it) {
427 // insert substyle
428 insert((*it)->rect(), subStyle, false);
429 }
430 }
431 for (Region::ConstIterator it(region.constBegin()), end(region.constEnd()); it != end; ++it) {
432 regionChanged((*it)->rect());
433 }
434}
435
436void StyleStorage::load(const QList<QPair<QRegion, Style> >& styles)
437{
438 Q_ASSERT(!d->loader);
439 d->loader = new StyleStorageLoaderJob(this, styles);
440}
441
442QList< QPair<QRectF, SharedSubStyle> > StyleStorage::insertRows(int position, int number)
443{
444 d->ensureLoaded();
445 const QRect invalidRect(1, position, KS_colMax, KS_rowMax);
446 // invalidate the affected, cached styles
447 invalidateCache(invalidRect);
448 // update the used area
449 const QRegion usedArea = d->usedArea & invalidRect;
450 d->usedArea -= invalidRect;
451 d->usedArea += usedArea.translated(0, number);
452 const QVector<QRect> rects = (d->usedArea & QRect(1, position - 1, KS_colMax, 1)).rects();
453 for (int i = 0; i < rects.count(); ++i)
454 d->usedArea += rects[i].adjusted(0, 1, 0, number + 1);
455 // update the used rows
456 QMap<int, bool> map;
457 QMap<int, bool>::iterator begin = d->usedRows.lowerBound(position);
458 QMap<int, bool>::iterator end = d->usedRows.end();
459 for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
460 if (it.key() + number <= KS_rowMax)
461 map.insert(it.key() + number, true);
462 }
463 for (QMap<int, bool>::iterator it = begin; it != end; )
464 it = d->usedRows.erase(it);
465 d->usedRows.unite(map);
466 // process the tree
467 QList< QPair<QRectF, SharedSubStyle> > undoData;
468 undoData << qMakePair(QRectF(1, KS_rowMax - number + 1, KS_colMax, number), SharedSubStyle());
469 undoData << d->tree.insertRows(position, number);
470 return undoData;
471}
472
473QList< QPair<QRectF, SharedSubStyle> > StyleStorage::insertColumns(int position, int number)
474{
475 d->ensureLoaded();
476 const QRect invalidRect(position, 1, KS_colMax, KS_rowMax);
477 // invalidate the affected, cached styles
478 invalidateCache(invalidRect);
479 // update the used area
480 const QRegion usedArea = d->usedArea & invalidRect;
481 d->usedArea -= invalidRect;
482 d->usedArea += usedArea.translated(number, 0);
483 const QVector<QRect> rects = (d->usedArea & QRect(position - 1, 0, 1, KS_rowMax)).rects();
484 for (int i = 0; i < rects.count(); ++i)
485 d->usedArea += rects[i].adjusted(1, 0, number + 1, 0);
486 // update the used columns
487 QMap<int, bool> map;
488 QMap<int, bool>::iterator begin = d->usedColumns.upperBound(position);
489 QMap<int, bool>::iterator end = d->usedColumns.end();
490 for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
491 if (it.key() + number <= KS_colMax)
492 map.insert(it.key() + number, true);
493 }
494 for (QMap<int, bool>::iterator it = begin; it != end; )
495 it = d->usedColumns.erase(it);
496 d->usedColumns.unite(map);
497 // process the tree
498 QList< QPair<QRectF, SharedSubStyle> > undoData;
499 undoData << qMakePair(QRectF(KS_colMax - number + 1, 1, number, KS_rowMax), SharedSubStyle());
500 undoData << d->tree.insertColumns(position, number);
501 return undoData;
502}
503
504QList< QPair<QRectF, SharedSubStyle> > StyleStorage::removeRows(int position, int number)
505{
506 d->ensureLoaded();
507 const QRect invalidRect(1, position, KS_colMax, KS_rowMax);
508 // invalidate the affected, cached styles
509 invalidateCache(invalidRect);
510 // update the used area
511 const QRegion usedArea = d->usedArea & QRect(1, position + number, KS_colMax, KS_rowMax);
512 d->usedArea -= invalidRect;
513 d->usedArea += usedArea.translated(0, -number);
514 // update the used rows
515 QMap<int, bool> map;
516 QMap<int, bool>::iterator begin = d->usedRows.upperBound(position);
517 QMap<int, bool>::iterator end = d->usedRows.end();
518 for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
519 if (it.key() - number >= position)
520 map.insert(it.key() - number, true);
521 }
522 for (QMap<int, bool>::iterator it = begin; it != end; )
523 it = d->usedRows.erase(it);
524 d->usedRows.unite(map);
525 // process the tree
526 QList< QPair<QRectF, SharedSubStyle> > undoData;
527 undoData << qMakePair(QRectF(1, position, KS_colMax, number), SharedSubStyle());
528 undoData << d->tree.removeRows(position, number);
529 return undoData;
530}
531
532QList< QPair<QRectF, SharedSubStyle> > StyleStorage::removeColumns(int position, int number)
533{
534 d->ensureLoaded();
535 const QRect invalidRect(position, 1, KS_colMax, KS_rowMax);
536 // invalidate the affected, cached styles
537 invalidateCache(invalidRect);
538 // update the used area
539 const QRegion usedArea = d->usedArea & QRect(position + number, 1, KS_colMax, KS_rowMax);
540 d->usedArea -= invalidRect;
541 d->usedArea += usedArea.translated(-number, 0);
542 // update the used columns
543 QMap<int, bool> map;
544 QMap<int, bool>::iterator begin = d->usedColumns.upperBound(position);
545 QMap<int, bool>::iterator end = d->usedColumns.end();
546 for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
547 if (it.key() - number >= position)
548 map.insert(it.key() - number, true);
549 }
550 for (QMap<int, bool>::iterator it = begin; it != end; )
551 it = d->usedColumns.erase(it);
552 d->usedColumns.unite(map);
553 // process the tree
554 QList< QPair<QRectF, SharedSubStyle> > undoData;
555 undoData << qMakePair(QRectF(position, 1, number, KS_rowMax), SharedSubStyle());
556 undoData << d->tree.removeColumns(position, number);
557 return undoData;
558}
559
560QList< QPair<QRectF, SharedSubStyle> > StyleStorage::insertShiftRight(const QRect& rect)
561{
562 d->ensureLoaded();
563 const QRect invalidRect(rect.topLeft(), QPoint(KS_colMax, rect.bottom()));
564 QList< QPair<QRectF, SharedSubStyle> > undoData;
565 undoData << qMakePair(QRectF(rect), SharedSubStyle());
566 undoData << d->tree.insertShiftRight(rect);
567 regionChanged(invalidRect);
568 // update the used area
569 const QRegion usedArea = d->usedArea & invalidRect;
570 d->usedArea -= invalidRect;
571 d->usedArea += usedArea.translated(rect.width(), 0);
572 const QVector<QRect> rects = (d->usedArea & QRect(rect.left() - 1, rect.top(), 1, rect.height())).rects();
573 for (int i = 0; i < rects.count(); ++i)
574 d->usedArea += rects[i].adjusted(1, 0, rect.width() + 1, 0);
575 // update the used columns
576 QMap<int, bool>::iterator begin = d->usedColumns.upperBound(rect.left());
577 QMap<int, bool>::iterator end = d->usedColumns.end();
578 for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
579 if (it.key() + rect.width() <= KS_colMax)
580 d->usedArea += QRect(it.key() + rect.width(), rect.top(), rect.width(), rect.height());
581 }
582 if (d->usedColumns.contains(rect.left() - 1))
583 d->usedArea += rect;
584 return undoData;
585}
586
587QList< QPair<QRectF, SharedSubStyle> > StyleStorage::insertShiftDown(const QRect& rect)
588{
589 d->ensureLoaded();
590 const QRect invalidRect(rect.topLeft(), QPoint(rect.right(), KS_rowMax));
591 QList< QPair<QRectF, SharedSubStyle> > undoData;
592 undoData << qMakePair(QRectF(rect), SharedSubStyle());
593 undoData << d->tree.insertShiftDown(rect);
594 regionChanged(invalidRect);
595 // update the used area
596 const QRegion usedArea = d->usedArea & invalidRect;
597 d->usedArea -= invalidRect;
598 d->usedArea += usedArea.translated(0, rect.height());
599 const QVector<QRect> rects = (d->usedArea & QRect(rect.left(), rect.top() - 1, rect.width(), 1)).rects();
600 for (int i = 0; i < rects.count(); ++i)
601 d->usedArea += rects[i].adjusted(0, 1, 0, rect.height() + 1);
602 // update the used rows
603 QMap<int, bool>::iterator begin = d->usedRows.upperBound(rect.top());
604 QMap<int, bool>::iterator end = d->usedRows.end();
605 for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
606 if (it.key() + rect.height() <= KS_rowMax)
607 d->usedArea += QRect(rect.left(), it.key() + rect.height(), rect.width(), rect.height());
608 }
609 if (d->usedRows.contains(rect.top() - 1))
610 d->usedArea += rect;
611 return undoData;
612}
613
614QList< QPair<QRectF, SharedSubStyle> > StyleStorage::removeShiftLeft(const QRect& rect)
615{
616 d->ensureLoaded();
617 const QRect invalidRect(rect.topLeft(), QPoint(KS_colMax, rect.bottom()));
618 QList< QPair<QRectF, SharedSubStyle> > undoData;
619 undoData << qMakePair(QRectF(rect), SharedSubStyle());
620 undoData << d->tree.removeShiftLeft(rect);
621 regionChanged(invalidRect);
622 // update the used area
623 const QRegion usedArea = d->usedArea & QRect(rect.right() + 1, rect.top(), KS_colMax, rect.height());
624 d->usedArea -= invalidRect;
625 d->usedArea += usedArea.translated(-rect.width(), 0);
626 // update the used columns
627 QMap<int, bool>::iterator begin = d->usedColumns.upperBound(rect.right() + 1);
628 QMap<int, bool>::iterator end = d->usedColumns.end();
629 for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
630 if (it.key() - rect.width() >= rect.left())
631 d->usedArea += QRect(it.key() - rect.width(), rect.top(), rect.width(), rect.height());
632 }
633 return undoData;
634}
635
636QList< QPair<QRectF, SharedSubStyle> > StyleStorage::removeShiftUp(const QRect& rect)
637{
638 d->ensureLoaded();
639 const QRect invalidRect(rect.topLeft(), QPoint(rect.right(), KS_rowMax));
640 QList< QPair<QRectF, SharedSubStyle> > undoData;
641 undoData << qMakePair(QRectF(rect), SharedSubStyle());
642 undoData << d->tree.removeShiftUp(rect);
643 regionChanged(invalidRect);
644 // update the used area
645 const QRegion usedArea = d->usedArea & QRect(rect.left(), rect.bottom() + 1, rect.width(), KS_rowMax);
646 d->usedArea -= invalidRect;
647 d->usedArea += usedArea.translated(0, -rect.height());
648 // update the used rows
649 QMap<int, bool>::iterator begin = d->usedRows.upperBound(rect.bottom() + 1);
650 QMap<int, bool>::iterator end = d->usedRows.end();
651 for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
652 if (it.key() - rect.height() >= rect.top())
653 d->usedArea += QRect(rect.left(), it.key() - rect.height(), rect.width(), rect.height());
654 }
655 return undoData;
656}
657
658void StyleStorage::invalidateCache()
659{
660 // still busy loading? no cache to invalidate
661 if (d->loader && !d->loader->isFinished())
662 return;
663
664#ifdef CALLIGRA_SHEETS_MT
665 QMutexLocker ml(&d->cacheMutex);
666#endif
667 d->cache.clear();
668 d->cachedArea = QRegion();
669}
670
671void StyleStorage::garbageCollection()
672{
673 // still busy loading? no garbage to collect
674 if (d->loader && !d->loader->isFinished())
675 return;
676
677 // any possible garbage left?
678 if (d->possibleGarbage.isEmpty())
679 return;
680
681 const int currentZIndex = d->possibleGarbage.constBegin().key();
682 const QPair<QRectF, SharedSubStyle> currentPair = d->possibleGarbage.take(currentZIndex);
683
684 // check whether the named style still exists
685 if (currentPair.second->type() == Style::NamedStyleKey &&
686 !styleManager()->style(static_cast<const NamedStyle*>(currentPair.second.data())->name)) {
687 kDebug(36006) << "removing" << currentPair.second->debugData()
688 << "at" << Region(currentPair.first.toRect()).name()
689 << "used" << currentPair.second->ref << "times" << endl;
690 d->tree.remove(currentPair.first.toRect(), currentPair.second);
691 d->subStyles[currentPair.second->type()].removeAll(currentPair.second);
692 QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
693 return; // already done
694 }
695
696 typedef QPair<QRectF, SharedSubStyle> SharedSubStylePair;
697 QMap<int, SharedSubStylePair> pairs = d->tree.intersectingPairs(currentPair.first.toRect());
698 if (pairs.isEmpty()) // actually never true, just for sanity
699 return;
700 int zIndex = pairs.constBegin().key();
701 SharedSubStylePair pair = pairs[zIndex];
702
703 // check whether the default style is placed first
704 if (zIndex == currentZIndex &&
705 currentPair.second->type() == Style::DefaultStyleKey &&
706 pair.second->type() == Style::DefaultStyleKey &&
707 pair.first == currentPair.first) {
708 kDebug(36006) << "removing default style"
709 << "at" << Region(currentPair.first.toRect()).name()
710 << "used" << currentPair.second->ref << "times" << endl;
711 d->tree.remove(currentPair.first.toRect(), currentPair.second);
712 QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
713 return; // already done
714 }
715
716 // special handling for indentation:
717 // check whether the default indentation is placed first
718 if (zIndex == currentZIndex &&
719 currentPair.second->type() == Style::Indentation &&
720 static_cast<const SubStyleOne<Style::Indentation, int>*>(currentPair.second.data())->value1 == 0 &&
721 pair.first == currentPair.first) {
722 kDebug(36006) << "removing default indentation"
723 << "at" << Region(currentPair.first.toRect()).name()
724 << "used" << currentPair.second->ref << "times" << endl;
725 d->tree.remove(currentPair.first.toRect(), currentPair.second);
726 QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
727 return; // already done
728 }
729
730 // special handling for precision:
731 // check whether the storage default precision is placed first
732 if (zIndex == currentZIndex &&
733 currentPair.second->type() == Style::Precision &&
734 static_cast<const SubStyleOne<Style::Precision, int>*>(currentPair.second.data())->value1 == 0 &&
735 pair.first == currentPair.first) {
736 kDebug(36006) << "removing default precision"
737 << "at" << Region(currentPair.first.toRect()).name()
738 << "used" << currentPair.second->ref << "times" << endl;
739 d->tree.remove(currentPair.first.toRect(), currentPair.second);
740 QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
741 return; // already done
742 }
743
744 // check, if the current substyle is covered by others added after it
745 bool found = false;
746 QMap<int, SharedSubStylePair>::ConstIterator end = pairs.constEnd();
747 for (QMap<int, SharedSubStylePair>::ConstIterator it = pairs.constFind(currentZIndex); it != end; ++it) {
748 zIndex = it.key();
749 pair = it.value();
750
751 // as long as the substyle in question was not found, skip the substyle
752 if (!found) {
753 if (pair.first == currentPair.first &&
754 Style::compare(pair.second.data(), currentPair.second.data()) &&
755 zIndex == currentZIndex) {
756 found = true;
757 }
758 continue;
759 }
760
761 // remove the current pair, if another substyle of the same type,
762 // the default style or a named style follows and the rectangle
763 // is completely covered
764 if (zIndex != currentZIndex &&
765 (pair.second->type() == currentPair.second->type() ||
766 pair.second->type() == Style::DefaultStyleKey ||
767 pair.second->type() == Style::NamedStyleKey) &&
768 pair.first.toRect().contains(currentPair.first.toRect())) {
769 // special handling for indentation
770 // only remove, if covered by default
771 if (pair.second->type() == Style::Indentation &&
772 static_cast<const SubStyleOne<Style::Indentation, int>*>(pair.second.data())->value1 != 0) {
773 continue;
774 }
775
776 // special handling for precision
777 // only remove, if covered by default
778 if (pair.second->type() == Style::Precision &&
779 static_cast<const SubStyleOne<Style::Precision, int>*>(pair.second.data())->value1 != 0) {
780 continue;
781 }
782
783 kDebug(36006) << "removing" << currentPair.second->debugData()
784 << "at" << Region(currentPair.first.toRect()).name()
785 << "used" << currentPair.second->ref << "times" << endl;
786 d->tree.remove(currentPair.first.toRect(), currentPair.second, currentZIndex);
787#if 0
788 kDebug(36006) << "StyleStorage: usage of" << currentPair.second->debugData() << " is" << currentPair.second->ref;
789 // FIXME Stefan: The usage of substyles used once should be
790 // two (?) here, not more. Why is this not the case?
791 // The shared pointers are used by:
792 // a) the tree
793 // b) the reusage list (where it should be removed)
794 // c) the cached styles (!)
795 // d) the undo data of operations (!)
796 if (currentPair.second->ref == 2) {
797 kDebug(36006) << "StyleStorage: removing" << currentPair.second << " from the used subStyles";
798 d->subStyles[currentPair.second->type()].removeAll(currentPair.second);
799 }
800#endif
801 break;
802 }
803 }
804 QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
805}
806
807void StyleStorage::regionChanged(const QRect& rect)
808{
809 // still busy loading? no garbage to collect
810 if (d->loader && !d->loader->isFinished())
811 return;
812 if (d->map->isLoading())
813 return;
814 // mark the possible garbage
815 // NOTE Stefan: The map may contain multiple indices. The already existing possible garbage has
816 // has to be inserted most recently, because it should be accessed first.
817 d->possibleGarbage = d->tree.intersectingPairs(rect).unite(d->possibleGarbage);
818 QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
819 // invalidate cache
820 invalidateCache(rect);
821}
822
823void StyleStorage::invalidateCache(const QRect& rect)
824{
825 // still busy loading? no cache to invalidate
826 if (d->loader && !d->loader->isFinished())
827 return;
828
829#ifdef CALLIGRA_SHEETS_MT
830 QMutexLocker ml(&d->cacheMutex);
831#endif
832// kDebug(36006) <<"StyleStorage: Invalidating" << rect;
833 const QRegion region = d->cachedArea.intersected(rect);
834 d->cachedArea = d->cachedArea.subtracted(rect);
835 foreach(const QRect& rect, region.rects()) {
836 for (int col = rect.left(); col <= rect.right(); ++col) {
837 for (int row = rect.top(); row <= rect.bottom(); ++row) {
838// kDebug(36006) <<"StyleStorage: Removing cached style for" << Cell::name( col, row );
839 d->cache.remove(QPoint(col, row)); // also deletes it
840 }
841 }
842 }
843}
844
845Style StyleStorage::composeStyle(const QList<SharedSubStyle>& subStyles) const
846{
847 d->ensureLoaded();
848
849 if (subStyles.isEmpty())
850 return *styleManager()->defaultStyle();
851
852 Style style;
853 for (int i = 0; i < subStyles.count(); ++i) {
854 if (subStyles[i]->type() == Style::DefaultStyleKey)
855 style = *styleManager()->defaultStyle();
856 else if (subStyles[i]->type() == Style::NamedStyleKey) {
857 style.clear();
858 const CustomStyle* namedStyle = styleManager()->style(static_cast<const NamedStyle*>(subStyles[i].data())->name);
859 if (namedStyle) {
860 // first, load the attributes of the parent style(s)
861 QList<CustomStyle*> parentStyles;
862 CustomStyle* parentStyle = styleManager()->style(namedStyle->parentName());
863// kDebug(36006) <<"StyleStorage:" << namedStyle->name() <<"'s parent =" << namedStyle->parentName();
864 while (parentStyle) {
865// kDebug(36006) <<"StyleStorage:" << parentStyle->name() <<"'s parent =" << parentStyle->parentName();
866 parentStyles.prepend(parentStyle);
867 parentStyle = styleManager()->style(parentStyle->parentName());
868 }
869 Style tmpStyle;
870 for (int i = 0; i < parentStyles.count(); ++i) {
871// kDebug(36006) <<"StyleStorage: merging" << parentStyles[i]->name() <<" in.";
872 tmpStyle = *parentStyles[i];
873 tmpStyle.merge(style);
874 style = tmpStyle;
875 }
876 // second, merge the other attributes in
877// kDebug(36006) <<"StyleStorage: merging" << namedStyle->name() <<" in.";
878 tmpStyle = *namedStyle;
879 tmpStyle.merge(style);
880 style = tmpStyle;
881 // not the default anymore
882 style.clearAttribute(Style::DefaultStyleKey);
883 // reset the parent name
884 style.setParentName(namedStyle->name());
885// kDebug(36006) <<"StyleStorage: merging done";
886 }
887 } else if (subStyles[i]->type() == Style::Indentation) {
888 // special handling for indentation
889 const int indentation = static_cast<const SubStyleOne<Style::Indentation, int>*>(subStyles[i].data())->value1;
890 if (indentation == 0 || (style.indentation() + indentation <= 0))
891 style.clearAttribute(Style::Indentation); // reset
892 else
893 style.setIndentation(style.indentation() + indentation); // increase/decrease
894 } else if (subStyles[i]->type() == Style::Precision) {
895 // special handling for precision
896 // The Style default (-1) and the storage default (0) differ.
897 const int precision = static_cast<const SubStyleOne<Style::Precision, int>*>(subStyles[i].data())->value1;
898 if (precision == 0) // storage default
899 style.clearAttribute(Style::Precision); // reset
900 else {
901 if (style.precision() == -1) // Style default
902 style.setPrecision(qMax(0, precision)); // positive initial value
903 else if (style.precision() + precision <= 0)
904 style.setPrecision(0);
905 else if (style.precision() + precision >= 10)
906 style.setPrecision(10);
907 else
908 style.setPrecision(style.precision() + precision); // increase/decrease
909 }
910 } else {
911 // insert the substyle
912// kDebug(36006) <<"StyleStorage: inserting" << subStyles[i]->debugData();
913 style.insertSubStyle(subStyles[i]);
914 // not the default anymore
915 style.clearAttribute(Style::DefaultStyleKey);
916 }
917 }
918 return style;
919}
920
921StyleManager* StyleStorage::styleManager() const
922{
923 return d->map->styleManager();
924}
925
926#include "StyleStorage.moc"
927