File indexing completed on 2024-04-21 15:03:00

0001 /***************************************************************************
0002  * actioncollectionmodel.cpp
0003  * This file is part of the KDE project
0004  * copyright (C) 2006-2007 by Sebastian Sauer (mail@dipe.org)
0005  *
0006  * This program is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU Library General Public
0008  * License as published by the Free Software Foundation; either
0009  * version 2 of the License, or (at your option) any later version.
0010  * This program 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  * You should have received a copy of the GNU Library General Public License
0015  * along with this program; see the file COPYING.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  ***************************************************************************/
0019 
0020 #include "actioncollectionmodel.h"
0021 #include "krossui_debug.h"
0022 
0023 #include <kross/core/action.h>
0024 #include <kross/core/actioncollection.h>
0025 #include <kross/core/manager.h>
0026 
0027 #include <klocalizedstring.h>
0028 #include <QDebug>
0029 
0030 #include <QEvent>
0031 #include <QMimeData>
0032 #include <QPointer>
0033 
0034 using namespace Kross;
0035 
0036 /******************************************************************************
0037  * ActionCollectionModel
0038  */
0039 
0040 namespace Kross
0041 {
0042 
0043 /// \internal d-pointer class.
0044 class ActionCollectionModel::Private
0045 {
0046 public:
0047     QPointer<ActionCollection> collection;
0048     Mode mode;
0049 };
0050 
0051 }
0052 
0053 ActionCollectionModel::ActionCollectionModel(QObject *parent, ActionCollection *collection, Mode mode)
0054     : QAbstractItemModel(parent)
0055     , d(new Private())
0056 {
0057     //krossdebug( QString( "ActionCollectionModel::ActionCollectionModel:") );
0058     d->collection = collection ? collection : Kross::Manager::self().actionCollection();
0059     d->mode = mode;
0060     //setSupportedDragActions(Qt::MoveAction);
0061 
0062     //ActionCollection propagates signals to parent
0063     QObject::connect(d->collection, SIGNAL(dataChanged(Action*)), this, SLOT(slotDataChanged(Action*)));
0064     QObject::connect(d->collection, SIGNAL(dataChanged(ActionCollection*)), this, SLOT(slotDataChanged(ActionCollection*)));
0065 
0066     QObject::connect(d->collection, SIGNAL(collectionToBeInserted(ActionCollection*,ActionCollection*)), this, SLOT(slotCollectionToBeInserted(ActionCollection*,ActionCollection*)));
0067     QObject::connect(d->collection, SIGNAL(collectionInserted(ActionCollection*,ActionCollection*)), this, SLOT(slotCollectionInserted(ActionCollection*,ActionCollection*)));
0068     QObject::connect(d->collection, SIGNAL(collectionToBeRemoved(ActionCollection*,ActionCollection*)), this, SLOT(slotCollectionToBeRemoved(ActionCollection*,ActionCollection*)));
0069     QObject::connect(d->collection, SIGNAL(collectionRemoved(ActionCollection*,ActionCollection*)), this, SLOT(slotCollectionRemoved(ActionCollection*,ActionCollection*)));
0070 
0071     QObject::connect(d->collection, SIGNAL(actionToBeInserted(Action*,ActionCollection*)), this, SLOT(slotActionToBeInserted(Action*,ActionCollection*)));
0072     QObject::connect(d->collection, SIGNAL(actionInserted(Action*,ActionCollection*)), this, SLOT(slotActionInserted(Action*,ActionCollection*)));
0073     QObject::connect(d->collection, SIGNAL(actionToBeRemoved(Action*,ActionCollection*)), this, SLOT(slotActionToBeRemoved(Action*,ActionCollection*)));
0074     QObject::connect(d->collection, SIGNAL(actionRemoved(Action*,ActionCollection*)), this, SLOT(slotActionRemoved(Action*,ActionCollection*)));
0075 }
0076 
0077 ActionCollectionModel::~ActionCollectionModel()
0078 {
0079     delete d;
0080 }
0081 
0082 ActionCollection *ActionCollectionModel::rootCollection() const
0083 {
0084     return d->collection;
0085 }
0086 
0087 int ActionCollectionModel::rowNumber(ActionCollection *collection) const
0088 {
0089     Q_ASSERT(collection != nullptr);
0090     ActionCollection *par = collection->parentCollection();
0091     Q_ASSERT(par != nullptr);
0092     int row = par->collections().indexOf(collection->objectName()) + par->actions().count();
0093     return row;
0094 }
0095 
0096 QModelIndex ActionCollectionModel::indexForCollection(ActionCollection *collection) const
0097 {
0098     if (collection == d->collection) {
0099         return QModelIndex();
0100     }
0101     return createIndex(rowNumber(collection), 0, collection->parentCollection());
0102 }
0103 
0104 QModelIndex ActionCollectionModel::indexForAction(Action *act) const
0105 {
0106     ActionCollection *coll = static_cast<ActionCollection *>(act->parent());
0107     return createIndex(coll->actions().indexOf(act), 0, coll);
0108 }
0109 
0110 void ActionCollectionModel::slotCollectionToBeInserted(ActionCollection *child, ActionCollection *parent)
0111 {
0112     //krossdebug( QString( "ActionCollectionModel::slotCollectionToBeInserted: %1 %2" ).arg( child->name() ).arg( parent->name( ) )  );
0113     Q_ASSERT(parent);
0114     Q_UNUSED(child)
0115     int row = parent->actions().count() + parent->collections().count(); // we assume child is appended!!
0116     QModelIndex parIdx = indexForCollection(parent);
0117     beginInsertRows(parIdx, row, row);
0118 }
0119 
0120 void ActionCollectionModel::slotCollectionInserted(ActionCollection *, ActionCollection *)
0121 {
0122     //krossdebug( QString( "ActionCollectionModel::slotCollectionInserted: %1 %2" ).arg( child->name( ) ).arg( parent->name( ) )  );
0123     endInsertRows();
0124 }
0125 
0126 void ActionCollectionModel::slotCollectionToBeRemoved(ActionCollection *child, ActionCollection *parent)
0127 {
0128     //krossdebug( QString( "ActionCollectionModel::slotCollectionToBeRemoved: %1 %2" ).arg( child->name() ).arg( parent->name() ) );
0129     int row = rowNumber(child);
0130     QModelIndex parIdx = indexForCollection(parent);
0131     beginRemoveRows(parIdx, row, row);
0132 }
0133 
0134 void ActionCollectionModel::slotCollectionRemoved(ActionCollection *, ActionCollection *)
0135 {
0136     //krossdebug( QString( "ActionCollectionModel::slotCollectionRemoved: %1 %2" ).arg( child->name() ).arg( parent->name() ) );
0137     endRemoveRows();
0138 }
0139 
0140 void ActionCollectionModel::slotActionToBeInserted(Action *child, ActionCollection *parent)
0141 {
0142     //krossdebug( QString( "ActionCollectionModel::slotActionInserted: %1 %2" ).arg( child->name() ).arg( parent->name() ) );
0143     Q_ASSERT(parent);
0144     Q_UNUSED(child)
0145     int row = parent->actions().count(); // assume child is appended to actions!!
0146     QModelIndex parIdx = indexForCollection(parent);
0147     beginInsertRows(parIdx, row, row);
0148 }
0149 
0150 void ActionCollectionModel::slotActionInserted(Action *, ActionCollection *)
0151 {
0152     //krossdebug( QString( "ActionCollectionModel::slotActionInserted: %1 %2" ).arg( child->name() ).arg( parent->name() ) );
0153     endInsertRows();
0154 }
0155 
0156 void ActionCollectionModel::slotActionToBeRemoved(Action *child, ActionCollection *parent)
0157 {
0158     //krossdebug( QString( "ActionCollectionModel::slotActionToBeRemoved: %1 %2" ).arg( child->name() ).arg( parent->name() ) );
0159     Q_ASSERT(parent);
0160     int row = parent->actions().indexOf(child);
0161     QModelIndex parIdx = indexForCollection(parent);
0162     beginRemoveRows(parIdx, row, row);
0163 }
0164 
0165 void ActionCollectionModel::slotActionRemoved(Action *, ActionCollection *)
0166 {
0167     //krossdebug( QString( "ActionCollectionModel::slotActionRemoved: %1 %2" ).arg( child->name() ).arg( parent->name() ) );
0168     endRemoveRows();
0169 }
0170 
0171 //NOTE: not used anymore, remove?
0172 void ActionCollectionModel::slotUpdated()
0173 {
0174     //emit layoutAboutToBeChanged();
0175     //emit layoutChanged();
0176 }
0177 
0178 void ActionCollectionModel::slotDataChanged(ActionCollection *coll)
0179 {
0180     //krossdebug( QString( "ActionCollectionModel::slotDataChanged: %1" ).arg( coll->name() ) );
0181     QModelIndex idx = indexForCollection(coll);
0182     emit dataChanged(idx, idx);   // NOTE: change if more than one column
0183 }
0184 
0185 void ActionCollectionModel::slotDataChanged(Action *act)
0186 {
0187     //krossdebug( QString( "ActionCollectionModel::slotDataChanged: %1" ).arg( act->name() ) );
0188     QModelIndex idx = indexForAction(act);
0189     emit dataChanged(idx, idx);   // NOTE: change if more than one column
0190 }
0191 
0192 Action *ActionCollectionModel::action(const QModelIndex &index)
0193 {
0194     ActionCollection *par = static_cast<ActionCollection *>(index.internalPointer());
0195     if (par == nullptr || index.row() >= par->actions().count()) {
0196         return nullptr;
0197     }
0198     return par->actions().value(index.row());
0199 }
0200 
0201 ActionCollection *ActionCollectionModel::collection(const QModelIndex &index)
0202 {
0203     ActionCollection *par = static_cast<ActionCollection *>(index.internalPointer());
0204     if (par == nullptr) {
0205         return nullptr;
0206     }
0207     int row = index.row() - par->actions().count();
0208     if (row < 0) {
0209         return nullptr; // this is probably an action
0210     }
0211     return par->collection(par->collections().value(row));
0212 }
0213 
0214 int ActionCollectionModel::columnCount(const QModelIndex &) const
0215 {
0216     return 1;
0217 }
0218 
0219 int ActionCollectionModel::rowCount(const QModelIndex &index) const
0220 {
0221     if (action(index)) {
0222         return 0;
0223     }
0224     ActionCollection *par = index.isValid() ? collection(index) : d->collection.data();
0225     Q_ASSERT_X(par, "ActionCollectionModel::rowCount", "index is not an action nor a collection");
0226     if (!par) {
0227         qWarning() << "index is not an action nor a collection" << index;
0228         return 0;
0229     }
0230     int rows = par->actions().count() + par->collections().count();
0231     return rows;
0232 }
0233 
0234 QModelIndex ActionCollectionModel::index(int row, int column, const QModelIndex &parent) const
0235 {
0236     if (! hasIndex(row, column, parent)) {
0237         return QModelIndex();
0238     }
0239     ActionCollection *par = parent.isValid() ? collection(parent) : d->collection.data();
0240     if (par == nullptr) {
0241         // safety: may happen if parent index is an action (ModelTest tests this)
0242         return QModelIndex();
0243     }
0244     return createIndex(row, column, par);
0245 }
0246 
0247 QModelIndex ActionCollectionModel::parent(const QModelIndex &index) const
0248 {
0249     if (! index.isValid()) {
0250         return QModelIndex();
0251     }
0252     ActionCollection *par = static_cast<ActionCollection *>(index.internalPointer());
0253     Q_ASSERT(par != nullptr);
0254     if (par == d->collection) {
0255         return QModelIndex();
0256     }
0257     return createIndex(rowNumber(par), 0, par->parentCollection());
0258 }
0259 
0260 Qt::ItemFlags ActionCollectionModel::flags(const QModelIndex &index) const
0261 {
0262     Qt::ItemFlags flags = QAbstractItemModel::flags(index);
0263     if (! index.isValid()) {
0264         return Qt::ItemIsDropEnabled | flags;
0265     }
0266 
0267     flags |= Qt::ItemIsSelectable;
0268     //flags |= Qt::ItemIsEditable;
0269     flags |= Qt::ItemIsDragEnabled;
0270     flags |= Qt::ItemIsDropEnabled;
0271 
0272     if ((index.column() == 0) && (d->mode & UserCheckable)) {
0273         flags |= Qt::ItemIsUserCheckable;
0274     }
0275     return flags;
0276 }
0277 
0278 QVariant ActionCollectionModel::data(const QModelIndex &index, int role) const
0279 {
0280     if (index.isValid()) {
0281         Action *act = action(index);
0282         if (act) {
0283             switch (role) {
0284             case Qt::DecorationRole: {
0285                 if (d->mode & Icons)
0286                     if (! act->iconName().isEmpty()) {
0287                         return act->icon();
0288                     }
0289             } break;
0290             case Qt::DisplayRole:
0291                 return KLocalizedString::removeAcceleratorMarker(act->text());
0292             case Qt::ToolTipRole: // fall through
0293             case Qt::WhatsThisRole: {
0294                 if (d->mode & ToolTips) {
0295                     const QString file = QFileInfo(act->file()).fileName();
0296                     return QString("<qt><b>%1</b><br>%2</qt>")
0297                            .arg(file.isEmpty() ? act->name() : file)
0298                            .arg(act->description());
0299                 }
0300             } break;
0301             case Qt::CheckStateRole: {
0302                 if (d->mode & UserCheckable) {
0303                     return act->isEnabled() ? Qt::Checked : Qt::Unchecked;
0304                 }
0305             } break;
0306             default: break;
0307             }
0308             return QVariant();
0309         }
0310         ActionCollection *coll = collection(index);
0311         if (coll) {
0312             switch (role) {
0313             case Qt::DecorationRole: {
0314                 if (d->mode & Icons)
0315                     if (! coll->iconName().isEmpty()) {
0316                         return coll->icon();
0317                     }
0318             } break;
0319             case Qt::DisplayRole:
0320                 return coll->text();
0321             case Qt::ToolTipRole: // fall through
0322             case Qt::WhatsThisRole: {
0323                 if (d->mode & ToolTips) {
0324                     return QString("<qt><b>%1</b><br>%2</qt>").arg(coll->text()).arg(coll->description());
0325                 }
0326             } break;
0327             case Qt::CheckStateRole: {
0328                 if (d->mode & UserCheckable) {
0329                     return coll->isEnabled() ? Qt::Checked : Qt::Unchecked;
0330                 }
0331             } break;
0332             default: break;
0333             }
0334             return QVariant();
0335         }
0336     }
0337     return QVariant();
0338 }
0339 
0340 bool ActionCollectionModel::setData(const QModelIndex &index, const QVariant &value, int role)
0341 {
0342     Q_UNUSED(value);
0343     if (! index.isValid() /*|| ! (d->mode & UserCheckable)*/) {
0344         return false;
0345     }
0346 
0347     Action *act = action(index);
0348     if (act) {
0349         switch (role) {
0350         //case Qt::EditRole: act->setText( value.toString() ); break;
0351         case Qt::CheckStateRole: act->setEnabled(! act->isEnabled()); break;
0352         default: return false;
0353         }
0354         return false;
0355     }
0356     ActionCollection *coll = collection(index);
0357     if (coll) {
0358         switch (role) {
0359         //case Qt::EditRole: item->coll->setText( value.toString() ); break;
0360         case Qt::CheckStateRole: coll->setEnabled(! coll->isEnabled()); break;
0361         default: return false;
0362         }
0363         return false;
0364     }
0365     //emit dataChanged(index, index);
0366     return true;
0367 }
0368 
0369 bool ActionCollectionModel::insertRows(int row, int count, const QModelIndex &parent)
0370 {
0371     qCDebug(KROSS_UI_LOG) << "ActionCollectionModel::insertRows: row=" << row << " count=" << count;
0372     if (! parent.isValid()) {
0373         return false;
0374     }
0375 
0376     ActionCollection *coll = collection(parent);
0377     if (coll) {
0378         qCDebug(KROSS_UI_LOG) << "ActionCollectionModel::insertRows: parentindex is ActionCollection with name=" << coll->name();
0379     } else {
0380         Action *act = action(parent);
0381         if (act) {
0382             qCDebug(KROSS_UI_LOG) << "ActionCollectionModel::insertRows: parentindex is Action with name=" << act->name();
0383         }
0384     }
0385     return QAbstractItemModel::insertRows(row, count, parent);
0386 }
0387 
0388 bool ActionCollectionModel::removeRows(int row, int count, const QModelIndex &parent)
0389 {
0390     qCDebug(KROSS_UI_LOG) << "ActionCollectionModel::removeRows: row=" << row << " count=" << count;
0391     return QAbstractItemModel::removeRows(row, count, parent);
0392 }
0393 
0394 bool ActionCollectionModel::insertColumns(int column, int count, const QModelIndex &parent)
0395 {
0396     qCDebug(KROSS_UI_LOG) << "ActionCollectionModel::insertColumns: column=" << column << " count=" << count;
0397     return QAbstractItemModel::insertColumns(column, count, parent);
0398 }
0399 
0400 bool ActionCollectionModel::removeColumns(int column, int count, const QModelIndex &parent)
0401 {
0402     qCDebug(KROSS_UI_LOG) << "ActionCollectionModel::removeColumns: column=" << column << " count=" << count;
0403     return QAbstractItemModel::removeColumns(column, count, parent);
0404 }
0405 
0406 QStringList ActionCollectionModel::mimeTypes() const
0407 {
0408     //krossdebug( QString("ActionCollectionModel::mimeTypes") );
0409     return QStringList() << "application/vnd.text.list";
0410 }
0411 
0412 QString fullPath(const QModelIndex &index)
0413 {
0414     if (! index.isValid()) {
0415         return QString();
0416     }
0417     QString n;
0418     Action *a = ActionCollectionModel::action(index);
0419     if (a) {
0420         n = a->name();
0421     } else {
0422         ActionCollection *c = ActionCollectionModel::collection(index);
0423         if (c) {
0424             n = c->name() + '/';
0425             if (! n.endsWith('/')) {
0426                 n += '/';
0427             }
0428         }
0429     }
0430     ActionCollection *par = static_cast<ActionCollection *>(index.internalPointer());
0431     for (ActionCollection *p = par; p != nullptr; p = par->parentCollection()) {
0432         QString s = p->name();
0433         if (! s.endsWith('/')) {
0434             s += '/';
0435         }
0436         n = s + n;
0437     }
0438     return n;
0439 }
0440 
0441 QMimeData *ActionCollectionModel::mimeData(const QModelIndexList &indexes) const
0442 {
0443     //krossdebug( QString("ActionCollectionModel::mimeData") );
0444     QMimeData *mimeData = new QMimeData();
0445     QByteArray encodedData;
0446 
0447     QDataStream stream(&encodedData, QIODevice::WriteOnly);
0448     foreach (const QModelIndex &index, indexes) {
0449         //if( ! index.isValid() ) continue;
0450         //QString text = data(index, Qt::DisplayRole).toString();
0451         QString path = fullPath(index);
0452         if (! path.isNull()) {
0453             stream << path;
0454         }
0455     }
0456 
0457     mimeData->setData("application/vnd.text.list", encodedData);
0458     return mimeData;
0459 }
0460 
0461 bool ActionCollectionModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
0462 {
0463     qCDebug(KROSS_UI_LOG) << "ActionCollectionModel::dropMimeData: row=" << row << " col=" << column;
0464     if (action == Qt::IgnoreAction) {
0465         return true;
0466     }
0467     if (! data->hasFormat("application/vnd.text.list")) {
0468         return false;
0469     }
0470     if (column > 0) {
0471         return false;
0472     }
0473 
0474     qCDebug(KROSS_UI_LOG) << "ActionCollectionModel::dropMimeData: ENCODED DATA:";
0475     QByteArray encodedData = data->data("application/vnd.text.list");
0476     QDataStream stream(&encodedData, QIODevice::ReadOnly);
0477     QStringList newItems;
0478     int rows = 0;
0479     while (! stream.atEnd()) {
0480         QString text;
0481         stream >> text;
0482         newItems << text;
0483         qCDebug(KROSS_UI_LOG) << QString("  %1 \"%2\"").arg(rows).arg(text);
0484         ++rows;
0485     }
0486 
0487     //FIXME: return false for now since insertRows/removeRows need to be implemented before!
0488     //return false;
0489 
0490     /*
0491     int beginRow;
0492     if( row != -1 )
0493         beginRow = row;
0494     else if( parent.isValid() )
0495         beginRow = parent.row();
0496     else
0497         beginRow = rowCount( QModelIndex() );
0498     krossdebug( QString("ActionCollectionModel::dropMimeData: beginRow=%1").arg(beginRow) );
0499     */
0500 
0501     QModelIndex targetindex = index(row, column, parent);
0502     ActionCollection *coll = collection(targetindex);
0503     if (coll) {
0504         qCDebug(KROSS_UI_LOG) << "ActionCollectionModel::dropMimeData: parentindex is ActionCollection with name=" << coll->name();
0505     } else {
0506         Action *act = this->action(targetindex);
0507         if (act) {
0508             qCDebug(KROSS_UI_LOG) << "ActionCollectionModel::dropMimeData: parentindex is Action with name=" << act->name();
0509         }
0510     }
0511     return false;
0512     //return QAbstractItemModel::dropMimeData(data, action, row, column, parent);
0513 }
0514 
0515 Qt::DropActions ActionCollectionModel::supportedDropActions() const
0516 {
0517     return Qt::CopyAction | Qt::MoveAction | Qt::TargetMoveAction;
0518     //return Qt::CopyAction | Qt::MoveAction | Qt::TargetMoveAction | Qt::LinkAction;
0519 }
0520 
0521 /******************************************************************************
0522  * ActionCollectionProxyModel
0523  */
0524 
0525 ActionCollectionProxyModel::ActionCollectionProxyModel(QObject *parent, ActionCollectionModel *model)
0526     : QSortFilterProxyModel(parent)
0527 {
0528     setSourceModel(model ? model : new ActionCollectionModel(this));
0529     setFilterCaseSensitivity(Qt::CaseInsensitive);
0530     setDynamicSortFilter(true);
0531 }
0532 
0533 ActionCollectionProxyModel::~ActionCollectionProxyModel()
0534 {
0535 }
0536 
0537 void ActionCollectionProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
0538 {
0539     Q_ASSERT(dynamic_cast< ActionCollectionModel * >(sourceModel));
0540     QSortFilterProxyModel::setSourceModel(sourceModel);
0541 }
0542 
0543 bool ActionCollectionProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
0544 {
0545     //krossdebug( QString( "ActionCollectionProxyModel::filterAcceptsRow: row=%1 parentrow=%2" ).arg( source_row ).arg( source_parent.row() ) );
0546     QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
0547     if (! index.isValid()) {
0548         return false;
0549     }
0550 
0551     Action *action = ActionCollectionModel::action(index);
0552     if (action) {
0553         return action->isEnabled() && QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
0554     }
0555     ActionCollection *collection = ActionCollectionModel::collection(index);
0556     if (collection) {
0557         return collection->isEnabled();
0558     }
0559     return true;
0560 }
0561 
0562 #include "moc_actioncollectionmodel.cpp"