File indexing completed on 2024-04-28 16:21:26
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 #ifndef CALLIGRA_SHEETS_RECT_STORAGE 0022 #define CALLIGRA_SHEETS_RECT_STORAGE 0023 0024 #include <QCache> 0025 #include <QRegion> 0026 #include <QTimer> 0027 #include <QRunnable> 0028 #include <QTime> 0029 #ifdef CALLIGRA_SHEETS_MT 0030 #include <QMutex> 0031 #include <QMutexLocker> 0032 #endif 0033 0034 #include "sheets_odf_export.h" 0035 0036 #include "Map.h" 0037 #include "Region.h" 0038 #include "RTree.h" 0039 0040 static const int g_garbageCollectionTimeOut = 100; 0041 0042 namespace Calligra 0043 { 0044 namespace Sheets 0045 { 0046 0047 template<typename T> 0048 class RectStorageLoader; 0049 0050 /** 0051 * \ingroup Storage 0052 * A custom rectangular storage. 0053 * Based on an R-Tree data structure. 0054 * Usable for any kind of data attached to rectangular regions. 0055 * 0056 * Acts mainly as a wrapper around the R-Tree data structure to allow a future 0057 * replacement of this backend. Decorated with some additional features like 0058 * garbage collection, caching, used area tracking, etc. 0059 * 0060 * \author Stefan Nikolaus <stefan.nikolaus@kdemail.net> 0061 * 0062 * \note For data assigned to points use PointStorage. 0063 */ 0064 template<typename T> 0065 class RectStorage 0066 { 0067 public: 0068 explicit RectStorage(Map* map); 0069 RectStorage(const RectStorage& other); 0070 virtual ~RectStorage(); 0071 0072 /** 0073 * \return the stored value at the position \p point . 0074 */ 0075 T contains(const QPoint& point) const; 0076 0077 /** 0078 * \return the stored rect/value pair at the position \p point . 0079 */ 0080 QPair<QRectF, T> containedPair(const QPoint& point) const; 0081 0082 QList< QPair<QRectF, T> > intersectingPairs(const Region& region) const; 0083 0084 QList< QPair<QRectF, T> > undoData(const Region& region) const; 0085 0086 /** 0087 * Returns the area, which got data attached. 0088 * \return the area using data 0089 */ 0090 QRect usedArea() const; 0091 0092 /** 0093 * Mass loading of data, removes any existing data first 0094 */ 0095 void load(const QList<QPair<QRegion, T> >& data); 0096 0097 /** 0098 * Assigns \p data to \p region . 0099 */ 0100 void insert(const Region& region, const T& data); 0101 0102 /** 0103 * Removes \p data from \p region . 0104 */ 0105 void remove(const Region& region, const T& data); 0106 0107 /** 0108 * Inserts \p number rows at the position \p position . 0109 * It extends or shifts rectangles, respectively. 0110 */ 0111 QList< QPair<QRectF, T> > insertRows(int position, int number); 0112 0113 /** 0114 * Inserts \p number columns at the position \p position . 0115 * It extends or shifts rectangles, respectively. 0116 */ 0117 QList< QPair<QRectF, T> > insertColumns(int position, int number); 0118 0119 /** 0120 * Deletes \p number rows at the position \p position . 0121 * It shrinks or shifts rectangles, respectively. 0122 */ 0123 QList< QPair<QRectF, T> > removeRows(int position, int number); 0124 0125 /** 0126 * Deletes \p number columns at the position \p position . 0127 * It shrinks or shifts rectangles, respectively. 0128 */ 0129 QList< QPair<QRectF, T> > removeColumns(int position, int number); 0130 0131 /** 0132 * Shifts the rows right of \p rect to the right by the width of \p rect . 0133 * It extends or shifts rectangles, respectively. 0134 */ 0135 QList< QPair<QRectF, T> > insertShiftRight(const QRect& rect); 0136 0137 /** 0138 * Shifts the columns at the bottom of \p rect to the bottom by the height of \p rect . 0139 * It extends or shifts rectangles, respectively. 0140 */ 0141 QList< QPair<QRectF, T> > insertShiftDown(const QRect& rect); 0142 0143 /** 0144 * Shifts the rows left of \p rect to the left by the width of \p rect . 0145 * It shrinks or shifts rectangles, respectively. 0146 * \return the former rectangle/data pairs 0147 */ 0148 QList< QPair<QRectF, T> > removeShiftLeft(const QRect& rect); 0149 0150 /** 0151 * Shifts the columns on top of \p rect to the top by the height of \p rect . 0152 * It shrinks or shifts rectangles, respectively. 0153 * \return the former rectangle/data pairs 0154 */ 0155 QList< QPair<QRectF, T> > removeShiftUp(const QRect& rect); 0156 0157 protected: 0158 virtual void triggerGarbageCollection(); 0159 virtual void garbageCollection(); 0160 0161 /** 0162 * Triggers all necessary actions after a change of \p rect . 0163 * Calls invalidateCache() and adds the data in 0164 * \p rect to the list of possible garbage. 0165 */ 0166 void regionChanged(const QRect& rect); 0167 0168 /** 0169 * Invalidates all cached styles lying in \p rect . 0170 */ 0171 void invalidateCache(const QRect& rect); 0172 0173 /** 0174 * Ensures that any load() operation has completed. 0175 */ 0176 void ensureLoaded() const; 0177 private: 0178 Map* m_map; 0179 RTree<T> m_tree; 0180 QRegion m_usedArea; 0181 QMap<int, QPair<QRectF, T> > m_possibleGarbage; 0182 QList<T> m_storedData; 0183 mutable QCache<QPoint, T> m_cache; 0184 #ifdef CALLIGRA_SHEETS_MT 0185 mutable QMutex m_mutex; 0186 #endif 0187 mutable QRegion m_cachedArea; 0188 0189 RectStorageLoader<T>* m_loader; 0190 friend class RectStorageLoader<T>; 0191 }; 0192 0193 template<typename T> 0194 class RectStorageLoader : public QRunnable 0195 { 0196 public: 0197 RectStorageLoader(RectStorage<T>* storage, const QList<QPair<QRegion, T> >& data); 0198 void run() override; 0199 void waitForFinished(); 0200 bool isFinished() const; 0201 QList<QPair<QRegion, T> > data() const; 0202 private: 0203 RectStorage<T>* m_storage; 0204 QList<QPair<QRegion, T> > m_data; 0205 }; 0206 0207 template<typename T> 0208 RectStorage<T>::RectStorage(Map* map) 0209 : m_map(map), m_loader(0) 0210 { 0211 } 0212 0213 template<typename T> 0214 RectStorage<T>::RectStorage(const RectStorage& other) 0215 : m_map(other.m_map) 0216 , m_usedArea(other.m_usedArea) 0217 , m_storedData(other.m_storedData) 0218 , m_loader(0) 0219 { 0220 m_tree = other.m_tree; 0221 if (other.m_loader) { 0222 m_loader = new RectStorageLoader<T>(this, other.m_loader->data()); 0223 } 0224 } 0225 0226 template<typename T> 0227 RectStorage<T>::~RectStorage() 0228 { 0229 delete m_loader; // needs fixing if this ever gets to be multithreaded 0230 } 0231 0232 template<typename T> 0233 T RectStorage<T>::contains(const QPoint& point) const 0234 { 0235 ensureLoaded(); 0236 #ifdef CALLIGRA_SHEETS_MT 0237 QMutexLocker ml(&m_mutex); 0238 #endif 0239 if (!usedArea().contains(point)) 0240 return T(); 0241 // first, lookup point in the cache 0242 if (m_cache.contains(point)) { 0243 return *m_cache.object(point); 0244 } 0245 // not found, lookup in the tree 0246 QList<T> results = m_tree.contains(point); 0247 T data = results.isEmpty() ? T() : results.last(); 0248 // insert style into the cache 0249 m_cache.insert(point, new T(data)); 0250 m_cachedArea += QRect(point, point); 0251 return data; 0252 } 0253 0254 template<typename T> 0255 QPair<QRectF, T> RectStorage<T>::containedPair(const QPoint& point) const 0256 { 0257 ensureLoaded(); 0258 const QList< QPair<QRectF, T> > results = m_tree.intersectingPairs(QRect(point, point)).values(); 0259 return results.isEmpty() ? qMakePair(QRectF(), T()) : results.last(); 0260 } 0261 0262 template<typename T> 0263 QList< QPair<QRectF, T> > RectStorage<T>::intersectingPairs(const Region& region) const 0264 { 0265 ensureLoaded(); 0266 QList< QPair<QRectF, T> > result; 0267 Region::ConstIterator end = region.constEnd(); 0268 for (Region::ConstIterator it = region.constBegin(); it != end; ++it) 0269 result += m_tree.intersectingPairs((*it)->rect()).values(); 0270 return result; 0271 } 0272 0273 template<typename T> 0274 QList< QPair<QRectF, T> > RectStorage<T>::undoData(const Region& region) const 0275 { 0276 ensureLoaded(); 0277 QList< QPair<QRectF, T> > 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, T> > pairs = m_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 add a default value even if there are no pairs. 0287 result << qMakePair(QRectF(rect), T()) << pairs; 0288 } 0289 return result; 0290 } 0291 0292 template<typename T> 0293 QRect RectStorage<T>::usedArea() const 0294 { 0295 ensureLoaded(); 0296 return m_tree.boundingBox().toRect(); 0297 } 0298 0299 template<typename T> 0300 void RectStorage<T>::load(const QList<QPair<QRegion, T> >& data) 0301 { 0302 Q_ASSERT(!m_loader); 0303 m_loader = new RectStorageLoader<T>(this, data); 0304 } 0305 0306 template<typename T> 0307 void RectStorage<T>::insert(const Region& region, const T& _data) 0308 { 0309 ensureLoaded(); 0310 T data; 0311 // lookup already used data 0312 int index = m_storedData.indexOf(_data); 0313 if (index != -1) 0314 data = m_storedData[index]; 0315 else { 0316 data = _data; 0317 m_storedData.append(_data); 0318 } 0319 0320 Region::ConstIterator end(region.constEnd()); 0321 for (Region::ConstIterator it(region.constBegin()); it != end; ++it) { 0322 // insert data 0323 m_tree.insert((*it)->rect(), data); 0324 regionChanged((*it)->rect()); 0325 } 0326 } 0327 0328 template<typename T> 0329 void RectStorage<T>::remove(const Region& region, const T& data) 0330 { 0331 ensureLoaded(); 0332 if (!m_storedData.contains(data)) { 0333 return; 0334 } 0335 const Region::ConstIterator end(region.constEnd()); 0336 for (Region::ConstIterator it(region.constBegin()); it != end; ++it) { 0337 // remove data 0338 m_tree.remove((*it)->rect(), data); 0339 regionChanged((*it)->rect()); 0340 } 0341 } 0342 0343 template<typename T> 0344 QList< QPair<QRectF, T> > RectStorage<T>::insertRows(int position, int number) 0345 { 0346 ensureLoaded(); 0347 const QRect invalidRect(1, position, KS_colMax, KS_rowMax); 0348 // invalidate the affected, cached styles 0349 invalidateCache(invalidRect); 0350 // process the tree 0351 QList< QPair<QRectF, T> > undoData; 0352 undoData << qMakePair(QRectF(1, KS_rowMax - number + 1, KS_colMax, number), T()); 0353 undoData << m_tree.insertRows(position, number, RTree<T>::CopyCurrent); 0354 return undoData; 0355 } 0356 0357 template<typename T> 0358 QList< QPair<QRectF, T> > RectStorage<T>::insertColumns(int position, int number) 0359 { 0360 ensureLoaded(); 0361 const QRect invalidRect(position, 1, KS_colMax, KS_rowMax); 0362 // invalidate the affected, cached styles 0363 invalidateCache(invalidRect); 0364 // process the tree 0365 QList< QPair<QRectF, T> > undoData; 0366 undoData << qMakePair(QRectF(KS_colMax - number + 1, 1, number, KS_rowMax), T()); 0367 undoData << m_tree.insertColumns(position, number, RTree<T>::CopyCurrent); 0368 return undoData; 0369 } 0370 0371 template<typename T> 0372 QList< QPair<QRectF, T> > RectStorage<T>::removeRows(int position, int number) 0373 { 0374 ensureLoaded(); 0375 const QRect invalidRect(1, position, KS_colMax, KS_rowMax); 0376 // invalidate the affected, cached styles 0377 invalidateCache(invalidRect); 0378 // process the tree 0379 QList< QPair<QRectF, T> > undoData; 0380 undoData << qMakePair(QRectF(1, position, KS_colMax, number), T()); 0381 undoData << m_tree.removeRows(position, number); 0382 return undoData; 0383 } 0384 0385 template<typename T> 0386 QList< QPair<QRectF, T> > RectStorage<T>::removeColumns(int position, int number) 0387 { 0388 ensureLoaded(); 0389 const QRect invalidRect(position, 1, KS_colMax, KS_rowMax); 0390 // invalidate the affected, cached styles 0391 invalidateCache(invalidRect); 0392 // process the tree 0393 QList< QPair<QRectF, T> > undoData; 0394 undoData << qMakePair(QRectF(position, 1, number, KS_rowMax), T()); 0395 undoData << m_tree.removeColumns(position, number); 0396 return undoData; 0397 } 0398 0399 template<typename T> 0400 QList< QPair<QRectF, T> > RectStorage<T>::insertShiftRight(const QRect& rect) 0401 { 0402 ensureLoaded(); 0403 const QRect invalidRect(rect.topLeft(), QPoint(KS_colMax, rect.bottom())); 0404 QList< QPair<QRectF, T> > undoData; 0405 undoData << qMakePair(QRectF(rect), T()); 0406 undoData << m_tree.insertShiftRight(rect); 0407 regionChanged(invalidRect); 0408 return undoData; 0409 } 0410 0411 template<typename T> 0412 QList< QPair<QRectF, T> > RectStorage<T>::insertShiftDown(const QRect& rect) 0413 { 0414 ensureLoaded(); 0415 const QRect invalidRect(rect.topLeft(), QPoint(rect.right(), KS_rowMax)); 0416 QList< QPair<QRectF, T> > undoData; 0417 undoData << qMakePair(QRectF(rect), T()); 0418 undoData << m_tree.insertShiftDown(rect); 0419 regionChanged(invalidRect); 0420 return undoData; 0421 } 0422 0423 template<typename T> 0424 QList< QPair<QRectF, T> > RectStorage<T>::removeShiftLeft(const QRect& rect) 0425 { 0426 ensureLoaded(); 0427 const QRect invalidRect(rect.topLeft(), QPoint(KS_colMax, rect.bottom())); 0428 QList< QPair<QRectF, T> > undoData; 0429 undoData << qMakePair(QRectF(rect), T()); 0430 undoData << m_tree.removeShiftLeft(rect); 0431 regionChanged(invalidRect); 0432 return undoData; 0433 } 0434 0435 template<typename T> 0436 QList< QPair<QRectF, T> > RectStorage<T>::removeShiftUp(const QRect& rect) 0437 { 0438 ensureLoaded(); 0439 const QRect invalidRect(rect.topLeft(), QPoint(rect.right(), KS_rowMax)); 0440 QList< QPair<QRectF, T> > undoData; 0441 undoData << qMakePair(QRectF(rect), T()); 0442 undoData << m_tree.removeShiftUp(rect); 0443 regionChanged(invalidRect); 0444 return undoData; 0445 } 0446 0447 template<typename T> 0448 void RectStorage<T>::triggerGarbageCollection() 0449 { 0450 } 0451 0452 template<typename T> 0453 void RectStorage<T>::garbageCollection() 0454 { 0455 if (m_loader && !m_loader->isFinished()) 0456 return; 0457 0458 // any possible garbage left? 0459 if (m_possibleGarbage.isEmpty()) 0460 return; 0461 0462 const int currentZIndex = m_possibleGarbage.constBegin().key(); 0463 const QPair<QRectF, T> currentPair = m_possibleGarbage.take(currentZIndex); 0464 0465 typedef QPair<QRectF, T> DataPair; 0466 QMap<int, DataPair> pairs = m_tree.intersectingPairs(currentPair.first.toRect()); 0467 if (pairs.isEmpty()) // actually never true, just for sanity 0468 return; 0469 int zIndex = pairs.constBegin().key(); 0470 DataPair pair = pairs[zIndex]; 0471 0472 // check whether the default style is placed first 0473 if (zIndex == currentZIndex && 0474 currentPair.second == T() && 0475 pair.second == T() && 0476 pair.first == currentPair.first) { 0477 debugSheets << "RectStorage: removing default data at" << Region(currentPair.first.toRect()).name(); 0478 m_tree.remove(currentPair.first.toRect(), currentPair.second); 0479 triggerGarbageCollection(); 0480 return; // already done 0481 } 0482 0483 bool found = false; 0484 typename QMap<int, DataPair>::ConstIterator end = pairs.constEnd(); 0485 for (typename QMap<int, DataPair>::ConstIterator it = pairs.constFind(currentZIndex); it != end; ++it) { 0486 zIndex = it.key(); 0487 pair = it.value(); 0488 0489 // as long as the substyle in question was not found, skip the substyle 0490 if (!found) { 0491 if (zIndex == currentZIndex && 0492 pair.first == currentPair.first && 0493 pair.second == currentPair.second) { 0494 found = true; 0495 } 0496 continue; 0497 } 0498 0499 // remove the current pair, if another substyle of the same type, 0500 // the default style or a named style follows and the rectangle 0501 // is completely covered 0502 if (zIndex != currentZIndex && 0503 (pair.second == currentPair.second || pair.second == T()) && 0504 pair.first.toRect().contains(currentPair.first.toRect())) { 0505 debugSheets << "RectStorage: removing data at" << Region(currentPair.first.toRect()).name(); 0506 m_tree.remove(currentPair.first.toRect(), currentPair.second); 0507 break; 0508 } 0509 } 0510 triggerGarbageCollection(); 0511 } 0512 0513 template<typename T> 0514 void RectStorage<T>::regionChanged(const QRect& rect) 0515 { 0516 if (m_loader && !m_loader->isFinished()) 0517 return; 0518 if (m_map->isLoading()) 0519 return; 0520 // mark the possible garbage 0521 // NOTE Stefan: The map may contain multiple indices. The already existing possible garbage has 0522 // has to be inserted most recently, because it should be accessed first. 0523 m_possibleGarbage = m_tree.intersectingPairs(rect).unite(m_possibleGarbage); 0524 triggerGarbageCollection(); 0525 // invalidate cache 0526 invalidateCache(rect); 0527 } 0528 0529 template<typename T> 0530 void RectStorage<T>::invalidateCache(const QRect& invRect) 0531 { 0532 if (m_loader && !m_loader->isFinished()) 0533 return; 0534 #ifdef CALLIGRA_SHEETS_MT 0535 QMutexLocker ml(&m_mutex); 0536 #endif 0537 const QVector<QRect> rects = m_cachedArea.intersected(invRect).rects(); 0538 m_cachedArea = m_cachedArea.subtracted(invRect); 0539 foreach(const QRect& rect, rects) { 0540 for (int col = rect.left(); col <= rect.right(); ++col) { 0541 for (int row = rect.top(); row <= rect.bottom(); ++row) 0542 m_cache.remove(QPoint(col, row)); // also deletes it 0543 } 0544 } 0545 } 0546 0547 template<typename T> 0548 void RectStorage<T>::ensureLoaded() const 0549 { 0550 if (m_loader) { 0551 m_loader->waitForFinished(); 0552 delete m_loader; 0553 const_cast<RectStorage<T>*>(this)->m_loader = 0; 0554 } 0555 } 0556 0557 template<typename T> 0558 RectStorageLoader<T>::RectStorageLoader(RectStorage<T> *storage, const QList<QPair<QRegion, T> > &data) 0559 : m_storage(storage) 0560 , m_data(data) 0561 { 0562 } 0563 0564 template<typename T> 0565 void RectStorageLoader<T>::run() 0566 { 0567 static int total = 0; 0568 debugSheets << "Loading conditional styles"; 0569 QTime t; t.start(); 0570 0571 QList<QPair<QRegion, T> > treeData; 0572 typedef QPair<QRegion, T> TRegion; 0573 foreach (const TRegion& tr, m_data) { 0574 const QRegion& reg = tr.first; 0575 const T& d = tr.second; 0576 0577 int index = m_storage->m_storedData.indexOf(d); 0578 if (index != -1) { 0579 treeData.append(qMakePair(reg, m_storage->m_storedData[index])); 0580 } else { 0581 treeData.append(tr); 0582 m_storage->m_storedData.append(d); 0583 } 0584 } 0585 0586 m_storage->m_tree.load(treeData); 0587 int e = t.elapsed(); 0588 total += e; 0589 debugSheets << "Time: " << e << total; 0590 } 0591 0592 template<typename T> 0593 void RectStorageLoader<T>::waitForFinished() 0594 { 0595 run(); 0596 } 0597 0598 template<typename T> 0599 bool RectStorageLoader<T>::isFinished() const 0600 { 0601 return false; 0602 } 0603 0604 template<typename T> 0605 QList<QPair<QRegion, T> > RectStorageLoader<T>::data() const 0606 { 0607 return m_data; 0608 } 0609 0610 class CommentStorage : public QObject, public RectStorage<QString> 0611 { 0612 Q_OBJECT 0613 public: 0614 explicit CommentStorage(Map* map) : QObject(map), RectStorage<QString>(map) {} 0615 CommentStorage(const CommentStorage& other) : QObject(other.parent()), RectStorage<QString>(other) {} 0616 0617 protected Q_SLOTS: 0618 void triggerGarbageCollection() override { 0619 QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection())); 0620 } 0621 void garbageCollection() override { 0622 RectStorage<QString>::garbageCollection(); 0623 } 0624 }; 0625 0626 0627 0628 class CALLIGRA_SHEETS_ODF_EXPORT FusionStorage : public QObject, public RectStorage<bool> 0629 { 0630 Q_OBJECT 0631 public: 0632 explicit FusionStorage(Map* map) : QObject(map), RectStorage<bool>(map) {} 0633 FusionStorage(const FusionStorage& other) : QObject(other.parent()), RectStorage<bool>(other) {} 0634 0635 protected Q_SLOTS: 0636 void triggerGarbageCollection() override { 0637 QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection())); 0638 } 0639 void garbageCollection() override { 0640 RectStorage<bool>::garbageCollection(); 0641 } 0642 }; 0643 0644 0645 0646 class MatrixStorage : public QObject, public RectStorage<bool> 0647 { 0648 Q_OBJECT 0649 public: 0650 explicit MatrixStorage(Map* map) : QObject(map), RectStorage<bool>(map) {} 0651 MatrixStorage(const MatrixStorage& other) : QObject(other.parent()), RectStorage<bool>(other) {} 0652 0653 protected Q_SLOTS: 0654 void triggerGarbageCollection() override { 0655 QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection())); 0656 } 0657 void garbageCollection() override { 0658 RectStorage<bool>::garbageCollection(); 0659 } 0660 }; 0661 0662 } // namespace Sheets 0663 } // namespace Calligra 0664 0665 #endif // CALLIGRA_SHEETS_RECT_STORAGE