File indexing completed on 2024-04-28 16:21:34

0001 /* This file is part of the KDE project
0002    Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
0003    Copyright 2006,2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018    Boston, MA 02110-1301, USA.
0019 */
0020 
0021 // Local
0022 #include "StyleStorage.h"
0023 
0024 #include <QCache>
0025 #include <QRegion>
0026 #include <QTimer>
0027 #include <QRunnable>
0028 #ifdef CALLIGRA_SHEETS_MT
0029 #include <QMutex>
0030 #include <QMutexLocker>
0031 #endif
0032 
0033 #include "Global.h"
0034 #include "Map.h"
0035 #include "RTree.h"
0036 #include "Style.h"
0037 #include "StyleManager.h"
0038 #include "RectStorage.h"
0039 
0040 static const int g_maximumCachedStyles = 10000;
0041 
0042 using namespace Calligra::Sheets;
0043 
0044 class Q_DECL_HIDDEN StyleStorage::Private
0045 {
0046 public:
0047     Private()
0048 #ifdef CALLIGRA_SHEETS_MT
0049         : cacheMutex(QMutex::Recursive)
0050 #endif
0051     {}
0052     Map* map;
0053     RTree<SharedSubStyle> tree;
0054     QMap<int, bool> usedColumns; // FIXME Stefan: Use QList and std::upper_bound() for insertion.
0055     QMap<int, bool> usedRows;
0056     QRegion usedArea;
0057     QHash<Style::Key, QList<SharedSubStyle> > subStyles;
0058     QMap<int, QPair<QRectF, SharedSubStyle> > possibleGarbage;
0059     QCache<QPoint, Style> cache;
0060     QRegion cachedArea;
0061     StyleStorageLoaderJob* loader;
0062 #ifdef CALLIGRA_SHEETS_MT
0063     QMutex cacheMutex;
0064 #endif
0065 
0066     void ensureLoaded();
0067 };
0068 
0069 class Calligra::Sheets::StyleStorageLoaderJob : public QRunnable
0070 {
0071 public:
0072     StyleStorageLoaderJob(StyleStorage* storage, const QList<QPair<QRegion, Style> >& styles);
0073     void run() override;
0074     void waitForFinished();
0075     bool isFinished();
0076     QList<QPair<QRegion, Style> > data() const { return m_styles; }
0077 private:
0078     StyleStorage* m_storage;
0079     QList<QPair<QRegion, Style> > m_styles;
0080 };
0081 
0082 StyleStorageLoaderJob::StyleStorageLoaderJob(StyleStorage *storage, const QList<QPair<QRegion, Style> > &styles)
0083     : m_storage(storage), m_styles(styles)
0084 {
0085 
0086 }
0087 
0088 void StyleStorageLoaderJob::waitForFinished()
0089 {
0090     run();
0091 }
0092 
0093 bool StyleStorageLoaderJob::isFinished()
0094 {
0095     return false;
0096 }
0097 
0098 void StyleStorageLoaderJob::run()
0099 {
0100     static int total = 0;
0101     debugSheetsStyle << "Loading styles:" << endl << m_styles;
0102     QTime t; t.start();
0103     StyleStorage::Private* d = m_storage->d;
0104     QList<QPair<QRegion, SharedSubStyle> > subStyles;
0105 
0106     d->usedArea = QRegion();
0107     d->usedColumns.clear();
0108     d->usedRows.clear();
0109     {
0110 #ifdef CALLIGRA_SHEETS_MT
0111         QMutexLocker(&d->cacheMutex);
0112 #endif
0113         d->cachedArea = QRegion();
0114         d->cache.clear();
0115     }
0116     typedef QPair<QRegion, Style> StyleRegion;
0117     foreach (const StyleRegion& styleArea, m_styles) {
0118         const QRegion& reg = styleArea.first;
0119         const Style& style = styleArea.second;
0120         if (style.isEmpty()) continue;
0121 
0122         // update used areas
0123         QRect bound = reg.boundingRect();
0124         if ((bound.top() == 1 && bound.bottom() >= KS_rowMax) || (bound.left() == 1 && bound.right() >= KS_colMax)) {
0125             foreach (const QRect& rect, reg.rects()) {
0126                 if (rect.top() == 1 && rect.bottom() >= KS_rowMax) {
0127                     for (int i = rect.left(); i <= rect.right(); ++i) {
0128                         d->usedColumns.insert(i, true);
0129                     }
0130                 } else if (rect.left() == 1 && rect.right() >= KS_colMax) {
0131                     for (int i = rect.top(); i <= rect.bottom(); ++i) {
0132                         d->usedRows.insert(i, true);
0133                     }
0134                 } else {
0135                     d->usedArea += rect;
0136                 }
0137             }
0138         } else {
0139             d->usedArea += reg;
0140         }
0141 
0142         // find substyles
0143         foreach(const SharedSubStyle& subStyle, style.subStyles()) {
0144             bool foundShared = false;
0145             typedef const QList< SharedSubStyle> StoredSubStyleList;
0146             StoredSubStyleList& storedSubStyles(d->subStyles.value(subStyle->type()));
0147             StoredSubStyleList::ConstIterator end(storedSubStyles.end());
0148             for (StoredSubStyleList::ConstIterator it(storedSubStyles.begin()); it != end; ++it) {
0149                 if (Style::compare(subStyle.data(), (*it).data())) {
0150         //             debugSheetsStyle <<"[REUSING EXISTING SUBSTYLE]";
0151                     subStyles.append(qMakePair(reg, *it));
0152                     foundShared = true;
0153                     break;
0154                 }
0155             }
0156             if (!foundShared) {
0157                 // insert substyle and add to the used substyle list
0158                 //if (reg.contains(QPoint(1,1))) {debugSheetsStyle<<"load:"<<reg<<':'; subStyle.data()->dump();}
0159                 subStyles.append(qMakePair(reg, subStyle));
0160             }
0161         }
0162     }
0163     d->tree.load(subStyles);
0164     int e = t.elapsed();
0165     total += e;
0166     debugSheetsStyle << "Time: " << e << total;
0167 }
0168 
0169 void StyleStorage::Private::ensureLoaded()
0170 {
0171     if (loader) {
0172         loader->waitForFinished();
0173         delete loader;
0174         loader = 0;
0175     }
0176 }
0177 
0178 StyleStorage::StyleStorage(Map* map)
0179         : QObject(map)
0180         , d(new Private)
0181 {
0182     d->map = map;
0183     d->cache.setMaxCost(g_maximumCachedStyles);
0184     d->loader = 0;
0185 }
0186 
0187 StyleStorage::StyleStorage(const StyleStorage& other)
0188         : QObject(other.d->map)
0189         , d(new Private)
0190 {
0191     d->map = other.d->map;
0192     d->tree = other.d->tree;
0193     d->usedColumns = other.d->usedColumns;
0194     d->usedRows = other.d->usedRows;
0195     d->usedArea = other.d->usedArea;
0196     d->subStyles = other.d->subStyles;
0197     if (other.d->loader) {
0198         d->loader = new StyleStorageLoaderJob(this, other.d->loader->data());
0199     } else {
0200         d->loader = 0;
0201     }
0202     // the other member variables are temporary stuff
0203 }
0204 
0205 StyleStorage::~StyleStorage()
0206 {
0207     delete d->loader; // in a multi-threaded approach this needs more care
0208     delete d;
0209 }
0210 
0211 Style StyleStorage::contains(const QPoint& point) const
0212 {
0213     d->ensureLoaded();
0214     if (!d->usedArea.contains(point) && !d->usedColumns.contains(point.x()) && !d->usedRows.contains(point.y()))
0215         return *styleManager()->defaultStyle();
0216 
0217     {
0218 #ifdef CALLIGRA_SHEETS_MT
0219         QMutexLocker ml(&d->cacheMutex);
0220 #endif
0221         // first, lookup point in the cache
0222         if (d->cache.contains(point)) {
0223             Style st = *d->cache.object(point);
0224             //if (point.x() == 1 && point.y() == 1) {debugSheetsStyle <<"StyleStorage: cached style:"<<point<<':'; st.dump();}
0225             return st;
0226         }
0227     }
0228     // not found, lookup in the tree
0229     QList<SharedSubStyle> subStyles = d->tree.contains(point);
0230     //if (point.x() == 1 && point.y() == 1) {debugSheetsStyle <<"StyleStorage: substyles:"<<point<<':'; for (const SharedSubStyle &s : subStyles) {debugSheetsStyle<<s.data()->debugData();}}
0231     if (subStyles.isEmpty()) {
0232         Style *style = styleManager()->defaultStyle();
0233         // let's try caching empty styles too, the lookup is rather expensive still
0234         {
0235 #ifdef CALLIGRA_SHEETS_MT
0236             QMutexLocker ml(&d->cacheMutex);
0237 #endif
0238             // insert style into the cache
0239             d->cache.insert(point, style);
0240             d->cachedArea += QRect(point, point);
0241         }
0242 
0243         return *style;
0244     }
0245     Style* style = new Style();
0246     (*style) = composeStyle(subStyles);
0247 
0248     {
0249 #ifdef CALLIGRA_SHEETS_MT
0250         QMutexLocker ml(&d->cacheMutex);
0251 #endif
0252         // insert style into the cache
0253         d->cache.insert(point, style);
0254         d->cachedArea += QRect(point, point);
0255     }
0256     //if (point.x() == 1 && point.y() == 1) {debugSheetsStyle <<"StyleStorage: style:"<<point<<':'; style->dump();}
0257     return *style;
0258 }
0259 
0260 Style StyleStorage::contains(const QRect& rect) const
0261 {
0262     d->ensureLoaded();
0263     QList<SharedSubStyle> subStyles = d->tree.contains(rect);
0264     return composeStyle(subStyles);
0265 }
0266 
0267 Style StyleStorage::intersects(const QRect& rect) const
0268 {
0269     d->ensureLoaded();
0270     QList<SharedSubStyle> subStyles = d->tree.intersects(rect);
0271     return composeStyle(subStyles);
0272 }
0273 
0274 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::undoData(const Region& region) const
0275 {
0276     d->ensureLoaded();
0277     QList< QPair<QRectF, SharedSubStyle> > result;
0278     Region::ConstIterator end = region.constEnd();
0279     for (Region::ConstIterator it = region.constBegin(); it != end; ++it) {
0280         const QRect rect = (*it)->rect();
0281         QList< QPair<QRectF, SharedSubStyle> > pairs = d->tree.intersectingPairs(rect).values();
0282         for (int i = 0; i < pairs.count(); ++i) {
0283             // trim the rects
0284             pairs[i].first = pairs[i].first.intersected(rect);
0285         }
0286         // Always a default subStyle first, even if there are no pairs.
0287         result << qMakePair(QRectF(rect), SharedSubStyle()) << pairs;
0288     }
0289     return result;
0290 }
0291 
0292 QRect StyleStorage::usedArea() const
0293 {
0294     d->ensureLoaded();
0295     if (d->usedArea.isEmpty())
0296         return QRect(1, 1, 0, 0);
0297     return QRect(QPoint(1, 1), d->usedArea.boundingRect().bottomRight());
0298 }
0299 
0300 // craete default styles in the style tables - used in Odf saving
0301 void StyleStorage::saveCreateDefaultStyles(int& maxCols, int& maxRows, QMap<int, Style> &columnDefaultStyles, QMap<int, Style> &rowDefaultStyles) const
0302 {
0303     d->ensureLoaded();
0304 #if 0 // TODO
0305     // If we have both, column and row styles, we can take the short route.
0306     if (!d->usedColumns.isEmpty() && !d->usedRows.isEmpty()) {
0307         for (int i = 0; i < d->usedColumns.count(); ++i) {
0308             const int col = d->usedColumns[i];
0309             columnDefaultStyles[col].insertSubStyle(contains(QRect(col, 1, 1, KS_rowMax)));
0310         }
0311         for (int i = 0; i < d->usedRow.count(); ++i) {
0312             const int row = d->usedRow[i];
0313             rowDefaultStyles[row].insertSubStyle(contains(QRect(1, row, KS_colMax, 1)));
0314         }
0315         return;
0316     }
0317 #endif
0318     const QRect sheetRect(QPoint(1, 1), QPoint(KS_colMax, KS_rowMax));
0319     if (d->usedColumns.count() != 0) {
0320         maxCols = qMax(maxCols, (--d->usedColumns.constEnd()).key());
0321         maxRows = KS_rowMax;
0322     }
0323     if (d->usedRows.count() != 0) {
0324         maxCols = KS_colMax;
0325         maxRows = qMax(maxRows, (--d->usedRows.constEnd()).key());
0326     }
0327     const QList< QPair<QRectF, SharedSubStyle> > pairs = d->tree.intersectingPairs(sheetRect).values();
0328     for (int i = 0; i < pairs.count(); ++i) {
0329         const QRect rect = pairs[i].first.toRect();
0330         // column default cell styles
0331         // Columns have no content. Prefer them over rows for the default cell styles.
0332         if (rect.top() == 1 && rect.bottom() == maxRows) {
0333             for (int col = rect.left(); col <= rect.right(); ++col) {
0334                 if (pairs[i].second.data()->type() == Style::DefaultStyleKey)
0335                     columnDefaultStyles.remove(col);
0336                 else
0337                     columnDefaultStyles[col].insertSubStyle(pairs[i].second);
0338             }
0339         }
0340         // row default cell styles
0341         else if (rect.left() == 1 && rect.right() == maxCols) {
0342             for (int row = rect.top(); row <= rect.bottom(); ++row) {
0343                 if (pairs[i].second.data()->type() == Style::DefaultStyleKey)
0344                     rowDefaultStyles.remove(row);
0345                 else
0346                     rowDefaultStyles[row].insertSubStyle(pairs[i].second);
0347             }
0348         }
0349     }
0350 }
0351 
0352 int StyleStorage::nextColumnStyleIndex(int column) const
0353 {
0354     d->ensureLoaded();
0355     QMap<int, bool>::iterator it = d->usedColumns.upperBound(column + 1);
0356     return (it == d->usedColumns.end()) ? 0 : it.key();
0357 }
0358 
0359 int StyleStorage::nextRowStyleIndex(int row) const
0360 {
0361     d->ensureLoaded();
0362     QMap<int, bool>::iterator it = d->usedRows.upperBound(row + 1);
0363     return (it == d->usedRows.end()) ? 0 : it.key();
0364 }
0365 
0366 int StyleStorage::firstColumnIndexInRow(int row) const
0367 {
0368     d->ensureLoaded();
0369     const QRect rect = (d->usedArea & QRect(QPoint(1, row), QPoint(KS_colMax, row))).boundingRect();
0370     return rect.isNull() ? 0 : rect.left();
0371 }
0372 
0373 int StyleStorage::nextColumnIndexInRow(int column, int row) const
0374 {
0375     d->ensureLoaded();
0376     const QRect rect = (d->usedArea & QRect(QPoint(column + 1, row), QPoint(KS_colMax, row))).boundingRect();
0377     return rect.isNull() ? 0 : rect.left();
0378 }
0379 
0380 void StyleStorage::insert(const QRect& rect, const SharedSubStyle& subStyle, bool markRegionChanged)
0381 {
0382     d->ensureLoaded();
0383 //     debugSheetsStyle <<"StyleStorage: inserting" << SubStyle::name(subStyle->type()) <<" into" << rect;
0384     // keep track of the used area
0385     const bool isDefault = subStyle->type() == Style::DefaultStyleKey;
0386     if (rect.top() == 1 && rect.bottom() >= KS_rowMax) {
0387         for (int i = rect.left(); i <= rect.right(); ++i) {
0388             if (isDefault)
0389                 d->usedColumns.remove(i);
0390             else
0391                 d->usedColumns.insert(i, true);
0392         }
0393         if (isDefault)
0394             d->usedArea -= rect;
0395     } else if (rect.left() == 1 && rect.right() >= KS_colMax) {
0396         for (int i = rect.top(); i <= rect.bottom(); ++i) {
0397             if (isDefault)
0398                 d->usedRows.remove(i);
0399             else
0400                 d->usedRows.insert(i, true);
0401         }
0402         if (isDefault)
0403             d->usedArea -= rect;
0404     } else {
0405         if (isDefault)
0406             d->usedArea -= rect;
0407         else
0408             d->usedArea += rect;
0409     }
0410 
0411     // lookup already used substyles
0412     typedef const QList< SharedSubStyle> StoredSubStyleList;
0413     StoredSubStyleList& storedSubStyles(d->subStyles.value(subStyle->type()));
0414     StoredSubStyleList::ConstIterator end(storedSubStyles.end());
0415     for (StoredSubStyleList::ConstIterator it(storedSubStyles.begin()); it != end; ++it) {
0416         if (Style::compare(subStyle.data(), (*it).data())) {
0417 //             debugSheetsStyle <<"[REUSING EXISTING SUBSTYLE]";
0418             d->tree.insert(rect, *it);
0419             if (markRegionChanged) {
0420                 regionChanged(rect);
0421             }
0422             return;
0423         }
0424     }
0425     // insert substyle and add to the used substyle list
0426     d->tree.insert(rect, subStyle);
0427     d->subStyles[subStyle->type()].append(subStyle);
0428     if (markRegionChanged) {
0429         regionChanged(rect);
0430     }
0431 }
0432 
0433 void StyleStorage::insert(const Region& region, const Style& style)
0434 {
0435     d->ensureLoaded();
0436     if (style.isEmpty())
0437         return;
0438     foreach(const SharedSubStyle& subStyle, style.subStyles()) {
0439         Region::ConstIterator end(region.constEnd());
0440         for (Region::ConstIterator it(region.constBegin()); it != end; ++it) {
0441             // insert substyle
0442             insert((*it)->rect(), subStyle, false);
0443         }
0444     }
0445     for (Region::ConstIterator it(region.constBegin()), end(region.constEnd()); it != end; ++it) {
0446         regionChanged((*it)->rect());
0447     }
0448 }
0449 
0450 void StyleStorage::load(const QList<QPair<QRegion, Style> >& styles)
0451 {
0452     Q_ASSERT(!d->loader);
0453     d->loader = new StyleStorageLoaderJob(this, styles);
0454 }
0455 
0456 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::insertRows(int position, int number)
0457 {
0458     d->ensureLoaded();
0459     const QRect invalidRect(1, position, KS_colMax, KS_rowMax);
0460     // invalidate the affected, cached styles
0461     invalidateCache(invalidRect);
0462     // update the used area
0463     const QRegion usedArea = d->usedArea & invalidRect;
0464     d->usedArea -= invalidRect;
0465     d->usedArea += usedArea.translated(0, number);
0466     const QVector<QRect> rects = (d->usedArea & QRect(1, position - 1, KS_colMax, 1)).rects();
0467     for (int i = 0; i < rects.count(); ++i)
0468         d->usedArea += rects[i].adjusted(0, 1, 0, number + 1);
0469     // update the used rows
0470     QMap<int, bool> map;
0471     QMap<int, bool>::iterator begin = d->usedRows.lowerBound(position);
0472     QMap<int, bool>::iterator end = d->usedRows.end();
0473     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
0474         if (it.key() + number <= KS_rowMax)
0475             map.insert(it.key() + number, true);
0476     }
0477     for (QMap<int, bool>::iterator it = begin; it != d->usedRows.end(); )
0478         it = d->usedRows.erase(it);
0479     d->usedRows.unite(map);
0480     // process the tree
0481     QList< QPair<QRectF, SharedSubStyle> > undoData;
0482     undoData << qMakePair(QRectF(1, KS_rowMax - number + 1, KS_colMax, number), SharedSubStyle());
0483     undoData << d->tree.insertRows(position, number);
0484     return undoData;
0485 }
0486 
0487 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::insertColumns(int position, int number)
0488 {
0489     d->ensureLoaded();
0490     const QRect invalidRect(position, 1, KS_colMax, KS_rowMax);
0491     // invalidate the affected, cached styles
0492     invalidateCache(invalidRect);
0493     // update the used area
0494     const QRegion usedArea = d->usedArea & invalidRect;
0495     d->usedArea -= invalidRect;
0496     d->usedArea += usedArea.translated(number, 0);
0497     const QVector<QRect> rects = (d->usedArea & QRect(position - 1, 0, 1, KS_rowMax)).rects();
0498     for (int i = 0; i < rects.count(); ++i)
0499         d->usedArea += rects[i].adjusted(1, 0, number + 1, 0);
0500     // update the used columns
0501     QMap<int, bool> map;
0502     QMap<int, bool>::iterator begin = d->usedColumns.upperBound(position);
0503     QMap<int, bool>::iterator end = d->usedColumns.end();
0504     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
0505         if (it.key() + number <= KS_colMax)
0506             map.insert(it.key() + number, true);
0507     }
0508     for (QMap<int, bool>::iterator it = begin; it != d->usedColumns.end(); )
0509         it = d->usedColumns.erase(it);
0510     d->usedColumns.unite(map);
0511     // process the tree
0512     QList< QPair<QRectF, SharedSubStyle> > undoData;
0513     undoData << qMakePair(QRectF(KS_colMax - number + 1, 1, number, KS_rowMax), SharedSubStyle());
0514     undoData << d->tree.insertColumns(position, number);
0515     return undoData;
0516 }
0517 
0518 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::removeRows(int position, int number)
0519 {
0520     d->ensureLoaded();
0521     const QRect invalidRect(1, position, KS_colMax, KS_rowMax);
0522     // invalidate the affected, cached styles
0523     invalidateCache(invalidRect);
0524     // update the used area
0525     const QRegion usedArea = d->usedArea & QRect(1, position + number, KS_colMax, KS_rowMax);
0526     d->usedArea -= invalidRect;
0527     d->usedArea += usedArea.translated(0, -number);
0528     // update the used rows
0529     QMap<int, bool> map;
0530     QMap<int, bool>::iterator begin = d->usedRows.upperBound(position);
0531     QMap<int, bool>::iterator end = d->usedRows.end();
0532     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
0533         if (it.key() - number >= position)
0534             map.insert(it.key() - number, true);
0535     }
0536     for (QMap<int, bool>::iterator it = begin; it != d->usedRows.end(); )
0537         it = d->usedRows.erase(it);
0538     d->usedRows.unite(map);
0539     // process the tree
0540     QList< QPair<QRectF, SharedSubStyle> > undoData;
0541     undoData << qMakePair(QRectF(1, position, KS_colMax, number), SharedSubStyle());
0542     undoData << d->tree.removeRows(position, number);
0543     return undoData;
0544 }
0545 
0546 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::removeColumns(int position, int number)
0547 {
0548     d->ensureLoaded();
0549     const QRect invalidRect(position, 1, KS_colMax, KS_rowMax);
0550     // invalidate the affected, cached styles
0551     invalidateCache(invalidRect);
0552     // update the used area
0553     const QRegion usedArea = d->usedArea & QRect(position + number, 1, KS_colMax, KS_rowMax);
0554     d->usedArea -= invalidRect;
0555     d->usedArea += usedArea.translated(-number, 0);
0556     // update the used columns
0557     QMap<int, bool> map;
0558     QMap<int, bool>::iterator begin = d->usedColumns.upperBound(position);
0559     QMap<int, bool>::iterator end = d->usedColumns.end();
0560     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
0561         if (it.key() - number >= position)
0562             map.insert(it.key() - number, true);
0563     }
0564     for (QMap<int, bool>::iterator it = begin; it != d->usedColumns.end(); )
0565         it = d->usedColumns.erase(it);
0566     d->usedColumns.unite(map);
0567     // process the tree
0568     QList< QPair<QRectF, SharedSubStyle> > undoData;
0569     undoData << qMakePair(QRectF(position, 1, number, KS_rowMax), SharedSubStyle());
0570     undoData << d->tree.removeColumns(position, number);
0571     return undoData;
0572 }
0573 
0574 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::insertShiftRight(const QRect& rect)
0575 {
0576     d->ensureLoaded();
0577     const QRect invalidRect(rect.topLeft(), QPoint(KS_colMax, rect.bottom()));
0578     QList< QPair<QRectF, SharedSubStyle> > undoData;
0579     undoData << qMakePair(QRectF(rect), SharedSubStyle());
0580     undoData << d->tree.insertShiftRight(rect);
0581     regionChanged(invalidRect);
0582     // update the used area
0583     const QRegion usedArea = d->usedArea & invalidRect;
0584     d->usedArea -= invalidRect;
0585     d->usedArea += usedArea.translated(rect.width(), 0);
0586     const QVector<QRect> rects = (d->usedArea & QRect(rect.left() - 1, rect.top(), 1, rect.height())).rects();
0587     for (int i = 0; i < rects.count(); ++i)
0588         d->usedArea += rects[i].adjusted(1, 0, rect.width() + 1, 0);
0589     // update the used columns
0590     QMap<int, bool>::iterator begin = d->usedColumns.upperBound(rect.left());
0591     QMap<int, bool>::iterator end = d->usedColumns.end();
0592     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
0593         if (it.key() + rect.width() <= KS_colMax)
0594             d->usedArea += QRect(it.key() + rect.width(), rect.top(), rect.width(), rect.height());
0595     }
0596     if (d->usedColumns.contains(rect.left() - 1))
0597         d->usedArea += rect;
0598     return undoData;
0599 }
0600 
0601 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::insertShiftDown(const QRect& rect)
0602 {
0603     d->ensureLoaded();
0604     const QRect invalidRect(rect.topLeft(), QPoint(rect.right(), KS_rowMax));
0605     QList< QPair<QRectF, SharedSubStyle> > undoData;
0606     undoData << qMakePair(QRectF(rect), SharedSubStyle());
0607     undoData << d->tree.insertShiftDown(rect);
0608     regionChanged(invalidRect);
0609     // update the used area
0610     const QRegion usedArea = d->usedArea & invalidRect;
0611     d->usedArea -= invalidRect;
0612     d->usedArea += usedArea.translated(0, rect.height());
0613     const QVector<QRect> rects = (d->usedArea & QRect(rect.left(), rect.top() - 1, rect.width(), 1)).rects();
0614     for (int i = 0; i < rects.count(); ++i)
0615         d->usedArea += rects[i].adjusted(0, 1, 0, rect.height() + 1);
0616     // update the used rows
0617     QMap<int, bool>::iterator begin = d->usedRows.upperBound(rect.top());
0618     QMap<int, bool>::iterator end = d->usedRows.end();
0619     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
0620         if (it.key() + rect.height() <= KS_rowMax)
0621             d->usedArea += QRect(rect.left(), it.key() + rect.height(), rect.width(), rect.height());
0622     }
0623     if (d->usedRows.contains(rect.top() - 1))
0624         d->usedArea += rect;
0625     return undoData;
0626 }
0627 
0628 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::removeShiftLeft(const QRect& rect)
0629 {
0630     d->ensureLoaded();
0631     const QRect invalidRect(rect.topLeft(), QPoint(KS_colMax, rect.bottom()));
0632     QList< QPair<QRectF, SharedSubStyle> > undoData;
0633     undoData << qMakePair(QRectF(rect), SharedSubStyle());
0634     undoData << d->tree.removeShiftLeft(rect);
0635     regionChanged(invalidRect);
0636     // update the used area
0637     const QRegion usedArea = d->usedArea & QRect(rect.right() + 1, rect.top(), KS_colMax, rect.height());
0638     d->usedArea -= invalidRect;
0639     d->usedArea += usedArea.translated(-rect.width(), 0);
0640     // update the used columns
0641     QMap<int, bool>::iterator begin = d->usedColumns.upperBound(rect.right() + 1);
0642     QMap<int, bool>::iterator end = d->usedColumns.end();
0643     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
0644         if (it.key() - rect.width() >= rect.left())
0645             d->usedArea += QRect(it.key() - rect.width(), rect.top(), rect.width(), rect.height());
0646     }
0647     return undoData;
0648 }
0649 
0650 QList< QPair<QRectF, SharedSubStyle> > StyleStorage::removeShiftUp(const QRect& rect)
0651 {
0652     d->ensureLoaded();
0653     const QRect invalidRect(rect.topLeft(), QPoint(rect.right(), KS_rowMax));
0654     QList< QPair<QRectF, SharedSubStyle> > undoData;
0655     undoData << qMakePair(QRectF(rect), SharedSubStyle());
0656     undoData << d->tree.removeShiftUp(rect);
0657     regionChanged(invalidRect);
0658     // update the used area
0659     const QRegion usedArea = d->usedArea & QRect(rect.left(), rect.bottom() + 1, rect.width(), KS_rowMax);
0660     d->usedArea -= invalidRect;
0661     d->usedArea += usedArea.translated(0, -rect.height());
0662     // update the used rows
0663     QMap<int, bool>::iterator begin = d->usedRows.upperBound(rect.bottom() + 1);
0664     QMap<int, bool>::iterator end = d->usedRows.end();
0665     for (QMap<int, bool>::iterator it = begin; it != end; ++it) {
0666         if (it.key() - rect.height() >= rect.top())
0667             d->usedArea += QRect(rect.left(), it.key() - rect.height(), rect.width(), rect.height());
0668     }
0669     return undoData;
0670 }
0671 
0672 void StyleStorage::invalidateCache()
0673 {
0674     // still busy loading? no cache to invalidate
0675     if (d->loader && !d->loader->isFinished())
0676         return;
0677 
0678 #ifdef CALLIGRA_SHEETS_MT
0679     QMutexLocker ml(&d->cacheMutex);
0680 #endif
0681     d->cache.clear();
0682     d->cachedArea = QRegion();
0683 }
0684 
0685 void StyleStorage::garbageCollection()
0686 {
0687     // still busy loading? no garbage to collect
0688     if (d->loader && !d->loader->isFinished())
0689         return;
0690 
0691     // any possible garbage left?
0692     if (d->possibleGarbage.isEmpty())
0693         return;
0694 
0695     const int currentZIndex = d->possibleGarbage.constBegin().key();
0696     const QPair<QRectF, SharedSubStyle> currentPair = d->possibleGarbage.take(currentZIndex);
0697 
0698     // check whether the named style still exists
0699     if (currentPair.second->type() == Style::NamedStyleKey &&
0700             !styleManager()->style(static_cast<const NamedStyle*>(currentPair.second.data())->name)) {
0701         debugSheetsStyle << "removing" << currentPair.second->debugData()
0702         << "at" << Region(currentPair.first.toRect()).name()
0703         << "used" << currentPair.second->ref << "times" << endl;
0704         d->tree.remove(currentPair.first.toRect(), currentPair.second);
0705         d->subStyles[currentPair.second->type()].removeAll(currentPair.second);
0706         QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
0707         return; // already done
0708     }
0709 
0710     typedef QPair<QRectF, SharedSubStyle> SharedSubStylePair;
0711     QMap<int, SharedSubStylePair> pairs = d->tree.intersectingPairs(currentPair.first.toRect());
0712     if (pairs.isEmpty())   // actually never true, just for sanity
0713         return;
0714     int zIndex = pairs.constBegin().key();
0715     SharedSubStylePair pair = pairs[zIndex];
0716 
0717     // check whether the default style is placed first
0718     if (zIndex == currentZIndex &&
0719             currentPair.second->type() == Style::DefaultStyleKey &&
0720             pair.second->type() == Style::DefaultStyleKey &&
0721             pair.first == currentPair.first) {
0722         debugSheetsStyle << "removing default style"
0723         << "at" << Region(currentPair.first.toRect()).name()
0724         << "used" << currentPair.second->ref << "times" << endl;
0725         d->tree.remove(currentPair.first.toRect(), currentPair.second);
0726         QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
0727         return; // already done
0728     }
0729 
0730     // special handling for indentation:
0731     // check whether the default indentation is placed first
0732     if (zIndex == currentZIndex &&
0733             currentPair.second->type() == Style::Indentation &&
0734             static_cast<const SubStyleOne<Style::Indentation, int>*>(currentPair.second.data())->value1 == 0 &&
0735             pair.first == currentPair.first) {
0736         debugSheetsStyle << "removing default indentation"
0737         << "at" << Region(currentPair.first.toRect()).name()
0738         << "used" << currentPair.second->ref << "times" << endl;
0739         d->tree.remove(currentPair.first.toRect(), currentPair.second);
0740         QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
0741         return; // already done
0742     }
0743 
0744     // special handling for precision:
0745     // check whether the storage default precision is placed first
0746     if (zIndex == currentZIndex &&
0747             currentPair.second->type() == Style::Precision &&
0748             static_cast<const SubStyleOne<Style::Precision, int>*>(currentPair.second.data())->value1 == 0 &&
0749             pair.first == currentPair.first) {
0750         debugSheetsStyle << "removing default precision"
0751         << "at" << Region(currentPair.first.toRect()).name()
0752         << "used" << currentPair.second->ref << "times" << endl;
0753         d->tree.remove(currentPair.first.toRect(), currentPair.second);
0754         QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
0755         return; // already done
0756     }
0757 
0758     // check, if the current substyle is covered by others added after it
0759     bool found = false;
0760     QMap<int, SharedSubStylePair>::ConstIterator end = pairs.constEnd();
0761     for (QMap<int, SharedSubStylePair>::ConstIterator it = pairs.constFind(currentZIndex); it != end; ++it) {
0762         zIndex = it.key();
0763         pair = it.value();
0764 
0765         // as long as the substyle in question was not found, skip the substyle
0766         if (!found) {
0767             if (pair.first == currentPair.first &&
0768                     Style::compare(pair.second.data(), currentPair.second.data()) &&
0769                     zIndex == currentZIndex) {
0770                 found = true;
0771             }
0772             continue;
0773         }
0774 
0775         // remove the current pair, if another substyle of the same type,
0776         // the default style or a named style follows and the rectangle
0777         // is completely covered
0778         if (zIndex != currentZIndex &&
0779                 (pair.second->type() == currentPair.second->type() ||
0780                  pair.second->type() == Style::DefaultStyleKey ||
0781                  pair.second->type() == Style::NamedStyleKey) &&
0782                 pair.first.toRect().contains(currentPair.first.toRect())) {
0783             // special handling for indentation
0784             // only remove, if covered by default
0785             if (pair.second->type() == Style::Indentation &&
0786                     static_cast<const SubStyleOne<Style::Indentation, int>*>(pair.second.data())->value1 != 0) {
0787                 continue;
0788             }
0789 
0790             // special handling for precision
0791             // only remove, if covered by default
0792             if (pair.second->type() == Style::Precision &&
0793                     static_cast<const SubStyleOne<Style::Precision, int>*>(pair.second.data())->value1 != 0) {
0794                 continue;
0795             }
0796 
0797             debugSheetsStyle << "removing" << currentPair.second->debugData()
0798             << "at" << Region(currentPair.first.toRect()).name()
0799             << "used" << currentPair.second->ref << "times" << endl;
0800             d->tree.remove(currentPair.first.toRect(), currentPair.second, currentZIndex);
0801 #if 0
0802             debugSheetsStyle << "StyleStorage: usage of" << currentPair.second->debugData() << " is" << currentPair.second->ref;
0803             // FIXME Stefan: The usage of substyles used once should be
0804             //               two (?) here, not more. Why is this not the case?
0805             //               The shared pointers are used by:
0806             //               a) the tree
0807             //               b) the reusage list (where it should be removed)
0808             //               c) the cached styles (!)
0809             //               d) the undo data of operations (!)
0810             if (currentPair.second->ref == 2) {
0811                 debugSheetsStyle << "StyleStorage: removing" << currentPair.second << " from the used subStyles";
0812                 d->subStyles[currentPair.second->type()].removeAll(currentPair.second);
0813             }
0814 #endif
0815             break;
0816         }
0817     }
0818     QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
0819 }
0820 
0821 void StyleStorage::regionChanged(const QRect& rect)
0822 {
0823     // still busy loading? no garbage to collect
0824     if (d->loader && !d->loader->isFinished())
0825         return;
0826     if (d->map->isLoading())
0827         return;
0828     // mark the possible garbage
0829     // NOTE Stefan: The map may contain multiple indices. The already existing possible garbage has
0830     // has to be inserted most recently, because it should be accessed first.
0831     d->possibleGarbage = d->tree.intersectingPairs(rect).unite(d->possibleGarbage);
0832     QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection()));
0833     // invalidate cache
0834     invalidateCache(rect);
0835 }
0836 
0837 void StyleStorage::invalidateCache(const QRect& rect)
0838 {
0839     // still busy loading? no cache to invalidate
0840     if (d->loader && !d->loader->isFinished())
0841         return;
0842 
0843 #ifdef CALLIGRA_SHEETS_MT
0844     QMutexLocker ml(&d->cacheMutex);
0845 #endif
0846 //     debugSheetsStyle <<"StyleStorage: Invalidating" << rect;
0847     const QRegion region = d->cachedArea.intersected(rect);
0848     d->cachedArea = d->cachedArea.subtracted(rect);
0849     foreach(const QRect& rect, region.rects()) {
0850         for (int col = rect.left(); col <= rect.right(); ++col) {
0851             for (int row = rect.top(); row <= rect.bottom(); ++row) {
0852 //                 debugSheetsStyle <<"StyleStorage: Removing cached style for" << Cell::name( col, row );
0853                 d->cache.remove(QPoint(col, row));     // also deletes it
0854             }
0855         }
0856     }
0857 }
0858 
0859 Style StyleStorage::composeStyle(const QList<SharedSubStyle>& subStyles) const
0860 {
0861     d->ensureLoaded();
0862 
0863     if (subStyles.isEmpty()) {
0864 //         debugSheetsStyle <<"StyleStorage:" << "nothing to merge, return the default style";
0865         return *styleManager()->defaultStyle();
0866     }
0867     // From OpenDocument-v1.2-os-part1 16.2<style:style>
0868     //
0869     // The <style:style> element represents styles.
0870     //
0871     // Styles defined by the <style:style> element use a hierarchical style model.
0872     // The <style:style> element supports inheritance of formatting properties by a style from its parent style.
0873     // A parent style is specified by the style:parent-style-name attribute on a <style:style> element.
0874     //
0875     // The determination of the value of a formatting property begins with any style that is specified by an element.
0876     // If the formatting property is present in that style, its value is used.
0877     //
0878     // If that style does not specify a value for that formatting property and it has a parent style,
0879     // the value of the formatting element is taken from the parent style, if present.
0880     //
0881     // If the parent style does not have a value for the formatting property, the search for the formatting property value continues up parent styles
0882     // until either the formatting property has been found or a style is found with no parent style.
0883     //
0884     // If a search of the parent styles of a style does not result in a value for a formatting property,
0885     // the determination of its value depends on the style family and the element to which a style is applied.
0886 
0887     // TODO review loading of libreOffice generated files:
0888     // It seems libreOffice saves parent also when parent is the Default style.
0889     // Sheets do not do this, it is handled implicitly.
0890     // According to the spec, both ways should be ok,
0891     // but the result is that when loading lo files, it may exist multiple (two) NamedStyleKey substyles:
0892     // One loaded explicitly (first in the list), and one generated by our loading code (later in the list).
0893     // We use the last one in the list here, this should be our generated one.
0894     CustomStyle *namedStyle = 0;
0895     for (int i = subStyles.count() - 1; i >= 0; --i) {
0896         if (subStyles[i]->type() == Style::NamedStyleKey) {
0897             namedStyle = styleManager()->style(static_cast<const NamedStyle*>(subStyles[i].data())->name);
0898             if (namedStyle) {
0899                 debugSheetsStyle<<"Compose found namedstyle:"<<static_cast<const NamedStyle*>(subStyles[i].data())->name<<namedStyle->parentName();namedStyle->dump();
0900                 break;
0901             }
0902         }
0903     }
0904 
0905     Style style;
0906     // get attributes from parent styles
0907     if (namedStyle) {
0908         // first, load the attributes of the parent style(s)
0909         QList<CustomStyle*> parentStyles;
0910         CustomStyle *parentStyle = styleManager()->style(namedStyle->parentName());
0911         // debugSheetsStyle <<"StyleStorage:" << namedStyle->name() <<"'s parent =" << namedStyle->parentName();
0912         while (parentStyle) {
0913             // debugSheetsStyle <<"StyleStorage:" << parentStyle->name() <<"'s parent =" << parentStyle->parentName();
0914             parentStyles.prepend(parentStyle);
0915             parentStyle = styleManager()->style(parentStyle->parentName());
0916         }
0917         Style tmpStyle;
0918         for (int i = 0; i < parentStyles.count(); ++i) {
0919             // debugSheetsStyle <<"StyleStorage: merging" << parentStyles[i]->name() <<" in.";
0920             tmpStyle = *parentStyles[i];
0921             tmpStyle.merge(style); // insert/replace substyles in tmpStyle with substyles from style
0922             style = tmpStyle;
0923         }
0924         // second, merge the other attributes in
0925         // debugSheetsStyle <<"StyleStorage: merging" << namedStyle->name() <<" in.";
0926         tmpStyle = *namedStyle;
0927         tmpStyle.merge(style); // insert/replace substyles in tmpStyle with substyles from style
0928         style = tmpStyle;
0929         // not the default anymore
0930         style.clearAttribute(Style::DefaultStyleKey);
0931         // reset the parent name
0932         style.setParentName(namedStyle->name());
0933         //                 debugSheetsStyle <<"StyleStorage: merging done";
0934     }
0935     for (int i = 0; i < subStyles.count(); ++i) {
0936         if (subStyles[i]->type() == Style::DefaultStyleKey) {
0937             // skip
0938         } else if (subStyles[i]->type() == Style::NamedStyleKey) {
0939             // treated above
0940         } else if (subStyles[i]->type() == Style::Indentation) {
0941             // special handling for indentation
0942             const int indentation = static_cast<const SubStyleOne<Style::Indentation, int>*>(subStyles[i].data())->value1;
0943             if (indentation == 0 || (style.indentation() + indentation <= 0))
0944                 style.clearAttribute(Style::Indentation);   // reset
0945             else
0946                 style.setIndentation(style.indentation() + indentation);   // increase/decrease
0947         } else if (subStyles[i]->type() == Style::Precision) {
0948             // special handling for precision
0949             // The Style default (-1) and the storage default (0) differ.
0950             const int precision = static_cast<const SubStyleOne<Style::Precision, int>*>(subStyles[i].data())->value1;
0951             if (precision == 0)   // storage default
0952                 style.clearAttribute(Style::Precision);   // reset
0953             else {
0954                 if (style.precision() == -1)   // Style default
0955                     style.setPrecision(qMax(0, precision));     // positive initial value
0956                 else if (style.precision() + precision <= 0)
0957                     style.setPrecision(0);
0958                 else if (style.precision() + precision >= 10)
0959                     style.setPrecision(10);
0960                 else
0961                     style.setPrecision(style.precision() + precision);   // increase/decrease
0962             }
0963         } else {
0964             // insert the substyle
0965 //             debugSheetsStyle <<"StyleStorage: inserting" << subStyles[i]->debugData();
0966             style.insertSubStyle(subStyles[i]);
0967             // not the default anymore
0968             style.clearAttribute(Style::DefaultStyleKey);
0969         }
0970     }
0971     // Implicitly merge in any missing attributes from the family (table-cell) default style
0972     // It might have been merged in via parent styles above, but we cannot rely on that.
0973     if (!styleManager()->defaultStyle()->isEmpty()) {
0974         // debugSheetsStyle << "StyleStorage: merging family default in";
0975         Style tmpStyle = *styleManager()->defaultStyle();
0976         tmpStyle.clearAttribute(Style::DefaultStyleKey);
0977         tmpStyle.merge(style); // insert/replace substyles in tmpStyle with substyles from style
0978         style = tmpStyle;
0979     }
0980     return style;
0981 }
0982 
0983 StyleManager* StyleStorage::styleManager() const
0984 {
0985     return d->map->styleManager();
0986 }