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 "RTree.h"
36 #include "Style.h"
37 #include "StyleManager.h"
38 #include "RectStorage.h"
39 
40 static const int g_maximumCachedStyles = 10000;
41 
42 using namespace Calligra::Sheets;
43 
44 class Q_DECL_HIDDEN StyleStorage::Private
45 {
46 public:
Private()47     Private()
48 #ifdef CALLIGRA_SHEETS_MT
49         : cacheMutex(QMutex::Recursive)
50 #endif
51     {}
52     Map* map;
53     RTree<SharedSubStyle> tree;
54     QMap<int, bool> usedColumns; // FIXME Stefan: Use QList and std::upper_bound() for insertion.
55     QMap<int, bool> usedRows;
56     QRegion usedArea;
57     QHash<Style::Key, QList<SharedSubStyle> > subStyles;
58     QMap<int, QPair<QRectF, SharedSubStyle> > possibleGarbage;
59     QCache<QPoint, Style> cache;
60     QRegion cachedArea;
61     StyleStorageLoaderJob* loader;
62 #ifdef CALLIGRA_SHEETS_MT
63     QMutex cacheMutex;
64 #endif
65 
66     void ensureLoaded();
67 };
68 
69 class Calligra::Sheets::StyleStorageLoaderJob : public QRunnable
70 {
71 public:
72     StyleStorageLoaderJob(StyleStorage* storage, const QList<QPair<QRegion, Style> >& styles);
73     void run() override;
74     void waitForFinished();
75     bool isFinished();
data() const76     QList<QPair<QRegion, Style> > data() const { return m_styles; }
77 private:
78     StyleStorage* m_storage;
79     QList<QPair<QRegion, Style> > m_styles;
80 };
81 
StyleStorageLoaderJob(StyleStorage * storage,const QList<QPair<QRegion,Style>> & styles)82 StyleStorageLoaderJob::StyleStorageLoaderJob(StyleStorage *storage, const QList<QPair<QRegion, Style> > &styles)
83     : m_storage(storage), m_styles(styles)
84 {
85 
86 }
87 
waitForFinished()88 void StyleStorageLoaderJob::waitForFinished()
89 {
90     run();
91 }
92 
isFinished()93 bool StyleStorageLoaderJob::isFinished()
94 {
95     return false;
96 }
97 
run()98 void StyleStorageLoaderJob::run()
99 {
100     static int total = 0;
101     debugSheetsStyle << "Loading styles:" << endl << m_styles;
102     QTime t; t.start();
103     StyleStorage::Private* d = m_storage->d;
104     QList<QPair<QRegion, SharedSubStyle> > subStyles;
105 
106     d->usedArea = QRegion();
107     d->usedColumns.clear();
108     d->usedRows.clear();
109     {
110 #ifdef CALLIGRA_SHEETS_MT
111         QMutexLocker(&d->cacheMutex);
112 #endif
113         d->cachedArea = QRegion();
114         d->cache.clear();
115     }
116     typedef QPair<QRegion, Style> StyleRegion;
117     foreach (const StyleRegion& styleArea, m_styles) {
118         const QRegion& reg = styleArea.first;
119         const Style& style = styleArea.second;
120         if (style.isEmpty()) continue;
121 
122         // update used areas
123         QRect bound = reg.boundingRect();
124         if ((bound.top() == 1 && bound.bottom() >= KS_rowMax) || (bound.left() == 1 && bound.right() >= KS_colMax)) {
125             foreach (const QRect& rect, reg.rects()) {
126                 if (rect.top() == 1 && rect.bottom() >= KS_rowMax) {
127                     for (int i = rect.left(); i <= rect.right(); ++i) {
128                         d->usedColumns.insert(i, true);
129                     }
130                 } else if (rect.left() == 1 && rect.right() >= KS_colMax) {
131                     for (int i = rect.top(); i <= rect.bottom(); ++i) {
132                         d->usedRows.insert(i, true);
133                     }
134                 } else {
135                     d->usedArea += rect;
136                 }
137             }
138         } else {
139             d->usedArea += reg;
140         }
141 
142         // find substyles
143         foreach(const SharedSubStyle& subStyle, style.subStyles()) {
144             bool foundShared = false;
145             typedef const QList< SharedSubStyle> StoredSubStyleList;
146             StoredSubStyleList& storedSubStyles(d->subStyles.value(subStyle->type()));
147             StoredSubStyleList::ConstIterator end(storedSubStyles.end());
148             for (StoredSubStyleList::ConstIterator it(storedSubStyles.begin()); it != end; ++it) {
149                 if (Style::compare(subStyle.data(), (*it).data())) {
150         //             debugSheetsStyle <<"[REUSING EXISTING SUBSTYLE]";
151                     subStyles.append(qMakePair(reg, *it));
152                     foundShared = true;
153                     break;
154                 }
155             }
156             if (!foundShared) {
157                 // insert substyle and add to the used substyle list
158                 //if (reg.contains(QPoint(1,1))) {debugSheetsStyle<<"load:"<<reg<<':'; subStyle.data()->dump();}
159                 subStyles.append(qMakePair(reg, subStyle));
160             }
161         }
162     }
163     d->tree.load(subStyles);
164     int e = t.elapsed();
165     total += e;
166     debugSheetsStyle << "Time: " << e << total;
167 }
168 
ensureLoaded()169 void StyleStorage::Private::ensureLoaded()
170 {
171     if (loader) {
172         loader->waitForFinished();
173         delete loader;
174         loader = 0;
175     }
176 }
177 
StyleStorage(Map * map)178 StyleStorage::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 
StyleStorage(const StyleStorage & other)187 StyleStorage::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 
~StyleStorage()205 StyleStorage::~StyleStorage()
206 {
207     delete d->loader; // in a multi-threaded approach this needs more care
208     delete d;
209 }
210 
contains(const QPoint & point) const211 Style 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             Style st = *d->cache.object(point);
224             //if (point.x() == 1 && point.y() == 1) {debugSheetsStyle <<"StyleStorage: cached style:"<<point<<':'; st.dump();}
225             return st;
226         }
227     }
228     // not found, lookup in the tree
229     QList<SharedSubStyle> subStyles = d->tree.contains(point);
230     //if (point.x() == 1 && point.y() == 1) {debugSheetsStyle <<"StyleStorage: substyles:"<<point<<':'; for (const SharedSubStyle &s : subStyles) {debugSheetsStyle<<s.data()->debugData();}}
231     if (subStyles.isEmpty()) {
232         Style *style = styleManager()->defaultStyle();
233         // let's try caching empty styles too, the lookup is rather expensive still
234         {
235 #ifdef CALLIGRA_SHEETS_MT
236             QMutexLocker ml(&d->cacheMutex);
237 #endif
238             // insert style into the cache
239             d->cache.insert(point, style);
240             d->cachedArea += QRect(point, point);
241         }
242 
243         return *style;
244     }
245     Style* style = new Style();
246     (*style) = composeStyle(subStyles);
247 
248     {
249 #ifdef CALLIGRA_SHEETS_MT
250         QMutexLocker ml(&d->cacheMutex);
251 #endif
252         // insert style into the cache
253         d->cache.insert(point, style);
254         d->cachedArea += QRect(point, point);
255     }
256     //if (point.x() == 1 && point.y() == 1) {debugSheetsStyle <<"StyleStorage: style:"<<point<<':'; style->dump();}
257     return *style;
258 }
259 
contains(const QRect & rect) const260 Style StyleStorage::contains(const QRect& rect) const
261 {
262     d->ensureLoaded();
263     QList<SharedSubStyle> subStyles = d->tree.contains(rect);
264     return composeStyle(subStyles);
265 }
266 
intersects(const QRect & rect) const267 Style StyleStorage::intersects(const QRect& rect) const
268 {
269     d->ensureLoaded();
270     QList<SharedSubStyle> subStyles = d->tree.intersects(rect);
271     return composeStyle(subStyles);
272 }
273 
undoData(const Region & region) const274 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::undoData(const Region& region) const
275 {
276     d->ensureLoaded();
277     QList< QPair<QRectF, SharedSubStyle> > result;
278     Region::ConstIterator end = region.constEnd();
279     for (Region::ConstIterator it = region.constBegin(); it != end; ++it) {
280         const QRect rect = (*it)->rect();
281         QList< QPair<QRectF, SharedSubStyle> > pairs = d->tree.intersectingPairs(rect).values();
282         for (int i = 0; i < pairs.count(); ++i) {
283             // trim the rects
284             pairs[i].first = pairs[i].first.intersected(rect);
285         }
286         // Always a default subStyle first, even if there are no pairs.
287         result << qMakePair(QRectF(rect), SharedSubStyle()) << pairs;
288     }
289     return result;
290 }
291 
usedArea() const292 QRect StyleStorage::usedArea() const
293 {
294     d->ensureLoaded();
295     if (d->usedArea.isEmpty())
296         return QRect(1, 1, 0, 0);
297     return QRect(QPoint(1, 1), d->usedArea.boundingRect().bottomRight());
298 }
299 
300 // craete default styles in the style tables - used in Odf saving
saveCreateDefaultStyles(int & maxCols,int & maxRows,QMap<int,Style> & columnDefaultStyles,QMap<int,Style> & rowDefaultStyles) const301 void StyleStorage::saveCreateDefaultStyles(int& maxCols, int& maxRows, QMap<int, Style> &columnDefaultStyles, QMap<int, Style> &rowDefaultStyles) const
302 {
303     d->ensureLoaded();
304 #if 0 // TODO
305     // If we have both, column and row styles, we can take the short route.
306     if (!d->usedColumns.isEmpty() && !d->usedRows.isEmpty()) {
307         for (int i = 0; i < d->usedColumns.count(); ++i) {
308             const int col = d->usedColumns[i];
309             columnDefaultStyles[col].insertSubStyle(contains(QRect(col, 1, 1, KS_rowMax)));
310         }
311         for (int i = 0; i < d->usedRow.count(); ++i) {
312             const int row = d->usedRow[i];
313             rowDefaultStyles[row].insertSubStyle(contains(QRect(1, row, KS_colMax, 1)));
314         }
315         return;
316     }
317 #endif
318     const QRect sheetRect(QPoint(1, 1), QPoint(KS_colMax, KS_rowMax));
319     if (d->usedColumns.count() != 0) {
320         maxCols = qMax(maxCols, (--d->usedColumns.constEnd()).key());
321         maxRows = KS_rowMax;
322     }
323     if (d->usedRows.count() != 0) {
324         maxCols = KS_colMax;
325         maxRows = qMax(maxRows, (--d->usedRows.constEnd()).key());
326     }
327     const QList< QPair<QRectF, SharedSubStyle> > pairs = d->tree.intersectingPairs(sheetRect).values();
328     for (int i = 0; i < pairs.count(); ++i) {
329         const QRect rect = pairs[i].first.toRect();
330         // column default cell styles
331         // Columns have no content. Prefer them over rows for the default cell styles.
332         if (rect.top() == 1 && rect.bottom() == maxRows) {
333             for (int col = rect.left(); col <= rect.right(); ++col) {
334                 if (pairs[i].second.data()->type() == Style::DefaultStyleKey)
335                     columnDefaultStyles.remove(col);
336                 else
337                     columnDefaultStyles[col].insertSubStyle(pairs[i].second);
338             }
339         }
340         // row default cell styles
341         else if (rect.left() == 1 && rect.right() == maxCols) {
342             for (int row = rect.top(); row <= rect.bottom(); ++row) {
343                 if (pairs[i].second.data()->type() == Style::DefaultStyleKey)
344                     rowDefaultStyles.remove(row);
345                 else
346                     rowDefaultStyles[row].insertSubStyle(pairs[i].second);
347             }
348         }
349     }
350 }
351 
nextColumnStyleIndex(int column) const352 int StyleStorage::nextColumnStyleIndex(int column) const
353 {
354     d->ensureLoaded();
355     QMap<int, bool>::iterator it = d->usedColumns.upperBound(column + 1);
356     return (it == d->usedColumns.end()) ? 0 : it.key();
357 }
358 
nextRowStyleIndex(int row) const359 int StyleStorage::nextRowStyleIndex(int row) const
360 {
361     d->ensureLoaded();
362     QMap<int, bool>::iterator it = d->usedRows.upperBound(row + 1);
363     return (it == d->usedRows.end()) ? 0 : it.key();
364 }
365 
firstColumnIndexInRow(int row) const366 int StyleStorage::firstColumnIndexInRow(int row) const
367 {
368     d->ensureLoaded();
369     const QRect rect = (d->usedArea & QRect(QPoint(1, row), QPoint(KS_colMax, row))).boundingRect();
370     return rect.isNull() ? 0 : rect.left();
371 }
372 
nextColumnIndexInRow(int column,int row) const373 int StyleStorage::nextColumnIndexInRow(int column, int row) const
374 {
375     d->ensureLoaded();
376     const QRect rect = (d->usedArea & QRect(QPoint(column + 1, row), QPoint(KS_colMax, row))).boundingRect();
377     return rect.isNull() ? 0 : rect.left();
378 }
379 
insert(const QRect & rect,const SharedSubStyle & subStyle,bool markRegionChanged)380 void StyleStorage::insert(const QRect& rect, const SharedSubStyle& subStyle, bool markRegionChanged)
381 {
382     d->ensureLoaded();
383 //     debugSheetsStyle <<"StyleStorage: inserting" << SubStyle::name(subStyle->type()) <<" into" << rect;
384     // keep track of the used area
385     const bool isDefault = subStyle->type() == Style::DefaultStyleKey;
386     if (rect.top() == 1 && rect.bottom() >= KS_rowMax) {
387         for (int i = rect.left(); i <= rect.right(); ++i) {
388             if (isDefault)
389                 d->usedColumns.remove(i);
390             else
391                 d->usedColumns.insert(i, true);
392         }
393         if (isDefault)
394             d->usedArea -= rect;
395     } else if (rect.left() == 1 && rect.right() >= KS_colMax) {
396         for (int i = rect.top(); i <= rect.bottom(); ++i) {
397             if (isDefault)
398                 d->usedRows.remove(i);
399             else
400                 d->usedRows.insert(i, true);
401         }
402         if (isDefault)
403             d->usedArea -= rect;
404     } else {
405         if (isDefault)
406             d->usedArea -= rect;
407         else
408             d->usedArea += rect;
409     }
410 
411     // lookup already used substyles
412     typedef const QList< SharedSubStyle> StoredSubStyleList;
413     StoredSubStyleList& storedSubStyles(d->subStyles.value(subStyle->type()));
414     StoredSubStyleList::ConstIterator end(storedSubStyles.end());
415     for (StoredSubStyleList::ConstIterator it(storedSubStyles.begin()); it != end; ++it) {
416         if (Style::compare(subStyle.data(), (*it).data())) {
417 //             debugSheetsStyle <<"[REUSING EXISTING SUBSTYLE]";
418             d->tree.insert(rect, *it);
419             if (markRegionChanged) {
420                 regionChanged(rect);
421             }
422             return;
423         }
424     }
425     // insert substyle and add to the used substyle list
426     d->tree.insert(rect, subStyle);
427     d->subStyles[subStyle->type()].append(subStyle);
428     if (markRegionChanged) {
429         regionChanged(rect);
430     }
431 }
432 
insert(const Region & region,const Style & style)433 void StyleStorage::insert(const Region& region, const Style& style)
434 {
435     d->ensureLoaded();
436     if (style.isEmpty())
437         return;
438     foreach(const SharedSubStyle& subStyle, style.subStyles()) {
439         Region::ConstIterator end(region.constEnd());
440         for (Region::ConstIterator it(region.constBegin()); it != end; ++it) {
441             // insert substyle
442             insert((*it)->rect(), subStyle, false);
443         }
444     }
445     for (Region::ConstIterator it(region.constBegin()), end(region.constEnd()); it != end; ++it) {
446         regionChanged((*it)->rect());
447     }
448 }
449 
load(const QList<QPair<QRegion,Style>> & styles)450 void StyleStorage::load(const QList<QPair<QRegion, Style> >& styles)
451 {
452     Q_ASSERT(!d->loader);
453     d->loader = new StyleStorageLoaderJob(this, styles);
454 }
455 
insertRows(int position,int number)456 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::insertRows(int position, int number)
457 {
458     d->ensureLoaded();
459     const QRect invalidRect(1, position, KS_colMax, KS_rowMax);
460     // invalidate the affected, cached styles
461     invalidateCache(invalidRect);
462     // update the used area
463     const QRegion usedArea = d->usedArea & invalidRect;
464     d->usedArea -= invalidRect;
465     d->usedArea += usedArea.translated(0, number);
466     const QVector<QRect> rects = (d->usedArea & QRect(1, position - 1, KS_colMax, 1)).rects();
467     for (int i = 0; i < rects.count(); ++i)
468         d->usedArea += rects[i].adjusted(0, 1, 0, number + 1);
469     // update the used rows
470     QMap<int, bool> map;
471     QMap<int, bool>::iterator begin = d->usedRows.lowerBound(position);
472     QMap<int, bool>::iterator end = d->usedRows.end();
473     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
474         if (it.key() + number <= KS_rowMax)
475             map.insert(it.key() + number, true);
476     }
477     for (QMap<int, bool>::iterator it = begin; it != d->usedRows.end(); )
478         it = d->usedRows.erase(it);
479     d->usedRows.unite(map);
480     // process the tree
481     QList< QPair<QRectF, SharedSubStyle> > undoData;
482     undoData << qMakePair(QRectF(1, KS_rowMax - number + 1, KS_colMax, number), SharedSubStyle());
483     undoData << d->tree.insertRows(position, number);
484     return undoData;
485 }
486 
insertColumns(int position,int number)487 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::insertColumns(int position, int number)
488 {
489     d->ensureLoaded();
490     const QRect invalidRect(position, 1, KS_colMax, KS_rowMax);
491     // invalidate the affected, cached styles
492     invalidateCache(invalidRect);
493     // update the used area
494     const QRegion usedArea = d->usedArea & invalidRect;
495     d->usedArea -= invalidRect;
496     d->usedArea += usedArea.translated(number, 0);
497     const QVector<QRect> rects = (d->usedArea & QRect(position - 1, 0, 1, KS_rowMax)).rects();
498     for (int i = 0; i < rects.count(); ++i)
499         d->usedArea += rects[i].adjusted(1, 0, number + 1, 0);
500     // update the used columns
501     QMap<int, bool> map;
502     QMap<int, bool>::iterator begin = d->usedColumns.upperBound(position);
503     QMap<int, bool>::iterator end = d->usedColumns.end();
504     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
505         if (it.key() + number <= KS_colMax)
506             map.insert(it.key() + number, true);
507     }
508     for (QMap<int, bool>::iterator it = begin; it != d->usedColumns.end(); )
509         it = d->usedColumns.erase(it);
510     d->usedColumns.unite(map);
511     // process the tree
512     QList< QPair<QRectF, SharedSubStyle> > undoData;
513     undoData << qMakePair(QRectF(KS_colMax - number + 1, 1, number, KS_rowMax), SharedSubStyle());
514     undoData << d->tree.insertColumns(position, number);
515     return undoData;
516 }
517 
removeRows(int position,int number)518 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::removeRows(int position, int number)
519 {
520     d->ensureLoaded();
521     const QRect invalidRect(1, position, KS_colMax, KS_rowMax);
522     // invalidate the affected, cached styles
523     invalidateCache(invalidRect);
524     // update the used area
525     const QRegion usedArea = d->usedArea & QRect(1, position + number, KS_colMax, KS_rowMax);
526     d->usedArea -= invalidRect;
527     d->usedArea += usedArea.translated(0, -number);
528     // update the used rows
529     QMap<int, bool> map;
530     QMap<int, bool>::iterator begin = d->usedRows.upperBound(position);
531     QMap<int, bool>::iterator end = d->usedRows.end();
532     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
533         if (it.key() - number >= position)
534             map.insert(it.key() - number, true);
535     }
536     for (QMap<int, bool>::iterator it = begin; it != d->usedRows.end(); )
537         it = d->usedRows.erase(it);
538     d->usedRows.unite(map);
539     // process the tree
540     QList< QPair<QRectF, SharedSubStyle> > undoData;
541     undoData << qMakePair(QRectF(1, position, KS_colMax, number), SharedSubStyle());
542     undoData << d->tree.removeRows(position, number);
543     return undoData;
544 }
545 
removeColumns(int position,int number)546 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::removeColumns(int position, int number)
547 {
548     d->ensureLoaded();
549     const QRect invalidRect(position, 1, KS_colMax, KS_rowMax);
550     // invalidate the affected, cached styles
551     invalidateCache(invalidRect);
552     // update the used area
553     const QRegion usedArea = d->usedArea & QRect(position + number, 1, KS_colMax, KS_rowMax);
554     d->usedArea -= invalidRect;
555     d->usedArea += usedArea.translated(-number, 0);
556     // update the used columns
557     QMap<int, bool> map;
558     QMap<int, bool>::iterator begin = d->usedColumns.upperBound(position);
559     QMap<int, bool>::iterator end = d->usedColumns.end();
560     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
561         if (it.key() - number >= position)
562             map.insert(it.key() - number, true);
563     }
564     for (QMap<int, bool>::iterator it = begin; it != d->usedColumns.end(); )
565         it = d->usedColumns.erase(it);
566     d->usedColumns.unite(map);
567     // process the tree
568     QList< QPair<QRectF, SharedSubStyle> > undoData;
569     undoData << qMakePair(QRectF(position, 1, number, KS_rowMax), SharedSubStyle());
570     undoData << d->tree.removeColumns(position, number);
571     return undoData;
572 }
573 
insertShiftRight(const QRect & rect)574 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::insertShiftRight(const QRect& rect)
575 {
576     d->ensureLoaded();
577     const QRect invalidRect(rect.topLeft(), QPoint(KS_colMax, rect.bottom()));
578     QList< QPair<QRectF, SharedSubStyle> > undoData;
579     undoData << qMakePair(QRectF(rect), SharedSubStyle());
580     undoData << d->tree.insertShiftRight(rect);
581     regionChanged(invalidRect);
582     // update the used area
583     const QRegion usedArea = d->usedArea & invalidRect;
584     d->usedArea -= invalidRect;
585     d->usedArea += usedArea.translated(rect.width(), 0);
586     const QVector<QRect> rects = (d->usedArea & QRect(rect.left() - 1, rect.top(), 1, rect.height())).rects();
587     for (int i = 0; i < rects.count(); ++i)
588         d->usedArea += rects[i].adjusted(1, 0, rect.width() + 1, 0);
589     // update the used columns
590     QMap<int, bool>::iterator begin = d->usedColumns.upperBound(rect.left());
591     QMap<int, bool>::iterator end = d->usedColumns.end();
592     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
593         if (it.key() + rect.width() <= KS_colMax)
594             d->usedArea += QRect(it.key() + rect.width(), rect.top(), rect.width(), rect.height());
595     }
596     if (d->usedColumns.contains(rect.left() - 1))
597         d->usedArea += rect;
598     return undoData;
599 }
600 
insertShiftDown(const QRect & rect)601 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::insertShiftDown(const QRect& rect)
602 {
603     d->ensureLoaded();
604     const QRect invalidRect(rect.topLeft(), QPoint(rect.right(), KS_rowMax));
605     QList< QPair<QRectF, SharedSubStyle> > undoData;
606     undoData << qMakePair(QRectF(rect), SharedSubStyle());
607     undoData << d->tree.insertShiftDown(rect);
608     regionChanged(invalidRect);
609     // update the used area
610     const QRegion usedArea = d->usedArea & invalidRect;
611     d->usedArea -= invalidRect;
612     d->usedArea += usedArea.translated(0, rect.height());
613     const QVector<QRect> rects = (d->usedArea & QRect(rect.left(), rect.top() - 1, rect.width(), 1)).rects();
614     for (int i = 0; i < rects.count(); ++i)
615         d->usedArea += rects[i].adjusted(0, 1, 0, rect.height() + 1);
616     // update the used rows
617     QMap<int, bool>::iterator begin = d->usedRows.upperBound(rect.top());
618     QMap<int, bool>::iterator end = d->usedRows.end();
619     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
620         if (it.key() + rect.height() <= KS_rowMax)
621             d->usedArea += QRect(rect.left(), it.key() + rect.height(), rect.width(), rect.height());
622     }
623     if (d->usedRows.contains(rect.top() - 1))
624         d->usedArea += rect;
625     return undoData;
626 }
627 
removeShiftLeft(const QRect & rect)628 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::removeShiftLeft(const QRect& rect)
629 {
630     d->ensureLoaded();
631     const QRect invalidRect(rect.topLeft(), QPoint(KS_colMax, rect.bottom()));
632     QList< QPair<QRectF, SharedSubStyle> > undoData;
633     undoData << qMakePair(QRectF(rect), SharedSubStyle());
634     undoData << d->tree.removeShiftLeft(rect);
635     regionChanged(invalidRect);
636     // update the used area
637     const QRegion usedArea = d->usedArea & QRect(rect.right() + 1, rect.top(), KS_colMax, rect.height());
638     d->usedArea -= invalidRect;
639     d->usedArea += usedArea.translated(-rect.width(), 0);
640     // update the used columns
641     QMap<int, bool>::iterator begin = d->usedColumns.upperBound(rect.right() + 1);
642     QMap<int, bool>::iterator end = d->usedColumns.end();
643     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
644         if (it.key() - rect.width() >= rect.left())
645             d->usedArea += QRect(it.key() - rect.width(), rect.top(), rect.width(), rect.height());
646     }
647     return undoData;
648 }
649 
removeShiftUp(const QRect & rect)650 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::removeShiftUp(const QRect& rect)
651 {
652     d->ensureLoaded();
653     const QRect invalidRect(rect.topLeft(), QPoint(rect.right(), KS_rowMax));
654     QList< QPair<QRectF, SharedSubStyle> > undoData;
655     undoData << qMakePair(QRectF(rect), SharedSubStyle());
656     undoData << d->tree.removeShiftUp(rect);
657     regionChanged(invalidRect);
658     // update the used area
659     const QRegion usedArea = d->usedArea & QRect(rect.left(), rect.bottom() + 1, rect.width(), KS_rowMax);
660     d->usedArea -= invalidRect;
661     d->usedArea += usedArea.translated(0, -rect.height());
662     // update the used rows
663     QMap<int, bool>::iterator begin = d->usedRows.upperBound(rect.bottom() + 1);
664     QMap<int, bool>::iterator end = d->usedRows.end();
665     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
666         if (it.key() - rect.height() >= rect.top())
667             d->usedArea += QRect(rect.left(), it.key() - rect.height(), rect.width(), rect.height());
668     }
669     return undoData;
670 }
671 
invalidateCache()672 void StyleStorage::invalidateCache()
673 {
674     // still busy loading? no cache to invalidate
675     if (d->loader && !d->loader->isFinished())
676         return;
677 
678 #ifdef CALLIGRA_SHEETS_MT
679     QMutexLocker ml(&d->cacheMutex);
680 #endif
681     d->cache.clear();
682     d->cachedArea = QRegion();
683 }
684 
garbageCollection()685 void StyleStorage::garbageCollection()
686 {
687     // still busy loading? no garbage to collect
688     if (d->loader && !d->loader->isFinished())
689         return;
690 
691     // any possible garbage left?
692     if (d->possibleGarbage.isEmpty())
693         return;
694 
695     const int currentZIndex = d->possibleGarbage.constBegin().key();
696     const QPair<QRectF, SharedSubStyle> currentPair = d->possibleGarbage.take(currentZIndex);
697 
698     // check whether the named style still exists
699     if (currentPair.second->type() == Style::NamedStyleKey &&
700             !styleManager()->style(static_cast<const NamedStyle*>(currentPair.second.data())->name)) {
701         debugSheetsStyle << "removing" << currentPair.second->debugData()
702         << "at" << Region(currentPair.first.toRect()).name()
703         << "used" << currentPair.second->ref << "times" << endl;
704         d->tree.remove(currentPair.first.toRect(), currentPair.second);
705         d->subStyles[currentPair.second->type()].removeAll(currentPair.second);
706         QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
707         return; // already done
708     }
709 
710     typedef QPair<QRectF, SharedSubStyle> SharedSubStylePair;
711     QMap<int, SharedSubStylePair> pairs = d->tree.intersectingPairs(currentPair.first.toRect());
712     if (pairs.isEmpty())   // actually never true, just for sanity
713         return;
714     int zIndex = pairs.constBegin().key();
715     SharedSubStylePair pair = pairs[zIndex];
716 
717     // check whether the default style is placed first
718     if (zIndex == currentZIndex &&
719             currentPair.second->type() == Style::DefaultStyleKey &&
720             pair.second->type() == Style::DefaultStyleKey &&
721             pair.first == currentPair.first) {
722         debugSheetsStyle << "removing default style"
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 indentation:
731     // check whether the default indentation is placed first
732     if (zIndex == currentZIndex &&
733             currentPair.second->type() == Style::Indentation &&
734             static_cast<const SubStyleOne<Style::Indentation, int>*>(currentPair.second.data())->value1 == 0 &&
735             pair.first == currentPair.first) {
736         debugSheetsStyle << "removing default indentation"
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     // special handling for precision:
745     // check whether the storage default precision is placed first
746     if (zIndex == currentZIndex &&
747             currentPair.second->type() == Style::Precision &&
748             static_cast<const SubStyleOne<Style::Precision, int>*>(currentPair.second.data())->value1 == 0 &&
749             pair.first == currentPair.first) {
750         debugSheetsStyle << "removing default precision"
751         << "at" << Region(currentPair.first.toRect()).name()
752         << "used" << currentPair.second->ref << "times" << endl;
753         d->tree.remove(currentPair.first.toRect(), currentPair.second);
754         QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
755         return; // already done
756     }
757 
758     // check, if the current substyle is covered by others added after it
759     bool found = false;
760     QMap<int, SharedSubStylePair>::ConstIterator end = pairs.constEnd();
761     for (QMap<int, SharedSubStylePair>::ConstIterator it = pairs.constFind(currentZIndex); it != end; ++it) {
762         zIndex = it.key();
763         pair = it.value();
764 
765         // as long as the substyle in question was not found, skip the substyle
766         if (!found) {
767             if (pair.first == currentPair.first &&
768                     Style::compare(pair.second.data(), currentPair.second.data()) &&
769                     zIndex == currentZIndex) {
770                 found = true;
771             }
772             continue;
773         }
774 
775         // remove the current pair, if another substyle of the same type,
776         // the default style or a named style follows and the rectangle
777         // is completely covered
778         if (zIndex != currentZIndex &&
779                 (pair.second->type() == currentPair.second->type() ||
780                  pair.second->type() == Style::DefaultStyleKey ||
781                  pair.second->type() == Style::NamedStyleKey) &&
782                 pair.first.toRect().contains(currentPair.first.toRect())) {
783             // special handling for indentation
784             // only remove, if covered by default
785             if (pair.second->type() == Style::Indentation &&
786                     static_cast<const SubStyleOne<Style::Indentation, int>*>(pair.second.data())->value1 != 0) {
787                 continue;
788             }
789 
790             // special handling for precision
791             // only remove, if covered by default
792             if (pair.second->type() == Style::Precision &&
793                     static_cast<const SubStyleOne<Style::Precision, int>*>(pair.second.data())->value1 != 0) {
794                 continue;
795             }
796 
797             debugSheetsStyle << "removing" << currentPair.second->debugData()
798             << "at" << Region(currentPair.first.toRect()).name()
799             << "used" << currentPair.second->ref << "times" << endl;
800             d->tree.remove(currentPair.first.toRect(), currentPair.second, currentZIndex);
801 #if 0
802             debugSheetsStyle << "StyleStorage: usage of" << currentPair.second->debugData() << " is" << currentPair.second->ref;
803             // FIXME Stefan: The usage of substyles used once should be
804             //               two (?) here, not more. Why is this not the case?
805             //               The shared pointers are used by:
806             //               a) the tree
807             //               b) the reusage list (where it should be removed)
808             //               c) the cached styles (!)
809             //               d) the undo data of operations (!)
810             if (currentPair.second->ref == 2) {
811                 debugSheetsStyle << "StyleStorage: removing" << currentPair.second << " from the used subStyles";
812                 d->subStyles[currentPair.second->type()].removeAll(currentPair.second);
813             }
814 #endif
815             break;
816         }
817     }
818     QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
819 }
820 
regionChanged(const QRect & rect)821 void StyleStorage::regionChanged(const QRect& rect)
822 {
823     // still busy loading? no garbage to collect
824     if (d->loader && !d->loader->isFinished())
825         return;
826     if (d->map->isLoading())
827         return;
828     // mark the possible garbage
829     // NOTE Stefan: The map may contain multiple indices. The already existing possible garbage has
830     // has to be inserted most recently, because it should be accessed first.
831     d->possibleGarbage = d->tree.intersectingPairs(rect).unite(d->possibleGarbage);
832     QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
833     // invalidate cache
834     invalidateCache(rect);
835 }
836 
invalidateCache(const QRect & rect)837 void StyleStorage::invalidateCache(const QRect& rect)
838 {
839     // still busy loading? no cache to invalidate
840     if (d->loader && !d->loader->isFinished())
841         return;
842 
843 #ifdef CALLIGRA_SHEETS_MT
844     QMutexLocker ml(&d->cacheMutex);
845 #endif
846 //     debugSheetsStyle <<"StyleStorage: Invalidating" << rect;
847     const QRegion region = d->cachedArea.intersected(rect);
848     d->cachedArea = d->cachedArea.subtracted(rect);
849     foreach(const QRect& rect, region.rects()) {
850         for (int col = rect.left(); col <= rect.right(); ++col) {
851             for (int row = rect.top(); row <= rect.bottom(); ++row) {
852 //                 debugSheetsStyle <<"StyleStorage: Removing cached style for" << Cell::name( col, row );
853                 d->cache.remove(QPoint(col, row));     // also deletes it
854             }
855         }
856     }
857 }
858 
composeStyle(const QList<SharedSubStyle> & subStyles) const859 Style StyleStorage::composeStyle(const QList<SharedSubStyle>& subStyles) const
860 {
861     d->ensureLoaded();
862 
863     if (subStyles.isEmpty()) {
864 //         debugSheetsStyle <<"StyleStorage:" << "nothing to merge, return the default style";
865         return *styleManager()->defaultStyle();
866     }
867     // From OpenDocument-v1.2-os-part1 16.2<style:style>
868     //
869     // The <style:style> element represents styles.
870     //
871     // Styles defined by the <style:style> element use a hierarchical style model.
872     // The <style:style> element supports inheritance of formatting properties by a style from its parent style.
873     // A parent style is specified by the style:parent-style-name attribute on a <style:style> element.
874     //
875     // The determination of the value of a formatting property begins with any style that is specified by an element.
876     // If the formatting property is present in that style, its value is used.
877     //
878     // If that style does not specify a value for that formatting property and it has a parent style,
879     // the value of the formatting element is taken from the parent style, if present.
880     //
881     // If the parent style does not have a value for the formatting property, the search for the formatting property value continues up parent styles
882     // until either the formatting property has been found or a style is found with no parent style.
883     //
884     // If a search of the parent styles of a style does not result in a value for a formatting property,
885     // the determination of its value depends on the style family and the element to which a style is applied.
886 
887     // TODO review loading of libreOffice generated files:
888     // It seems libreOffice saves parent also when parent is the Default style.
889     // Sheets do not do this, it is handled implicitly.
890     // According to the spec, both ways should be ok,
891     // but the result is that when loading lo files, it may exist multiple (two) NamedStyleKey substyles:
892     // One loaded explicitly (first in the list), and one generated by our loading code (later in the list).
893     // We use the last one in the list here, this should be our generated one.
894     CustomStyle *namedStyle = 0;
895     for (int i = subStyles.count() - 1; i >= 0; --i) {
896         if (subStyles[i]->type() == Style::NamedStyleKey) {
897             namedStyle = styleManager()->style(static_cast<const NamedStyle*>(subStyles[i].data())->name);
898             if (namedStyle) {
899                 debugSheetsStyle<<"Compose found namedstyle:"<<static_cast<const NamedStyle*>(subStyles[i].data())->name<<namedStyle->parentName();namedStyle->dump();
900                 break;
901             }
902         }
903     }
904 
905     Style style;
906     // get attributes from parent styles
907     if (namedStyle) {
908         // first, load the attributes of the parent style(s)
909         QList<CustomStyle*> parentStyles;
910         CustomStyle *parentStyle = styleManager()->style(namedStyle->parentName());
911         // debugSheetsStyle <<"StyleStorage:" << namedStyle->name() <<"'s parent =" << namedStyle->parentName();
912         while (parentStyle) {
913             // debugSheetsStyle <<"StyleStorage:" << parentStyle->name() <<"'s parent =" << parentStyle->parentName();
914             parentStyles.prepend(parentStyle);
915             parentStyle = styleManager()->style(parentStyle->parentName());
916         }
917         Style tmpStyle;
918         for (int i = 0; i < parentStyles.count(); ++i) {
919             // debugSheetsStyle <<"StyleStorage: merging" << parentStyles[i]->name() <<" in.";
920             tmpStyle = *parentStyles[i];
921             tmpStyle.merge(style); // insert/replace substyles in tmpStyle with substyles from style
922             style = tmpStyle;
923         }
924         // second, merge the other attributes in
925         // debugSheetsStyle <<"StyleStorage: merging" << namedStyle->name() <<" in.";
926         tmpStyle = *namedStyle;
927         tmpStyle.merge(style); // insert/replace substyles in tmpStyle with substyles from style
928         style = tmpStyle;
929         // not the default anymore
930         style.clearAttribute(Style::DefaultStyleKey);
931         // reset the parent name
932         style.setParentName(namedStyle->name());
933         //                 debugSheetsStyle <<"StyleStorage: merging done";
934     }
935     for (int i = 0; i < subStyles.count(); ++i) {
936         if (subStyles[i]->type() == Style::DefaultStyleKey) {
937             // skip
938         } else if (subStyles[i]->type() == Style::NamedStyleKey) {
939             // treated above
940         } else if (subStyles[i]->type() == Style::Indentation) {
941             // special handling for indentation
942             const int indentation = static_cast<const SubStyleOne<Style::Indentation, int>*>(subStyles[i].data())->value1;
943             if (indentation == 0 || (style.indentation() + indentation <= 0))
944                 style.clearAttribute(Style::Indentation);   // reset
945             else
946                 style.setIndentation(style.indentation() + indentation);   // increase/decrease
947         } else if (subStyles[i]->type() == Style::Precision) {
948             // special handling for precision
949             // The Style default (-1) and the storage default (0) differ.
950             const int precision = static_cast<const SubStyleOne<Style::Precision, int>*>(subStyles[i].data())->value1;
951             if (precision == 0)   // storage default
952                 style.clearAttribute(Style::Precision);   // reset
953             else {
954                 if (style.precision() == -1)   // Style default
955                     style.setPrecision(qMax(0, precision));     // positive initial value
956                 else if (style.precision() + precision <= 0)
957                     style.setPrecision(0);
958                 else if (style.precision() + precision >= 10)
959                     style.setPrecision(10);
960                 else
961                     style.setPrecision(style.precision() + precision);   // increase/decrease
962             }
963         } else {
964             // insert the substyle
965 //             debugSheetsStyle <<"StyleStorage: inserting" << subStyles[i]->debugData();
966             style.insertSubStyle(subStyles[i]);
967             // not the default anymore
968             style.clearAttribute(Style::DefaultStyleKey);
969         }
970     }
971     // Implicitly merge in any missing attributes from the family (table-cell) default style
972     // It might have been merged in via parent styles above, but we cannot rely on that.
973     if (!styleManager()->defaultStyle()->isEmpty()) {
974         // debugSheetsStyle << "StyleStorage: merging family default in";
975         Style tmpStyle = *styleManager()->defaultStyle();
976         tmpStyle.clearAttribute(Style::DefaultStyleKey);
977         tmpStyle.merge(style); // insert/replace substyles in tmpStyle with substyles from style
978         style = tmpStyle;
979     }
980     return style;
981 }
982 
styleManager() const983 StyleManager* StyleStorage::styleManager() const
984 {
985     return d->map->styleManager();
986 }
987