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