diff --git a/sheets/Condition.cpp b/sheets/Condition.cpp index f01f0e1cd93..c1cf7f560fe 100644 --- a/sheets/Condition.cpp +++ b/sheets/Condition.cpp @@ -1,412 +1,414 @@ /* This file is part of the KDE project Copyright 2010 Marijn Kruisselbrink Copyright 1998, 1999 Torben Weis Copyright 1999- 2006 The KSpread Team This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Local #include "Condition.h" #include #include "SheetsDebug.h" #include "CalculationSettings.h" #include "Cell.h" #include "Formula.h" #include "Map.h" #include "NamedAreaManager.h" #include "Region.h" #include "Sheet.h" #include "Style.h" #include "StyleManager.h" #include "ValueCalc.h" #include "ValueConverter.h" #include "ValueParser.h" #include using namespace Calligra::Sheets; ///////////////////////////////////////////////////////////////////////////// // // Conditional // ///////////////////////////////////////////////////////////////////////////// Conditional::Conditional() : cond(None) { } bool Conditional::operator==(const Conditional &other) const { if (cond != other.cond) { return false; } if (!value1.equal(other.value1)) { return false; } if (!value2.equal(other.value2)) { return false; } return styleName == other.styleName; } ///////////////////////////////////////////////////////////////////////////// // // Conditions // ///////////////////////////////////////////////////////////////////////////// class Q_DECL_HIDDEN Conditions::Private : public QSharedData { public: QLinkedList conditionList; Style defaultStyle; }; Conditions::Conditions() : d(new Private) { } Conditions::Conditions(const Conditions& other) : d(other.d) { } Conditions::~Conditions() { } bool Conditions::isEmpty() const { return d->conditionList.isEmpty(); } Style Conditions::testConditions( const Cell& cell ) const { Conditional condition; if (currentCondition(cell, condition)) { StyleManager *const styleManager = cell.sheet()->map()->styleManager(); Style *const style = styleManager->style(condition.styleName); if (style) return *style; } return d->defaultStyle; } bool Conditions::currentCondition(const Cell& cell, Conditional & condition) const { /* for now, the first condition that is true is the one that will be used */ const Value value = cell.value(); ValueCalc *const calc = cell.sheet()->map()->calc(); QLinkedList::const_iterator it; for (it = d->conditionList.begin(); it != d->conditionList.end(); ++it) { condition = *it; // debugSheets << "Checking condition resulting in applying" << it->styleName; // The first value of the condition is always used and has to be // comparable to the cell's value. if (!value.allowComparison(condition.value1)) { continue; } switch (condition.cond) { case Conditional::Equal: if (value.equal(condition.value1, calc->settings()->caseSensitiveComparisons())) { return true; } break; case Conditional::Superior: if (value.greater(condition.value1, calc->settings()->caseSensitiveComparisons())) { return true; } break; case Conditional::Inferior: if (value.less(condition.value1, calc->settings()->caseSensitiveComparisons())) { return true; } break; case Conditional::SuperiorEqual: if (value.compare(condition.value1, calc->settings()->caseSensitiveComparisons()) >= 0) { return true; } break; case Conditional::InferiorEqual: if (value.compare(condition.value1, calc->settings()->caseSensitiveComparisons()) <= 0) { return true; } break; case Conditional::Between: { const QVector values(QVector() << condition.value1 << condition.value2); const Value min = calc->min(values); const Value max = calc->max(values); if (value.compare(min, calc->settings()->caseSensitiveComparisons()) >= 0 && value.compare(max, calc->settings()->caseSensitiveComparisons()) <= 0) { return true; } break; } case Conditional::Different: { const QVector values(QVector() << condition.value1 << condition.value2); const Value min = calc->min(values); const Value max = calc->max(values); if (value.greater(max, calc->settings()->caseSensitiveComparisons()) || value.less(min, calc->settings()->caseSensitiveComparisons())) { return true; } break; } case Conditional::DifferentTo: if (!value.equal(condition.value1, calc->settings()->caseSensitiveComparisons())) { return true; } break; case Conditional::IsTrueFormula: // TODO: do some caching if (isTrueFormula(cell, condition.value1.asString(), condition.baseCellAddress)) { return true; } break; default: break; } } return false; } bool Conditions::isTrueFormula(const Cell &cell, const QString &formula, const QString &baseCellAddress) const { Map* const map = cell.sheet()->map(); ValueCalc *const calc = map->calc(); Formula f(cell.sheet(), cell); f.setExpression('=' + formula); Region r(baseCellAddress, map, cell.sheet()); if (r.isValid() && r.isSingular()) { QPoint basePoint = static_cast(*r.constBegin())->pos(); QString newFormula('='); const Tokens tokens = f.tokens(); for (int t = 0; t < tokens.count(); ++t) { const Token token = tokens[t]; if (token.type() == Token::Cell || token.type() == Token::Range) { if (map->namedAreaManager()->contains(token.text())) { newFormula.append(token.text()); continue; } const Region region(token.text(), map, cell.sheet()); if (!region.isValid() || !region.isContiguous()) { newFormula.append(token.text()); continue; } if (region.firstSheet() != r.firstSheet()) { newFormula.append(token.text()); continue; } Region::Element* element = *region.constBegin(); if (element->type() == Region::Element::Point) { Region::Point* point = static_cast(element); QPoint pos = point->pos(); if (!point->isRowFixed()) { int delta = pos.y() - basePoint.y(); pos.setY(cell.row() + delta); } if (!point->isColumnFixed()) { int delta = pos.x() - basePoint.x(); pos.setX(cell.column() + delta); } newFormula.append(Region(pos, cell.sheet()).name()); } else { Region::Range* range = static_cast(element); QRect r = range->rect(); if (!range->isTopFixed()) { int delta = r.top() - basePoint.y(); r.setTop(cell.row() + delta); } if (!range->isBottomFixed()) { int delta = r.bottom() - basePoint.y(); r.setBottom(cell.row() + delta); } if (!range->isLeftFixed()) { int delta = r.left() - basePoint.x(); r.setLeft(cell.column() + delta); } if (!range->isRightFixed()) { int delta = r.right() - basePoint.x(); r.setRight(cell.column() + delta); } newFormula.append(Region(r, cell.sheet()).name()); } } else { newFormula.append(token.text()); } } f.setExpression(newFormula); } Value val = f.eval(); return calc->conv()->asBoolean(val).asBoolean(); } QLinkedList Conditions::conditionList() const { return d->conditionList; } void Conditions::setConditionList(const QLinkedList & list) { d->conditionList = list; } Style Conditions::defaultStyle() const { return d->defaultStyle; } void Conditions::setDefaultStyle(const Style &style) { d->defaultStyle = style; } void Conditions::addCondition(Conditional cond) { d->conditionList.append(cond); } QDomElement Conditions::saveConditions(QDomDocument &doc, ValueConverter *converter) const { QDomElement conditions = doc.createElement("condition"); QLinkedList::const_iterator it; QDomElement child; int num = 0; QString name; for (it = d->conditionList.begin(); it != d->conditionList.end(); ++it) { Conditional condition = *it; /* the name of the element will be "condition" * This is unimportant now but in older versions three conditions were * hardcoded with names "first" "second" and "third" */ name.setNum(num); name.prepend("condition"); child = doc.createElement(name); child.setAttribute("cond", QString::number((int) condition.cond)); // TODO: saving in KSpread 1.1 | KSpread 1.2 format if (condition.value1.isString()) { child.setAttribute("strval1", condition.value1.asString()); if (!condition.value2.asString().isEmpty()) { child.setAttribute("strval2", condition.value2.asString()); } } else { child.setAttribute("val1", converter->asString(condition.value1).asString()); child.setAttribute("val2", converter->asString(condition.value2).asString()); } if (!condition.styleName.isEmpty()) { child.setAttribute("style", condition.styleName); } conditions.appendChild(child); ++num; } if (num == 0) { /* there weren't any real conditions -- return a null dom element */ return QDomElement(); } else { return conditions; } } void Conditions::loadConditions(const KoXmlElement &element, const ValueParser *parser) { Conditional newCondition; KoXmlElement conditionElement; forEachElement(conditionElement, element) { if (!conditionElement.hasAttribute("cond")) continue; bool ok = true; newCondition.cond = (Conditional::Type) conditionElement.attribute("cond").toInt(&ok); if(!ok) continue; if (conditionElement.hasAttribute("val1")) { newCondition.value1 = parser->parse(conditionElement.attribute("val1")); if (conditionElement.hasAttribute("val2")) newCondition.value2 = parser->parse(conditionElement.attribute("val2")); } if (conditionElement.hasAttribute("strval1")) { newCondition.value1 = Value(conditionElement.attribute("strval1")); if (conditionElement.hasAttribute("strval2")) newCondition.value2 = Value(conditionElement.attribute("strval2")); } if (conditionElement.hasAttribute("style")) { newCondition.styleName = conditionElement.attribute("style"); } d->conditionList.append(newCondition); } } void Conditions::operator=(const Conditions & other) { d = other.d; } bool Conditions::operator==(const Conditions& other) const { + if (d->defaultStyle != other.d->defaultStyle) + return false; if (d->conditionList.count() != other.d->conditionList.count()) return false; QLinkedList::ConstIterator end(d->conditionList.end()); for (QLinkedList::ConstIterator it(d->conditionList.begin()); it != end; ++it) { bool found = false; QLinkedList::ConstIterator otherEnd(other.d->conditionList.end()); for (QLinkedList::ConstIterator otherIt(other.d->conditionList.begin()); otherIt != otherEnd; ++otherIt) { if ((*it) == (*otherIt)) found = true; } if (!found) return false; } return true; } uint Calligra::Sheets::qHash(const Conditions &c) { - uint res = 0; + uint res = qHash(c.defaultStyle()); foreach (const Conditional& co, c.conditionList()) { res ^= qHash(co); } return res; } uint Calligra::Sheets::qHash(const Conditional& c) { return qHash(c.value1); } diff --git a/sheets/RectStorage.h b/sheets/RectStorage.h index c9f77ab9875..c6f00a20468 100644 --- a/sheets/RectStorage.h +++ b/sheets/RectStorage.h @@ -1,669 +1,665 @@ /* This file is part of the KDE project Copyright 2010 Marijn Kruisselbrink Copyright 2006,2007 Stefan Nikolaus This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef CALLIGRA_SHEETS_RECT_STORAGE #define CALLIGRA_SHEETS_RECT_STORAGE #include #include #include #include #include #ifdef CALLIGRA_SHEETS_MT #include #include #endif #include "sheets_odf_export.h" #include "Map.h" #include "Region.h" #include "RTree.h" static const int g_garbageCollectionTimeOut = 100; namespace Calligra { namespace Sheets { template class RectStorageLoader; /** * \ingroup Storage * A custom rectangular storage. * Based on an R-Tree data structure. * Usable for any kind of data attached to rectangular regions. * * Acts mainly as a wrapper around the R-Tree data structure to allow a future * replacement of this backend. Decorated with some additional features like * garbage collection, caching, used area tracking, etc. * * \author Stefan Nikolaus * * \note For data assigned to points use PointStorage. */ template class RectStorage { public: explicit RectStorage(Map* map); RectStorage(const RectStorage& other); virtual ~RectStorage(); /** * \return the stored value at the position \p point . */ T contains(const QPoint& point) const; /** * \return the stored rect/value pair at the position \p point . */ QPair containedPair(const QPoint& point) const; QList< QPair > intersectingPairs(const Region& region) const; QList< QPair > undoData(const Region& region) const; /** * Returns the area, which got data attached. * \return the area using data */ QRect usedArea() const; /** * Mass loading of data, removes any existing data first */ void load(const QList >& data); /** * Assigns \p data to \p region . */ void insert(const Region& region, const T& data); /** * Removes \p data from \p region . */ void remove(const Region& region, const T& data); /** * Inserts \p number rows at the position \p position . * It extends or shifts rectangles, respectively. */ QList< QPair > insertRows(int position, int number); /** * Inserts \p number columns at the position \p position . * It extends or shifts rectangles, respectively. */ QList< QPair > insertColumns(int position, int number); /** * Deletes \p number rows at the position \p position . * It shrinks or shifts rectangles, respectively. */ QList< QPair > removeRows(int position, int number); /** * Deletes \p number columns at the position \p position . * It shrinks or shifts rectangles, respectively. */ QList< QPair > removeColumns(int position, int number); /** * Shifts the rows right of \p rect to the right by the width of \p rect . * It extends or shifts rectangles, respectively. */ QList< QPair > insertShiftRight(const QRect& rect); /** * Shifts the columns at the bottom of \p rect to the bottom by the height of \p rect . * It extends or shifts rectangles, respectively. */ QList< QPair > insertShiftDown(const QRect& rect); /** * Shifts the rows left of \p rect to the left by the width of \p rect . * It shrinks or shifts rectangles, respectively. * \return the former rectangle/data pairs */ QList< QPair > removeShiftLeft(const QRect& rect); /** * Shifts the columns on top of \p rect to the top by the height of \p rect . * It shrinks or shifts rectangles, respectively. * \return the former rectangle/data pairs */ QList< QPair > removeShiftUp(const QRect& rect); protected: virtual void triggerGarbageCollection(); virtual void garbageCollection(); /** * Triggers all necessary actions after a change of \p rect . * Calls invalidateCache() and adds the data in * \p rect to the list of possible garbage. */ void regionChanged(const QRect& rect); /** * Invalidates all cached styles lying in \p rect . */ void invalidateCache(const QRect& rect); /** * Ensures that any load() operation has completed. */ void ensureLoaded() const; private: Map* m_map; RTree m_tree; QRegion m_usedArea; QMap > m_possibleGarbage; QList m_storedData; mutable QCache m_cache; #ifdef CALLIGRA_SHEETS_MT mutable QMutex m_mutex; #endif mutable QRegion m_cachedArea; RectStorageLoader* m_loader; friend class RectStorageLoader; }; template class RectStorageLoader : public QRunnable { public: RectStorageLoader(RectStorage* storage, const QList >& data); virtual void run(); void waitForFinished(); bool isFinished() const; QList > data() const; private: RectStorage* m_storage; QList > m_data; }; template RectStorage::RectStorage(Map* map) : m_map(map), m_loader(0) { } template RectStorage::RectStorage(const RectStorage& other) : m_map(other.m_map) , m_usedArea(other.m_usedArea) , m_storedData(other.m_storedData) , m_loader(0) { m_tree = other.m_tree; if (other.m_loader) { m_loader = new RectStorageLoader(this, other.m_loader->data()); } } template RectStorage::~RectStorage() { delete m_loader; // needs fixing if this ever gets to be multithreaded } template T RectStorage::contains(const QPoint& point) const { ensureLoaded(); #ifdef CALLIGRA_SHEETS_MT QMutexLocker ml(&m_mutex); #endif if (!usedArea().contains(point)) return T(); // first, lookup point in the cache if (m_cache.contains(point)) { return *m_cache.object(point); } // not found, lookup in the tree QList results = m_tree.contains(point); T data = results.isEmpty() ? T() : results.last(); // insert style into the cache m_cache.insert(point, new T(data)); m_cachedArea += QRect(point, point); return data; } template QPair RectStorage::containedPair(const QPoint& point) const { ensureLoaded(); const QList< QPair > results = m_tree.intersectingPairs(QRect(point, point)).values(); return results.isEmpty() ? qMakePair(QRectF(), T()) : results.last(); } template QList< QPair > RectStorage::intersectingPairs(const Region& region) const { ensureLoaded(); QList< QPair > result; Region::ConstIterator end = region.constEnd(); for (Region::ConstIterator it = region.constBegin(); it != end; ++it) result += m_tree.intersectingPairs((*it)->rect()).values(); return result; } template QList< QPair > RectStorage::undoData(const Region& region) const { ensureLoaded(); QList< QPair > result; Region::ConstIterator end = region.constEnd(); for (Region::ConstIterator it = region.constBegin(); it != end; ++it) { const QRect rect = (*it)->rect(); QList< QPair > pairs = m_tree.intersectingPairs(rect).values(); for (int i = 0; i < pairs.count(); ++i) { // trim the rects pairs[i].first = pairs[i].first.intersected(rect); } // Always add a default value even if there are no pairs. result << qMakePair(QRectF(rect), T()) << pairs; } return result; } template QRect RectStorage::usedArea() const { ensureLoaded(); return m_tree.boundingBox().toRect(); } template void RectStorage::load(const QList >& data) { Q_ASSERT(!m_loader); m_loader = new RectStorageLoader(this, data); } template void RectStorage::insert(const Region& region, const T& _data) { ensureLoaded(); T data; // lookup already used data int index = m_storedData.indexOf(_data); if (index != -1) data = m_storedData[index]; else { data = _data; m_storedData.append(_data); } Region::ConstIterator end(region.constEnd()); for (Region::ConstIterator it(region.constBegin()); it != end; ++it) { // insert data m_tree.insert((*it)->rect(), data); regionChanged((*it)->rect()); } } template void RectStorage::remove(const Region& region, const T& data) { ensureLoaded(); if (!m_storedData.contains(data)) { return; } const Region::ConstIterator end(region.constEnd()); for (Region::ConstIterator it(region.constBegin()); it != end; ++it) { // remove data m_tree.remove((*it)->rect(), data); regionChanged((*it)->rect()); } } template QList< QPair > RectStorage::insertRows(int position, int number) { ensureLoaded(); const QRect invalidRect(1, position, KS_colMax, KS_rowMax); // invalidate the affected, cached styles invalidateCache(invalidRect); // process the tree QList< QPair > undoData; undoData << qMakePair(QRectF(1, KS_rowMax - number + 1, KS_colMax, number), T()); undoData << m_tree.insertRows(position, number, RTree::CopyCurrent); return undoData; } template QList< QPair > RectStorage::insertColumns(int position, int number) { ensureLoaded(); const QRect invalidRect(position, 1, KS_colMax, KS_rowMax); // invalidate the affected, cached styles invalidateCache(invalidRect); // process the tree QList< QPair > undoData; undoData << qMakePair(QRectF(KS_colMax - number + 1, 1, number, KS_rowMax), T()); undoData << m_tree.insertColumns(position, number, RTree::CopyCurrent); return undoData; } template QList< QPair > RectStorage::removeRows(int position, int number) { ensureLoaded(); const QRect invalidRect(1, position, KS_colMax, KS_rowMax); // invalidate the affected, cached styles invalidateCache(invalidRect); // process the tree QList< QPair > undoData; undoData << qMakePair(QRectF(1, position, KS_colMax, number), T()); undoData << m_tree.removeRows(position, number); return undoData; } template QList< QPair > RectStorage::removeColumns(int position, int number) { ensureLoaded(); const QRect invalidRect(position, 1, KS_colMax, KS_rowMax); // invalidate the affected, cached styles invalidateCache(invalidRect); // process the tree QList< QPair > undoData; undoData << qMakePair(QRectF(position, 1, number, KS_rowMax), T()); undoData << m_tree.removeColumns(position, number); return undoData; } template QList< QPair > RectStorage::insertShiftRight(const QRect& rect) { ensureLoaded(); const QRect invalidRect(rect.topLeft(), QPoint(KS_colMax, rect.bottom())); QList< QPair > undoData; undoData << qMakePair(QRectF(rect), T()); undoData << m_tree.insertShiftRight(rect); regionChanged(invalidRect); return undoData; } template QList< QPair > RectStorage::insertShiftDown(const QRect& rect) { ensureLoaded(); const QRect invalidRect(rect.topLeft(), QPoint(rect.right(), KS_rowMax)); QList< QPair > undoData; undoData << qMakePair(QRectF(rect), T()); undoData << m_tree.insertShiftDown(rect); regionChanged(invalidRect); return undoData; } template QList< QPair > RectStorage::removeShiftLeft(const QRect& rect) { ensureLoaded(); const QRect invalidRect(rect.topLeft(), QPoint(KS_colMax, rect.bottom())); QList< QPair > undoData; undoData << qMakePair(QRectF(rect), T()); undoData << m_tree.removeShiftLeft(rect); regionChanged(invalidRect); return undoData; } template QList< QPair > RectStorage::removeShiftUp(const QRect& rect) { ensureLoaded(); const QRect invalidRect(rect.topLeft(), QPoint(rect.right(), KS_rowMax)); QList< QPair > undoData; undoData << qMakePair(QRectF(rect), T()); undoData << m_tree.removeShiftUp(rect); regionChanged(invalidRect); return undoData; } template void RectStorage::triggerGarbageCollection() { } template void RectStorage::garbageCollection() { if (m_loader && !m_loader->isFinished()) return; // any possible garbage left? if (m_possibleGarbage.isEmpty()) return; const int currentZIndex = m_possibleGarbage.constBegin().key(); const QPair currentPair = m_possibleGarbage.take(currentZIndex); typedef QPair DataPair; QMap pairs = m_tree.intersectingPairs(currentPair.first.toRect()); if (pairs.isEmpty()) // actually never true, just for sanity return; int zIndex = pairs.constBegin().key(); DataPair pair = pairs[zIndex]; // check whether the default style is placed first if (zIndex == currentZIndex && currentPair.second == T() && pair.second == T() && pair.first == currentPair.first) { debugSheets << "RectStorage: removing default data at" << Region(currentPair.first.toRect()).name(); m_tree.remove(currentPair.first.toRect(), currentPair.second); triggerGarbageCollection(); return; // already done } bool found = false; typename QMap::ConstIterator end = pairs.constEnd(); for (typename QMap::ConstIterator it = pairs.constFind(currentZIndex); it != end; ++it) { zIndex = it.key(); pair = it.value(); // as long as the substyle in question was not found, skip the substyle if (!found) { if (zIndex == currentZIndex && pair.first == currentPair.first && pair.second == currentPair.second) { found = true; } continue; } // remove the current pair, if another substyle of the same type, // the default style or a named style follows and the rectangle // is completely covered if (zIndex != currentZIndex && (pair.second == currentPair.second || pair.second == T()) && pair.first.toRect().contains(currentPair.first.toRect())) { debugSheets << "RectStorage: removing data at" << Region(currentPair.first.toRect()).name(); m_tree.remove(currentPair.first.toRect(), currentPair.second); break; } } triggerGarbageCollection(); } template void RectStorage::regionChanged(const QRect& rect) { if (m_loader && !m_loader->isFinished()) return; if (m_map->isLoading()) return; // mark the possible garbage // NOTE Stefan: The map may contain multiple indices. The already existing possible garbage has // has to be inserted most recently, because it should be accessed first. m_possibleGarbage = m_tree.intersectingPairs(rect).unite(m_possibleGarbage); triggerGarbageCollection(); // invalidate cache invalidateCache(rect); } template void RectStorage::invalidateCache(const QRect& invRect) { if (m_loader && !m_loader->isFinished()) return; #ifdef CALLIGRA_SHEETS_MT QMutexLocker ml(&m_mutex); #endif const QVector rects = m_cachedArea.intersected(invRect).rects(); m_cachedArea = m_cachedArea.subtracted(invRect); foreach(const QRect& rect, rects) { for (int col = rect.left(); col <= rect.right(); ++col) { for (int row = rect.top(); row <= rect.bottom(); ++row) m_cache.remove(QPoint(col, row)); // also deletes it } } } template void RectStorage::ensureLoaded() const { if (m_loader) { m_loader->waitForFinished(); delete m_loader; const_cast*>(this)->m_loader = 0; } } template RectStorageLoader::RectStorageLoader(RectStorage *storage, const QList > &data) : m_storage(storage) , m_data(data) { } template void RectStorageLoader::run() { static int total = 0; debugSheets << "Loading conditional styles"; QTime t; t.start(); QList > treeData; typedef QPair TRegion; - QMap indexCache; foreach (const TRegion& tr, m_data) { const QRegion& reg = tr.first; const T& d = tr.second; - typename QMap::iterator idx = indexCache.find(d); - int index = idx != indexCache.end() ? idx.value() : m_storage->m_storedData.indexOf(d); + int index = m_storage->m_storedData.indexOf(d); if (index != -1) { treeData.append(qMakePair(reg, m_storage->m_storedData[index])); - if (idx == indexCache.end()) indexCache.insert(d, index); } else { treeData.append(tr); - if (idx == indexCache.end()) indexCache.insert(d, m_storage->m_storedData.size()); m_storage->m_storedData.append(d); } } m_storage->m_tree.load(treeData); int e = t.elapsed(); total += e; debugSheets << "Time: " << e << total; } template void RectStorageLoader::waitForFinished() { run(); } template bool RectStorageLoader::isFinished() const { return false; } template QList > RectStorageLoader::data() const { return m_data; } class CommentStorage : public QObject, public RectStorage { Q_OBJECT public: explicit CommentStorage(Map* map) : QObject(map), RectStorage(map) {} CommentStorage(const CommentStorage& other) : QObject(other.parent()), RectStorage(other) {} protected Q_SLOTS: virtual void triggerGarbageCollection() { QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection())); } virtual void garbageCollection() { RectStorage::garbageCollection(); } }; class CALLIGRA_SHEETS_ODF_EXPORT FusionStorage : public QObject, public RectStorage { Q_OBJECT public: explicit FusionStorage(Map* map) : QObject(map), RectStorage(map) {} FusionStorage(const FusionStorage& other) : QObject(other.parent()), RectStorage(other) {} protected Q_SLOTS: virtual void triggerGarbageCollection() { QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection())); } virtual void garbageCollection() { RectStorage::garbageCollection(); } }; class MatrixStorage : public QObject, public RectStorage { Q_OBJECT public: explicit MatrixStorage(Map* map) : QObject(map), RectStorage(map) {} MatrixStorage(const MatrixStorage& other) : QObject(other.parent()), RectStorage(other) {} protected Q_SLOTS: virtual void triggerGarbageCollection() { QTimer::singleShot(g_garbageCollectionTimeOut, this, SLOT(garbageCollection())); } virtual void garbageCollection() { RectStorage::garbageCollection(); } }; } // namespace Sheets } // namespace Calligra #endif // CALLIGRA_SHEETS_RECT_STORAGE