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

0001 /* This file is part of the KDE project
0002    Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #ifndef CALLIGRA_SHEETS_POINT_STORAGE
0021 #define CALLIGRA_SHEETS_POINT_STORAGE
0022 
0023 #include <QRect>
0024 #include <QString>
0025 #include <QVector>
0026 
0027 #include "Region.h"
0028 #include "calligra_sheets_limits.h"
0029 
0030 #include <algorithm>
0031 
0032 // #define KSPREAD_POINT_STORAGE_HASH
0033 
0034 namespace Calligra
0035 {
0036 namespace Sheets
0037 {
0038 
0039 /**
0040  * \ingroup Storage
0041  * A custom pointwise storage.
0042  * Based on a compressed sparse matrix data structure.
0043  * Usable for any kind of data attached to 2D coordinates.
0044  *
0045  * Only non-default data with its coordinate is stored. Hence, the storage
0046  * has a small memory footprint nearly regardless of the data's location.
0047  * Each empty row before a location occupy an integer, which is not the case
0048  * for columns. Iterating over the data becomes fast compared to dense
0049  * matrix/array, where each location has to be traversed irrespective of
0050  * default or non-default data.
0051  *
0052  * The actual data is stored in the list m_data. It is grouped by rows in
0053  * ascending order. The rows' beginnings and ends are stored in the list
0054  * m_rows. Its index corresponds to the row index. The values denote the
0055  * starting index of a row in m_data. The row's end is determined by
0056  * the starting position of the next row. The entries in each row are ordered
0057  * by column. The corresponding column indices are stored in m_cols. Hence,
0058  * m_cols has the same amount of entries as m_data.
0059  *
0060  * \author Stefan Nikolaus <stefan.nikolaus@kdemail.net>
0061  *
0062  * \note If you fill the storage, do it row-wise. That's more performant.
0063  * \note For data assigned to rectangular regions use RectStorage.
0064  * \note It's QVector based. To boost performance a lot, declare the stored
0065  *       data type as movable.
0066  */
0067 template<typename T>
0068 class PointStorage
0069 {
0070     friend class PointStorageBenchmark;
0071     friend class PointStorageTest;
0072 
0073 public:
0074     /**
0075      * Constructor.
0076      * Creates an empty storage. Actually, does nothing.
0077      */
0078     PointStorage() {}
0079 
0080     /**
0081      * Destructor.
0082      */
0083     ~PointStorage() {}
0084 
0085     /**
0086      * Clears the storage.
0087      */
0088     void clear() {
0089         m_cols.clear();
0090         m_rows.clear();
0091         m_data.clear();
0092     }
0093 
0094     /**
0095      * Returns the number of items in the storage.
0096      * Usable to iterate over all non-default data.
0097      * \return number of items
0098      * \see col()
0099      * \see row()
0100      * \see data()
0101      */
0102     int count() const {
0103         return m_data.count();
0104     }
0105 
0106     /**
0107      * Inserts \p data at \p col , \p row .
0108      * \return the overridden data (default data, if no overwrite)
0109      */
0110     T insert(int col, int row, const T& data) {
0111         Q_ASSERT(1 <= col && col <= KS_colMax);
0112         Q_ASSERT(1 <= row && row <= KS_rowMax);
0113         // row's missing?
0114         if (row > m_rows.count()) {
0115             // insert missing rows
0116             m_rows.insert(m_rows.count(), row - m_rows.count(), m_data.count());
0117             // append the actual data
0118 #ifdef KSPREAD_POINT_STORAGE_HASH
0119             m_data.append(*m_usedData.insert(data));
0120 #else
0121             m_data.append(data);
0122 #endif
0123             // append the column index
0124             m_cols.append(col);
0125         }
0126         // the row exists
0127         else {
0128             const QVector<int>::const_iterator cstart(m_cols.begin() + m_rows.value(row - 1));
0129             const QVector<int>::const_iterator cend((row < m_rows.count()) ? (m_cols.begin() + m_rows.value(row)) : m_cols.end());
0130             const QVector<int>::const_iterator cit = std::lower_bound(cstart, cend, col);
0131             // column's missing?
0132             if (cit == cend || *cit != col) {
0133                 // determine the index where the data and column has to be inserted
0134                 const int index = m_rows.value(row - 1) + (cit - cstart);
0135                 // insert the actual data
0136 #ifdef KSPREAD_POINT_STORAGE_HASH
0137                 m_data.insert(index, *m_usedData.insert(data));
0138 #else
0139                 m_data.insert(index, data);
0140 #endif
0141                 // insert the column index
0142                 m_cols.insert(index, col);
0143                 // adjust the offsets of the following rows
0144                 for (int r = row; r < m_rows.count(); ++r)
0145                     ++m_rows[r];
0146             }
0147             // column exists
0148             else {
0149                 const int index = m_rows.value(row - 1) + (cit - cstart);
0150                 const T oldData = m_data[ index ];
0151 #ifdef KSPREAD_POINT_STORAGE_HASH
0152                 m_data[ index ] = *m_usedData.insert(data);
0153 #else
0154                 m_data[ index ] = data;
0155 #endif
0156                 return oldData;
0157             }
0158         }
0159         squeezeRows();
0160         return T();
0161     }
0162 
0163     /**
0164      * Looks up the data at \p col , \p row . If no data was found returns a
0165      * default object.
0166      * \return the data at the given coordinate
0167      */
0168     T lookup(int col, int row, const T& defaultVal = T()) const {
0169         Q_ASSERT(1 <= col && col <= KS_colMax);
0170         Q_ASSERT(1 <= row && row <= KS_rowMax);
0171         // is the row not present?
0172         if (row > m_rows.count())
0173             return defaultVal;
0174         const QVector<int>::const_iterator cstart(m_cols.begin() + m_rows.value(row - 1));
0175         const QVector<int>::const_iterator cend((row < m_rows.count()) ? (m_cols.begin() + m_rows.value(row)) : m_cols.end());
0176         const QVector<int>::const_iterator cit = std::lower_bound(cstart, cend, col);
0177         // is the col not present?
0178         if (cit == cend || col != *cit)
0179             return defaultVal;
0180         return m_data.value(m_rows.value(row - 1) + (cit - cstart));
0181     }
0182 
0183     /**
0184      * Removes data at \p col , \p row .
0185      * \return the removed data (default data, if none)
0186      */
0187     T take(int col, int row, const T& defaultVal = T()) {
0188         Q_ASSERT(1 <= col && col <= KS_colMax);
0189         Q_ASSERT(1 <= row && row <= KS_rowMax);
0190         // row's missing?
0191         if (row > m_rows.count())
0192             return defaultVal;
0193         const int rowStart = (row - 1 < m_rows.count()) ? m_rows.value(row - 1) : m_data.count();
0194         const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1;
0195         const QVector<int> cols = m_cols.mid(rowStart, rowLength);
0196         QVector<int>::const_iterator cit = std::lower_bound(cols.begin(), cols.end(), col);
0197         // column's missing?
0198         if (cit == cols.constEnd() || col != *cit)
0199             return defaultVal;
0200         const int index = rowStart + (cit - cols.constBegin());
0201         // save the old data
0202         const T oldData = m_data[ index ];
0203         // remove the actual data
0204         m_data.remove(index);
0205         // remove the column index
0206         m_cols.remove(index);
0207         // adjust the offsets of the following rows
0208         for (int r = row; r < m_rows.count(); ++r)
0209             --m_rows[r];
0210         squeezeRows();
0211         return oldData;
0212     }
0213 
0214     /**
0215      * Insert \p number columns at \p position .
0216      * \return the data, that became out of range (shifted over the end)
0217      */
0218     QVector< QPair<QPoint, T> > insertColumns(int position, int number) {
0219         Q_ASSERT(1 <= position && position <= KS_colMax);
0220         QVector< QPair<QPoint, T> > oldData;
0221         for (int row = m_rows.count(); row >= 1; --row) {
0222             const int rowStart = m_rows.value(row - 1);
0223             const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1;
0224             const QVector<int> cols = m_cols.mid(rowStart, rowLength);
0225             for (int col = cols.count(); col >= 0; --col) {
0226                 if (cols.value(col) + number > KS_colMax) {
0227                     oldData.append(qMakePair(QPoint(cols.value(col), row), m_data.value(rowStart + col)));
0228                     m_cols.remove(rowStart + col);
0229                     m_data.remove(rowStart + col);
0230                     // adjust the offsets of the following rows
0231                     for (int r = row; r < m_rows.count(); ++r)
0232                         --m_rows[r];
0233                 } else if (cols.value(col) >= position)
0234                     m_cols[rowStart + col] += number;
0235             }
0236         }
0237         squeezeRows();
0238         return oldData;
0239     }
0240 
0241     /**
0242      * Removes \p number columns at \p position .
0243      * \return the removed data
0244      */
0245     QVector< QPair<QPoint, T> > removeColumns(int position, int number) {
0246         Q_ASSERT(1 <= position && position <= KS_colMax);
0247         QVector< QPair<QPoint, T> > oldData;
0248         for (int row = m_rows.count(); row >= 1; --row) {
0249             const int rowStart = m_rows.value(row - 1);
0250             const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1;
0251             const QVector<int> cols = m_cols.mid(rowStart, rowLength);
0252             for (int col = cols.count() - 1; col >= 0; --col) {
0253                 if (cols.value(col) >= position) {
0254                     if (cols.value(col) < position + number) {
0255                         oldData.append(qMakePair(QPoint(cols.value(col), row), m_data.value(rowStart + col)));
0256                         m_cols.remove(rowStart + col);
0257                         m_data.remove(rowStart + col);
0258                         for (int r = row; r < m_rows.count(); ++r)
0259                             --m_rows[r];
0260                     } else
0261                         m_cols[rowStart + col] -= number;
0262                 }
0263             }
0264         }
0265         squeezeRows();
0266         return oldData;
0267     }
0268 
0269     /**
0270      * Insert \p number rows at \p position .
0271      * \return the data, that became out of range (shifted over the end)
0272      */
0273     QVector< QPair<QPoint, T> > insertRows(int position, int number) {
0274         Q_ASSERT(1 <= position && position <= KS_rowMax);
0275         // row's missing?
0276         if (position > m_rows.count())
0277             return QVector< QPair<QPoint, T> >();
0278         QVector< QPair<QPoint, T> > oldData;
0279         int dataCount = 0;
0280         int rowCount = 0;
0281         // save the old data
0282         for (int row = KS_rowMax - number + 1; row <= m_rows.count() && row <= KS_rowMax; ++row) {
0283             const QVector<int>::const_iterator cstart(m_cols.begin() + m_rows.value(row - 1));
0284             const QVector<int>::const_iterator cend((row < m_rows.count()) ? (m_cols.begin() + m_rows.value(row)) : m_cols.end());
0285             for (QVector<int>::const_iterator cit = cstart; cit != cend; ++cit)
0286                 oldData.append(qMakePair(QPoint(*cit, row), m_data.value(cit - m_cols.constBegin())));
0287             dataCount += (cend - cstart);
0288             ++rowCount;
0289         }
0290         // remove the out of range data
0291         while (dataCount-- > 0) {
0292             m_data.remove(m_data.count() - 1);
0293             m_cols.remove(m_cols.count() - 1);
0294         }
0295         while (rowCount-- > 0)
0296             m_rows.remove(m_rows.count() - 1);
0297         // insert the new rows
0298         const int index = m_rows.value(position - 1);
0299         for (int r = 0; r < number; ++r)
0300             m_rows.insert(position, index);
0301         squeezeRows();
0302         return oldData;
0303     }
0304 
0305     /**
0306      * Removes \p number rows at \p position .
0307      * \return the removed data
0308      */
0309     QVector< QPair<QPoint, T> > removeRows(int position, int number) {
0310         Q_ASSERT(1 <= position && position <= KS_rowMax);
0311         // row's missing?
0312         if (position > m_rows.count())
0313             return QVector< QPair<QPoint, T> >();
0314         QVector< QPair<QPoint, T> > oldData;
0315         int dataCount = 0;
0316         int rowCount = 0;
0317         // save the old data
0318         for (int row = position; row <= m_rows.count() && row <= position + number - 1; ++row) {
0319             const int rowStart = m_rows.value(row - 1);
0320             const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1;
0321             const QVector<int> cols = m_cols.mid(rowStart, rowLength);
0322             const QVector<T> data = m_data.mid(rowStart, rowLength);
0323             for (int col = 0; col < cols.count(); ++col)
0324                 oldData.append(qMakePair(QPoint(cols.value(col), row), data.value(col)));
0325             dataCount += data.count();
0326             ++rowCount;
0327         }
0328         // adjust the offsets of the following rows
0329         for (int r = position + number - 1; r < m_rows.count(); ++r)
0330             m_rows[r] -= dataCount;
0331         // remove the out of range data
0332         while (dataCount-- > 0) {
0333             m_data.remove(m_rows.value(position - 1));
0334             m_cols.remove(m_rows.value(position - 1));
0335         }
0336         while (rowCount-- > 0)
0337             m_rows.remove(position - 1);
0338         squeezeRows();
0339         return oldData;
0340     }
0341 
0342     /**
0343      * Shifts the data right of \p rect to the left by the width of \p rect .
0344      * The data formerly contained in \p rect becomes overridden.
0345      * \return the removed data
0346      */
0347     QVector< QPair<QPoint, T> > removeShiftLeft(const QRect& rect) {
0348         Q_ASSERT(1 <= rect.left() && rect.left() <= KS_colMax);
0349         QVector< QPair<QPoint, T> > oldData;
0350         for (int row = qMin(rect.bottom(), m_rows.count()); row >= rect.top(); --row) {
0351             const int rowStart = m_rows.value(row - 1);
0352             const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1;
0353             const QVector<int> cols = m_cols.mid(rowStart, rowLength);
0354             for (int col = cols.count() - 1; col >= 0; --col) {
0355                 if (cols.value(col) >= rect.left()) {
0356                     if (cols.value(col) <= rect.right()) {
0357                         oldData.append(qMakePair(QPoint(cols.value(col), row), m_data.value(rowStart + col)));
0358                         m_cols.remove(rowStart + col);
0359                         m_data.remove(rowStart + col);
0360                         for (int r = row; r < m_rows.count(); ++r)
0361                             --m_rows[r];
0362                     } else
0363                         m_cols[rowStart + col] -= rect.width();
0364                 }
0365             }
0366         }
0367         squeezeRows();
0368         return oldData;
0369     }
0370 
0371     /**
0372      * Shifts the data in and right of \p rect to the right by the width of \p rect .
0373      * \return the data, that became out of range (shifted over the end)
0374      */
0375     QVector< QPair<QPoint, T> > insertShiftRight(const QRect& rect) {
0376         Q_ASSERT(1 <= rect.left() && rect.left() <= KS_colMax);
0377         QVector< QPair<QPoint, T> > oldData;
0378         for (int row = rect.top(); row <= rect.bottom() && row <= m_rows.count(); ++row) {
0379             const int rowStart = m_rows.value(row - 1);
0380             const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1;
0381             const QVector<int> cols = m_cols.mid(rowStart, rowLength);
0382             for (int col = cols.count(); col >= 0; --col) {
0383                 if (cols.value(col) + rect.width() > KS_colMax) {
0384                     oldData.append(qMakePair(QPoint(cols.value(col), row), m_data.value(rowStart + col)));
0385                     m_cols.remove(rowStart + col);
0386                     m_data.remove(rowStart + col);
0387                     // adjust the offsets of the following rows
0388                     for (int r = row; r < m_rows.count(); ++r)
0389                         --m_rows[r];
0390                 } else if (cols.value(col) >= rect.left())
0391                     m_cols[rowStart + col] += rect.width();
0392             }
0393         }
0394         squeezeRows();
0395         return oldData;
0396     }
0397 
0398     /**
0399      * Shifts the data below \p rect to the top by the height of \p rect .
0400      * The data formerly contained in \p rect becomes overridden.
0401      * \return the removed data
0402      */
0403     QVector< QPair<QPoint, T> > removeShiftUp(const QRect& rect) {
0404         Q_ASSERT(1 <= rect.top() && rect.top() <= KS_rowMax);
0405         // row's missing?
0406         if (rect.top() > m_rows.count()) {
0407             return QVector< QPair<QPoint, T> >();
0408         }
0409         QVector< QPair<QPoint, T> > oldData;
0410         for (int row = rect.top(); row <= m_rows.count() && row <= KS_rowMax - rect.height(); ++row) {
0411             const int rowStart = m_rows.value(row - 1);
0412             const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1;
0413             const QVector<int> cols = m_cols.mid(rowStart, rowLength);
0414             const QVector<T> data = m_data.mid(rowStart, rowLength);
0415             // first, iterate over the destination row
0416             for (int col = cols.count() - 1; col >= 0; --col) {
0417                 const int column = cols.value(col); // real column value (1...KS_colMax)
0418                 if (column >= rect.left() && column <= rect.right()) {
0419                     // save the old data
0420                     if (row <= rect.bottom())
0421                         oldData.append(qMakePair(QPoint(column, row), data.value(col)));
0422                     // search
0423                     const int srcRow = row + rect.height();
0424                     const QVector<int>::const_iterator cstart2((srcRow - 1 < m_rows.count()) ? m_cols.begin() + m_rows.value(srcRow - 1) : m_cols.end());
0425                     const QVector<int>::const_iterator cend2((srcRow < m_rows.count()) ? (m_cols.begin() + m_rows.value(srcRow)) : m_cols.end());
0426                     const QVector<int>::const_iterator cit2 = std::lower_bound(cstart2, cend2, column);
0427                     // column's missing?
0428                     if (cit2 == cend2 || *cit2 != column) {
0429                         m_cols.remove(rowStart + col);
0430                         m_data.remove(rowStart + col);
0431                         // adjust the offsets of the following rows
0432                         for (int r = row; r < m_rows.count(); ++r)
0433                             --m_rows[r];
0434                     }
0435                     // column exists
0436                     else {
0437                         // copy
0438                         m_data[rowStart + col] = m_data.value(cit2 - m_cols.constBegin());
0439                         // remove
0440                         m_cols.remove(cit2 - m_cols.constBegin());
0441                         m_data.remove(cit2 - m_cols.constBegin());
0442                         // adjust the offsets of the following rows
0443                         for (int r = row + rect.height(); r < m_rows.count(); ++r)
0444                             --m_rows[r];
0445                     }
0446                 }
0447             }
0448             // last, iterate over the source row
0449             const int srcRow = row + rect.height();
0450             const int rowStart2 = (srcRow - 1 < m_rows.count()) ? m_rows.value(srcRow - 1) : m_data.count();
0451             const int rowLength2 = (srcRow < m_rows.count()) ? m_rows.value(srcRow) - rowStart2 : -1;
0452             const QVector<int> cols2 = m_cols.mid(rowStart2, rowLength2);
0453             const QVector<T> data2 = m_data.mid(rowStart2, rowLength2);
0454             int offset = 0;
0455             for (int col = cols2.count() - 1; col >= 0; --col) {
0456                 const int column = cols2.value(col); // real column value (1...KS_colMax)
0457                 if (column >= rect.left() && column <= rect.right()) {
0458                     // find the insertion position
0459                     const QVector<int>::const_iterator cstart((row - 1 < m_rows.count()) ? m_cols.begin() + m_rows.value(row - 1) : m_cols.end());
0460                     const QVector<int>::const_iterator cend(((row < m_rows.count())) ? (m_cols.begin() + m_rows.value(row)) : m_cols.end());
0461                     const QVector<int>::const_iterator cit = std::upper_bound(cstart, cend, cols2.value(col));
0462                     // Destination column:
0463                     const QVector<int>::const_iterator dstcit = std::lower_bound(cols.begin(), cols.end(), column);
0464                     if (dstcit != cols.end() && *dstcit == column) { // destination column exists
0465                         // replace the existing destination value
0466                         const int dstCol = (dstcit - cols.constBegin());
0467                         m_data[rowStart + dstCol] = m_data.value(rowStart2 + col);
0468                         // remove it from its old position
0469                         m_data.remove(rowStart2 + col + 1);
0470                         m_cols.remove(rowStart2 + col + 1);
0471                         // The amount of values in the range from the
0472                         // destination row to the source row have not changed.
0473                         // adjust the offsets of the following rows
0474                         for (int r = srcRow; r < m_rows.count(); ++r) {
0475                             ++m_rows[r];
0476                         }
0477                     } else { // destination column does not exist yet
0478                         // copy it to its new position
0479                         const int dstCol = cit - m_cols.constBegin();
0480                         m_data.insert(dstCol, data2.value(col));
0481                         m_cols.insert(dstCol, cols2.value(col));
0482                         // remove it from its old position
0483                         m_data.remove(rowStart2 + col + 1 + offset);
0484                         m_cols.remove(rowStart2 + col + 1 + offset);
0485                         ++offset;
0486                         // adjust the offsets of the following rows
0487                         for (int r = row; r < srcRow; ++r) {
0488                             ++m_rows[r];
0489                         }
0490                     }
0491                 }
0492             }
0493         }
0494         squeezeRows();
0495         return oldData;
0496     }
0497 
0498     /**
0499      * Shifts the data in and below \p rect to the bottom by the height of \p rect .
0500      * \return the data, that became out of range (shifted over the end)
0501      */
0502     QVector< QPair<QPoint, T> > insertShiftDown(const QRect& rect) {
0503         Q_ASSERT(1 <= rect.top() && rect.top() <= KS_rowMax);
0504         // row's missing?
0505         if (rect.top() > m_rows.count())
0506             return QVector< QPair<QPoint, T> >();
0507         QVector< QPair<QPoint, T> > oldData;
0508         for (int row = m_rows.count(); row >= rect.top(); --row) {
0509             const int rowStart = m_rows.value(row - 1);
0510             const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1;
0511             const QVector<int> cols = m_cols.mid(rowStart, rowLength);
0512             const QVector<T> data = m_data.mid(rowStart, rowLength);
0513             for (int col = cols.count() - 1; col >= 0; --col) {
0514                 if (cols.value(col) >= rect.left() && cols.value(col) <= rect.right()) {
0515                     if (row + rect.height() > KS_rowMax) {
0516                         // save old data
0517                         oldData.append(qMakePair(QPoint(cols.value(col), row), data.value(col)));
0518                     } else {
0519                         // insert missing rows
0520                         if (row + rect.height() > m_rows.count())
0521                             m_rows.insert(m_rows.count(), row + rect.height() - m_rows.count(), m_data.count());
0522 
0523                         // copy the data down
0524                         const int row2 = row + rect.height();
0525                         const QVector<int>::const_iterator cstart2(m_cols.begin() + m_rows.value(row2 - 1));
0526                         const QVector<int>::const_iterator cend2((row2 < m_rows.count()) ? (m_cols.begin() + m_rows.value(row2)) : m_cols.end());
0527                         const QVector<int>::const_iterator cit2 = std::lower_bound(cstart2, cend2, cols.value(col));
0528                         // column's missing?
0529                         if (cit2 == cend2 || *cit2 != cols.value(col)) {
0530                             // determine the index where the data and column has to be inserted
0531                             const int index = m_rows.value(row2 - 1) + (cit2 - cstart2);
0532                             // insert the actual data
0533                             m_data.insert(index, data.value(col));
0534                             // insert the column index
0535                             m_cols.insert(index, cols.value(col));
0536                             // adjust the offsets of the following rows
0537                             for (int r = row2; r < m_rows.count(); ++r)
0538                                 ++m_rows[r];
0539                         }
0540                         // column exists
0541                         else {
0542                             const int index = m_rows.value(row2 - 1) + (cit2 - cstart2);
0543                             m_data[ index ] = data.value(col);
0544                         }
0545                     }
0546 
0547                     // remove the data
0548                     m_cols.remove(rowStart + col);
0549                     m_data.remove(rowStart + col);
0550                     // adjust the offsets of the following rows
0551                     for (int r = row; r < m_rows.count(); ++r)
0552                         --m_rows[r];
0553                 }
0554             }
0555         }
0556         squeezeRows();
0557         return oldData;
0558     }
0559 
0560     /**
0561      * Retrieve the first used data in \p col .
0562      * Can be used in conjunction with nextInColumn() to loop through a column.
0563      * \return the first used data in \p col or the default data, if the column is empty.
0564      */
0565     T firstInColumn(int col, int* newRow = 0) const {
0566         Q_ASSERT(1 <= col && col <= KS_colMax);
0567         const int index = m_cols.indexOf(col);
0568         if (newRow) {
0569             if (index == -1)   // not found
0570                 *newRow = 0;
0571             else
0572                 *newRow = std::upper_bound(m_rows.begin(), m_rows.end(), index) - m_rows.begin();
0573         }
0574         return m_data.value(index);
0575     }
0576 
0577     /**
0578      * Retrieve the first used data in \p row .
0579      * Can be used in conjunction with nextInRow() to loop through a row.
0580      * \return the first used data in \p row or the default data, if the row is empty.
0581      */
0582     T firstInRow(int row, int* newCol = 0) const {
0583         Q_ASSERT(1 <= row && row <= KS_rowMax);
0584         // row's empty?
0585         if (row > m_rows.count() || ((row < m_rows.count()) && m_rows.value(row - 1) == m_rows.value(row))) {
0586             if (newCol)
0587                 *newCol = 0;
0588             return T();
0589         }
0590         if (newCol)
0591             *newCol = m_cols.value(m_rows.value(row - 1));
0592         return m_data.value(m_rows.value(row - 1));
0593     }
0594 
0595     /**
0596      * Retrieve the last used data in \p col .
0597      * Can be used in conjunction with prevInColumn() to loop through a column.
0598      * \return the last used data in \p col or the default data, if the column is empty.
0599      */
0600     T lastInColumn(int col, int* newRow = 0) const {
0601         Q_ASSERT(1 <= col && col <= KS_colMax);
0602         const int index = m_cols.lastIndexOf(col);
0603         if (newRow) {
0604             if (index == -1)   // not found
0605                 *newRow = 0;
0606             else
0607                 *newRow = std::upper_bound(m_rows.begin(), m_rows.end(), index) - m_rows.begin();
0608         }
0609         return m_data.value(index);
0610     }
0611 
0612     /**
0613      * Retrieve the last used data in \p row .
0614      * Can be used in conjunction with prevInRow() to loop through a row.
0615      * \return the last used data in \p row or the default data, if the row is empty.
0616      */
0617     T lastInRow(int row, int* newCol = 0) const {
0618         Q_ASSERT(1 <= row && row <= KS_rowMax);
0619         if (m_rows.isEmpty()) {
0620             if (newCol)
0621                 *newCol = 0;
0622             return T();
0623         }
0624         // last row?
0625         if (row == m_rows.count()) {
0626             if (newCol)
0627                 *newCol = m_cols.value(m_data.count() - 1);
0628             return m_data.last();
0629         }
0630         // row's empty?
0631         if (m_rows.value(row - 1) == m_rows.value(row) || m_rows.value(row - 1) == m_data.count()) {
0632             if (newCol)
0633                 *newCol = 0;
0634             return T();
0635         }
0636         if (newCol)
0637             *newCol = m_cols.value(m_rows.value(row) - 1);
0638         return m_data.value(m_rows.value(row) - 1);
0639     }
0640 
0641     /**
0642      * Retrieve the next used data in \p col after \p row .
0643      * Can be used in conjunction with firstInColumn() to loop through a column.
0644      * \return the next used data in \p col or the default data, there is no further data.
0645      */
0646     T nextInColumn(int col, int row, int* newRow = 0) const {
0647         Q_ASSERT(1 <= col && col <= KS_colMax);
0648         Q_ASSERT(1 <= row && row <= KS_rowMax);
0649         // no next row?
0650         if (row + 1 > m_rows.count()) {
0651             if (newRow)
0652                 *newRow = 0;
0653             return T();
0654         }
0655         // search beginning in rows after the specified row
0656         const int index = m_cols.indexOf(col, m_rows.value(row));
0657         if (newRow) {
0658             if (index == -1)   // not found
0659                 *newRow = 0;
0660             else
0661                 *newRow = std::upper_bound(m_rows.begin(), m_rows.end(), index) - m_rows.begin();
0662         }
0663         return m_data.value(index);
0664     }
0665 
0666     /**
0667      * Retrieve the next used data in \p row after \p col .
0668      * Can be used in conjunction with firstInRow() to loop through a row.
0669      * \return the next used data in \p row or the default data, if there is no further data.
0670      */
0671     T nextInRow(int col, int row, int* newCol = 0) const {
0672         Q_ASSERT(1 <= col && col <= KS_colMax);
0673         Q_ASSERT(1 <= row && row <= KS_rowMax);
0674         // is the row not present?
0675         if (row > m_rows.count()) {
0676             if (newCol)
0677                 *newCol = 0;
0678             return T();
0679         }
0680         const QVector<int>::const_iterator cstart(m_cols.begin() + m_rows.value(row - 1));
0681         const QVector<int>::const_iterator cend((row < m_rows.count()) ? (m_cols.begin() + m_rows.value(row)) : m_cols.end());
0682         const QVector<int>::const_iterator cit = std::upper_bound(cstart, cend, col);
0683         if (cit == cend || *cit <= col) {
0684             if (newCol)
0685                 *newCol = 0;
0686             return T();
0687         }
0688         if (newCol)
0689             *newCol = m_cols.value(m_rows.value(row - 1) + (cit - cstart));
0690         return m_data.value(m_rows.value(row - 1) + (cit - cstart));
0691     }
0692 
0693     /**
0694      * Retrieve the previous used data in \p col after \p row .
0695      * Can be used in conjunction with lastInColumn() to loop through a column.
0696      * \return the previous used data in \p col or the default data, there is no further data.
0697      */
0698     T prevInColumn(int col, int row, int* newRow = 0) const {
0699         Q_ASSERT(1 <= col && col <= KS_colMax);
0700         Q_ASSERT(1 <= row && row <= KS_rowMax);
0701         // first row?
0702         if (row <= m_rows.count() && m_rows.value(row - 1) == 0) {
0703             if (newRow)
0704                 *newRow = 0;
0705             return T();
0706         }
0707         const int index = m_cols.lastIndexOf(col, m_rows.value(row - 1) - 1);
0708         if (newRow) {
0709             if (index == -1)   // not found
0710                 *newRow = 0;
0711             else
0712                 *newRow = std::upper_bound(m_rows.begin(), m_rows.end(), index) - m_rows.begin();
0713         }
0714         return m_data.value(index);
0715     }
0716 
0717     /**
0718      * Retrieve the previous used data in \p row after \p col .
0719      * Can be used in conjunction with lastInRow() to loop through a row.
0720      * \return the previous used data in \p row or the default data, if there is no further data.
0721      */
0722     T prevInRow(int col, int row, int* newCol = 0) const {
0723         Q_ASSERT(1 <= col && col <= KS_colMax);
0724         Q_ASSERT(1 <= row && row <= KS_rowMax);
0725         const QVector<int>::const_iterator cstart((row - 1 < m_rows.count()) ? m_cols.begin() + m_rows.value(row - 1) : m_cols.end());
0726         const QVector<int>::const_iterator cend((row < m_rows.count()) ? (m_cols.begin() + m_rows.value(row)) : m_cols.end());
0727         const QVector<int>::const_iterator cit = std::lower_bound(cstart, cend, col);
0728         if (cit == cstart) {
0729             if (newCol)
0730                 *newCol = 0;
0731             return T();
0732         }
0733         if (newCol)
0734             *newCol = m_cols.value(cit - 1 - m_cols.begin());
0735         return m_data.value(cit - 1 - m_cols.begin());
0736     }
0737 
0738     /**
0739      * For debugging/testing purposes.
0740      * \note only works with primitive/printable data
0741      */
0742     QString dump() const {
0743         QString str;
0744         // determine the dimension of the matrix (the missing column number)
0745         int maxCols = 0;
0746         for (int row = 0; row < m_rows.count(); ++row) {
0747             const int rowStart = m_rows.value(row);
0748             const int rowLength = (row + 1 < m_rows.count()) ? m_rows.value(row + 1) - rowStart : -1;
0749             const QVector<int> cols = m_cols.mid(rowStart, rowLength);
0750             maxCols = qMax(maxCols, cols.value(cols.count() - 1));
0751         }
0752         for (int row = 0; row < m_rows.count(); ++row) {
0753             str += '(';
0754             const int rowStart = m_rows.value(row);
0755             const int rowLength = (row + 1 < m_rows.count()) ? m_rows.value(row + 1) - rowStart : -1;
0756             const QVector<int> cols = m_cols.mid(rowStart, rowLength);
0757             const QVector<T> data = m_data.mid(rowStart, rowLength);
0758             int lastCol = 0;
0759             for (int col = 0; col < cols.count(); ++col) {
0760                 int counter = cols.value(col) - lastCol;
0761                 while (counter-- > 1)
0762                     str += "  ,";
0763                 str += QString("%1,").arg(data.value(col), 2);
0764 //                 str += QString( "%1," ).arg( (data.value( col ) == T()) ? "" : "_", 2 );
0765                 lastCol = cols.value(col);
0766             }
0767             // fill the column up to the max
0768             int counter = maxCols - lastCol;
0769             while (counter-- > 0)
0770                 str += "  ,";
0771             // replace the last comma
0772             str[str.length()-1] = ')';
0773             str += '\n';
0774         }
0775         return str.isEmpty() ? QString("()") : str.mid(0, str.length() - 1);
0776     }
0777 
0778     /**
0779      * Returns the column of the non-default data at \p index .
0780      * \return the data's column at \p index .
0781      * \see count()
0782      * \see row()
0783      * \see data()
0784      */
0785     int col(int index) const {
0786         return m_cols.value(index);
0787     }
0788 
0789     /**
0790      * Returns the row of the non-default data at \p index .
0791      * \return the data's row at \p index .
0792      * \see count()
0793      * \see col()
0794      * \see data()
0795      */
0796     int row(int index) const {
0797         return std::upper_bound(m_rows.begin(), m_rows.end(), index) - m_rows.begin();
0798     }
0799 
0800     /**
0801      * Returns the non-default data at \p index .
0802      * \return the data at \p index .
0803      * \see count()
0804      * \see col()
0805      * \see row()
0806      */
0807     T data(int index) const {
0808         return m_data.value(index);
0809     }
0810 
0811     /**
0812      * The maximum occupied column, i.e. the horizontal storage dimension.
0813      * \return the maximum column
0814      */
0815     int columns() const {
0816         int columns = 0;
0817         for (int c = 0; c < m_cols.count(); ++c)
0818             columns = qMax(m_cols.value(c), columns);
0819         return columns;
0820     }
0821 
0822     /**
0823      * The maximum occupied row, i.e. the vertical storage dimension.
0824      * \return the maximum row
0825      */
0826     int rows() const {
0827         return m_rows.count();
0828     }
0829 
0830     /**
0831      * Creates a substorage consisting of the values in \p region.
0832      * If \p keepOffset is \c true, the values' positions are not altered.
0833      * Otherwise, the upper left of \p region's bounding rect is used as new origin,
0834      * and all positions are adjusted.
0835      * \return a subset of the storage stripped down to the values in \p region
0836      */
0837     PointStorage<T> subStorage(const Region& region, bool keepOffset = true) const {
0838         // Determine the offset.
0839         const QPoint offset = keepOffset ? QPoint(0, 0) : region.boundingRect().topLeft() - QPoint(1, 1);
0840         // this generates an array of values
0841         PointStorage<T> subStorage;
0842         Region::ConstIterator end(region.constEnd());
0843         for (Region::ConstIterator it(region.constBegin()); it != end; ++it) {
0844             const QRect rect = (*it)->rect();
0845             for (int row = rect.top(); row <= rect.bottom() && row <= m_rows.count(); ++row) {
0846                 const QVector<int>::const_iterator cstart(m_cols.begin() + m_rows.value(row - 1));
0847                 const QVector<int>::const_iterator cend((row < m_rows.count()) ? (m_cols.begin() + m_rows.value(row)) : m_cols.end());
0848                 for (QVector<int>::const_iterator cit = cstart; cit != cend; ++cit) {
0849                     if (*cit >= rect.left() && *cit <= rect.right()) {
0850                         if (keepOffset)
0851                             subStorage.insert(*cit, row, m_data.value(cit - m_cols.begin()));
0852                         else
0853                             subStorage.insert(*cit - offset.x(), row - offset.y(), m_data.value(cit - m_cols.begin()));
0854                     }
0855                 }
0856             }
0857         }
0858         return subStorage;
0859     }
0860 
0861     /**
0862      * Equality operator.
0863      */
0864     bool operator==(const PointStorage<T>& o) const {
0865         return (m_rows == o.m_rows && m_cols == o.m_cols && m_data == o.m_data);
0866     }
0867 
0868 private:
0869     void squeezeRows() {
0870         int row = m_rows.count() - 1;
0871         while (m_rows.value(row) == m_data.count() && row >= 0)
0872             m_rows.remove(row--);
0873     }
0874 
0875 private:
0876     QVector<int> m_cols;    // stores the column indices (beginning with one)
0877     QVector<int> m_rows;    // stores the row offsets in m_data
0878     QVector<T>   m_data;    // stores the actual non-default data
0879 
0880 #ifdef KSPREAD_POINT_STORAGE_HASH
0881     QSet<T> m_usedData;
0882 #endif
0883 };
0884 
0885 } // namespace Sheets
0886 } // namespace Calligra
0887 
0888 #endif // CALLIGRA_SHEETS_POINT_STORAGE