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