File indexing completed on 2024-04-28 04:50:50

0001 /*
0002  * tablemodel.h
0003  *
0004  * Copyright (C) 2011 Christoph Pfister <christophpfister@gmail.com>
0005  *
0006  * This program is free software; you can redistribute it and/or modify
0007  * it under the terms of the GNU General Public License as published by
0008  * the Free Software Foundation; either version 2 of the License, or
0009  * (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License along
0017  * with this program; if not, write to the Free Software Foundation, Inc.,
0018  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
0019  */
0020 
0021 #ifndef TABLEMODEL_H
0022 #define TABLEMODEL_H
0023 
0024 #include <QAbstractTableModel>
0025 
0026 template<class T> class TableModel : public QAbstractTableModel
0027 {
0028     typedef typename T::ItemType ItemType;
0029     typedef typename T::LessThanType LessThanType;
0030     typedef typename LessThanType::SortOrder SortOrder;
0031 public:
0032     explicit TableModel(QObject *parent) : QAbstractTableModel(parent), updatingRow(-1) { }
0033     ~TableModel() { }
0034 
0035     QModelIndex find(const ItemType &item) const
0036     {
0037         if (item.isValid()) {
0038             int row = binaryFind(item);
0039 
0040             if (row < items.size()) {
0041                 return index(row, 0);
0042             }
0043         }
0044 
0045         return QModelIndex();
0046     }
0047 
0048     const ItemType &value(int row) const
0049     {
0050         if ((row >= 0) && (row < items.size())) {
0051             return items.at(row);
0052         }
0053 
0054         return sharedNull;
0055     }
0056 
0057     const ItemType &value(const QModelIndex &index) const
0058     {
0059         if ((index.row() >= 0) && (index.row() < items.size())) {
0060             return items.at(index.row());
0061         }
0062 
0063         return sharedNull;
0064     }
0065 
0066     int columnCount(const QModelIndex &parent) const override
0067     {
0068         if (!parent.isValid()) {
0069             return helper.columnCount();
0070         }
0071 
0072         return 0;
0073     }
0074 
0075     int rowCount(const QModelIndex &parent) const override
0076     {
0077         if (!parent.isValid()) {
0078             return items.size();
0079         }
0080 
0081         return 0;
0082     }
0083 
0084 protected:
0085     template<class U> void reset(const U &container)
0086     {
0087         beginLayoutChange();
0088         items.clear();
0089 
0090         for (typename U::ConstIterator it = container.constBegin();
0091              it != container.constEnd(); ++it) {
0092             const ItemType &item = *it;
0093 
0094             if (helper.filterAcceptsItem(item)) {
0095                 items.append(item);
0096             }
0097         }
0098 
0099         std::sort(items.begin(), items.end(), lessThan);
0100         endLayoutChange();
0101     }
0102 
0103     template<class U> void resetFromKeys(const U &container)
0104     {
0105         beginLayoutChange();
0106         items.clear();
0107 
0108         for (typename U::ConstIterator it = container.constBegin();
0109              it != container.constEnd(); ++it) {
0110             const ItemType &item = it.key();
0111 
0112             if (helper.filterAcceptsItem(item)) {
0113                 items.append(item);
0114             }
0115         }
0116 
0117         std::sort(items.begin(), items.end(), lessThan);
0118         endLayoutChange();
0119     }
0120 
0121     void insert(const ItemType &item)
0122     {
0123         if (item.isValid() && helper.filterAcceptsItem(item)) {
0124             int row = upperBound(item);
0125             beginInsertRows(QModelIndex(), row, row);
0126             items.insert(row, item);
0127             endInsertRows();
0128         }
0129     }
0130 
0131     void aboutToUpdate(const ItemType &item)
0132     {
0133         updatingRow = -1;
0134 
0135         if (item.isValid()) {
0136             updatingRow = binaryFind(item);
0137         }
0138     }
0139 
0140     void update(const ItemType &item)
0141     {
0142         int row = updatingRow;
0143         updatingRow = -1;
0144 
0145         if ((row >= 0) && (row < items.size())) {
0146             if (item.isValid() && helper.filterAcceptsItem(item)) {
0147                 items.replace(row, item);
0148                 int targetRow = row;
0149 
0150                 while (((targetRow - 1) >= 0) &&
0151                        lessThan(item, items.at(targetRow - 1))) {
0152                     --targetRow;
0153                 }
0154 
0155                 while (((targetRow + 1) < items.size()) &&
0156                        lessThan(items.at(targetRow + 1), item)) {
0157                     ++targetRow;
0158                 }
0159 
0160                 if (row == targetRow) {
0161                     emit dataChanged(index(row, 0),
0162                         index(row, helper.columnCount() - 1));
0163                 } else {
0164                     beginLayoutChange();
0165                     items.move(row, targetRow);
0166                     endLayoutChange();
0167                 }
0168             } else {
0169                 beginRemoveRows(QModelIndex(), row, row);
0170                 items.removeAt(row);
0171                 endRemoveRows();
0172             }
0173         } else {
0174             insert(item);
0175         }
0176     }
0177 
0178     void remove(const ItemType &item)
0179     {
0180         if (item.isValid()) {
0181             int row = binaryFind(item);
0182             beginRemoveRows(QModelIndex(), row, row);
0183             items.removeAt(row);
0184             endRemoveRows();
0185         }
0186     }
0187 
0188     void internalSort(SortOrder sortOrder)
0189     {
0190         if (lessThan.getSortOrder() != sortOrder) {
0191             beginLayoutChange();
0192             lessThan.setSortOrder(sortOrder);
0193             std::sort(items.begin(), items.end(), lessThan);
0194             endLayoutChange();
0195         }
0196     }
0197 
0198 private:
0199     int binaryFind(const ItemType &item) const
0200     {
0201         return (std::lower_bound(items.constBegin(), items.constEnd(), item, lessThan) -
0202             items.constBegin());
0203     }
0204 
0205     int upperBound(const ItemType &item) const
0206     {
0207         return (std::upper_bound(items.constBegin(), items.constEnd(), item, lessThan) -
0208             items.constBegin());
0209     }
0210 
0211     void beginLayoutChange()
0212     {
0213         emit layoutAboutToBeChanged();
0214         oldPersistentIndexes = persistentIndexList();
0215         persistentItems.clear();
0216 
0217         foreach (const QModelIndex &index, oldPersistentIndexes) {
0218             if ((index.row() >= 0) && (index.row() < items.size())) {
0219                 persistentItems.append(items.at(index.row()));
0220             } else {
0221                 persistentItems.append(ItemType());
0222             }
0223         }
0224     }
0225 
0226     void endLayoutChange()
0227     {
0228         QModelIndexList newPersistentIndexes;
0229 
0230         for (int i = 0; i < oldPersistentIndexes.size(); ++i) {
0231             const QModelIndex &oldIndex = oldPersistentIndexes.at(i);
0232             const ItemType &item = persistentItems.at(i);
0233 
0234             if (item.isValid()) {
0235                 int row = binaryFind(item);
0236                 newPersistentIndexes.append(index(row, oldIndex.column()));
0237             } else {
0238                 newPersistentIndexes.append(QModelIndex());
0239             }
0240         }
0241 
0242         changePersistentIndexList(oldPersistentIndexes, newPersistentIndexes);
0243         oldPersistentIndexes.clear();
0244         persistentItems.clear();
0245         emit layoutChanged();
0246     }
0247 
0248 private:
0249     QList<ItemType> items;
0250     LessThanType lessThan;
0251     ItemType sharedNull;
0252     QModelIndexList oldPersistentIndexes;
0253     QList<ItemType> persistentItems;
0254     int updatingRow;
0255 
0256 protected:
0257     T helper;
0258 };
0259 
0260 #endif /* TABLEMODEL_H */