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 }