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"