File indexing completed on 2024-05-12 16:33:36
0001 /* This file is part of the KDE project 0002 0003 Copyright 2010 Johannes Simon <johannes.simon@gmail.com> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Library General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Library General Public License for more details. 0014 0015 You should have received a copy of the GNU Library General Public License 0016 along with this library; see the file COPYING.LIB. If not, write to 0017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 Boston, MA 02110-1301, USA. 0019 */ 0020 0021 #include "TableSource.h" 0022 #include <QAbstractItemModel> 0023 #include <QPointer> 0024 #include <QMap> 0025 #include <QSet> 0026 #include <Qt> 0027 0028 Q_DECLARE_METATYPE(QPointer<QAbstractItemModel>) 0029 0030 using namespace KoChart; 0031 0032 Table::Table(const QString &name, QAbstractItemModel *model) 0033 : m_name(name) 0034 , m_model(model) 0035 { 0036 Q_ASSERT(!name.isEmpty()); 0037 Q_ASSERT(m_model); 0038 } 0039 0040 class TableSource::Private 0041 { 0042 public: 0043 Private(TableSource *parent); 0044 ~Private(); 0045 0046 /** 0047 * Called if a column in the SAM (Sheet Access Model) is changed that 0048 * previously was invalid. 0049 * If both a table name and a valid model pointer are found, the table 0050 * is added and the column is removed from the empty column list. 0051 */ 0052 void updateEmptySamColumn(int col); 0053 0054 /// Pointer to owner of this Private instance 0055 TableSource *const q; 0056 0057 QAbstractItemModel *sheetAccessModel; 0058 0059 /// A list of columns in the sheetAccessModel with an empty table name. 0060 /// Once the name is changed to something non-empty, we'll add the table 0061 /// in this column. This is needed in case first the column is inserted, 0062 /// and then the data is set, e.g. when a QStandardItemModel is used. 0063 QList<int> samEmptyColumns; 0064 0065 /// All tables, with name as unique identifier 0066 QMap<QString, Table*> tablesByName; 0067 /// Redundant (but complete!) list of tables, now with model as UID 0068 QMap<const QAbstractItemModel*, Table*> tablesByModel; 0069 0070 /// Set of Table instances owned by this TableSource. 0071 /// This isn't equivalent to Tables in tablesByName or tablesByModel 0072 /// as a Table isn't deleted when it is removed from this source (the 0073 /// model pointer is just set to null). 0074 QSet<Table*> tables; 0075 }; 0076 0077 TableSource::Private::Private(TableSource *parent) 0078 : q(parent) 0079 , sheetAccessModel(0) 0080 { 0081 } 0082 0083 TableSource::Private::~Private() 0084 { 0085 qDeleteAll(tablesByName.values()); 0086 } 0087 0088 /** 0089 * Retrieves and returns the model of a sheet in @a sheetAccessModel in column @a col 0090 */ 0091 static QAbstractItemModel *getModel(QAbstractItemModel *sheetAccessModel, int col) 0092 { 0093 QModelIndex tableIndex = sheetAccessModel->index(0, col); 0094 QPointer<QAbstractItemModel> table = sheetAccessModel->data(tableIndex).value< QPointer<QAbstractItemModel> >(); 0095 0096 return table.data(); 0097 } 0098 0099 void TableSource::Private::updateEmptySamColumn(int col) 0100 { 0101 // Check for consistency 0102 Q_ASSERT(samEmptyColumns.contains(col)); 0103 0104 QString tableName = sheetAccessModel->headerData(col, Qt::Horizontal).toString(); 0105 QAbstractItemModel *model = getModel(sheetAccessModel, col); 0106 if (tableName.isEmpty() || model == 0) 0107 return; 0108 0109 // Ok. Column is valid now. Add table in this column. 0110 samEmptyColumns.removeAll(col); 0111 q->add(tableName, model); 0112 } 0113 0114 TableSource::TableSource() 0115 : d(new Private(this)) 0116 { 0117 } 0118 0119 TableSource::~TableSource() 0120 { 0121 delete d; 0122 } 0123 0124 Table *TableSource::get(const QString &tableName) const 0125 { 0126 if(!d->tablesByName.contains(tableName)) 0127 return 0; 0128 return d->tablesByName[tableName]; 0129 } 0130 0131 Table *TableSource::get(const QAbstractItemModel *model) const 0132 { 0133 if(!d->tablesByModel.contains(model)) 0134 return 0; 0135 return d->tablesByModel[model]; 0136 } 0137 0138 TableMap TableSource::tableMap() const 0139 { 0140 return d->tablesByName; 0141 } 0142 0143 void TableSource::setSheetAccessModel(QAbstractItemModel *model) 0144 { 0145 // Disconnect slots from signals in old sheetAccessModel 0146 if (d->sheetAccessModel) 0147 d->sheetAccessModel->disconnect(this); 0148 0149 d->sheetAccessModel = model; 0150 0151 if (model) { 0152 connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)), 0153 this, SLOT(samColumnsInserted(QModelIndex,int,int))); 0154 connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), 0155 this, SLOT(samColumnsRemoved(QModelIndex,int,int))); 0156 connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), 0157 this, SLOT(samHeaderDataChanged(Qt::Orientation,int,int))); 0158 0159 // Process existing data 0160 samColumnsInserted(QModelIndex(), 0, model->columnCount() - 1); 0161 } 0162 } 0163 0164 Table *TableSource::add(const QString &name, QAbstractItemModel *model) 0165 { 0166 Q_ASSERT(!d->tablesByName.contains(name)); 0167 Q_ASSERT(!d->tablesByModel.contains(model)); 0168 0169 Table *table = new Table(name, model); 0170 d->tablesByName.insert(name, table); 0171 d->tablesByModel.insert(model, table); 0172 d->tables.insert(table); 0173 0174 emit tableAdded(table); 0175 0176 return table; 0177 } 0178 0179 void TableSource::remove(const QString &name) 0180 { 0181 Q_ASSERT(d->tablesByName.contains(name)); 0182 0183 Table *table = get(name); 0184 if (table) { 0185 d->tablesByName.remove(table->m_name); 0186 d->tablesByModel.remove(table->m_model); 0187 d->tables.remove(table); 0188 emit tableRemoved(table); 0189 // Don't delete the Table instance, it might still be in use. 0190 table->m_model = 0; 0191 } 0192 } 0193 0194 void TableSource::rename(const QString &from, const QString &to) 0195 { 0196 Q_ASSERT(!d->tablesByName.contains(to)); 0197 0198 Table *table = get(from); 0199 if (table) { 0200 d->tablesByName.remove(from); 0201 d->tablesByName.insert(to, table); 0202 table->m_name = to; 0203 } 0204 } 0205 0206 void TableSource::clear() 0207 { 0208 d->tablesByName.clear(); 0209 d->tablesByModel.clear(); 0210 setSheetAccessModel(0); 0211 } 0212 0213 void TableSource::samColumnsInserted(QModelIndex, int first, int last) 0214 { 0215 Q_ASSERT(d->sheetAccessModel); 0216 0217 for (int col = first; col <= last; col++) { 0218 QString tableName = d->sheetAccessModel->headerData(col, Qt::Horizontal).toString(); 0219 QAbstractItemModel *model = getModel(d->sheetAccessModel, col); 0220 if (tableName.isEmpty() || model == 0) 0221 d->samEmptyColumns.append(col); 0222 else 0223 add(tableName, getModel(d->sheetAccessModel, col)); 0224 } 0225 } 0226 0227 void TableSource::samColumnsRemoved(QModelIndex, int first, int last) 0228 { 0229 Q_ASSERT(d->sheetAccessModel); 0230 0231 for (int col = first; col <= last; col++) { 0232 QString tableName = d->sheetAccessModel->headerData(col, Qt::Horizontal).toString(); 0233 remove(tableName); 0234 } 0235 } 0236 0237 void TableSource::samDataChanged(const QModelIndex &first, const QModelIndex &last) 0238 { 0239 // Only the first row contains useful information for us 0240 if (first.row() != 0) 0241 return; 0242 0243 for (int col = first.column(); col <= last.column(); col++) { 0244 // If this column wasn't valid before check if it is now and update 0245 if (d->samEmptyColumns.contains(col)) 0246 d->updateEmptySamColumn(col); 0247 else 0248 Q_ASSERT("Changing the model of an existing table is not supported!"); 0249 } 0250 } 0251 0252 void TableSource::samHeaderDataChanged(Qt::Orientation orientation, int first, int last) 0253 { 0254 // There's no useful information for us in vertical headers 0255 if (orientation == Qt::Vertical) 0256 return; 0257 0258 for (int col = first; col <= last; col++) { 0259 // If this column wasn't valid before check if it is now and update 0260 if (d->samEmptyColumns.contains(col)) { 0261 d->updateEmptySamColumn(col); 0262 continue; 0263 } 0264 0265 QAbstractItemModel *model = getModel(d->sheetAccessModel, col); 0266 Q_ASSERT(model); 0267 Table *table = get(model); 0268 Q_ASSERT(table); 0269 QString newName = d->sheetAccessModel->headerData(col, Qt::Horizontal).toString(); 0270 rename(table->m_name, newName); 0271 } 0272 } 0273 0274