File indexing completed on 2024-05-12 16:01:36
0001 /* 0002 * SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org> 0003 * SPDX-FileCopyrightText: 2008 Cyrille Berger <cberger@cberger.net> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 #include "kis_node_model.h" 0008 0009 #include <iostream> 0010 0011 #include <QMimeData> 0012 #include <QBuffer> 0013 #include <QPointer> 0014 0015 #include <KoColorSpaceConstants.h> 0016 0017 #include <klocalizedstring.h> 0018 0019 #include "kis_mimedata.h" 0020 #include <kis_debug.h> 0021 #include <kis_node.h> 0022 #include <kis_node_progress_proxy.h> 0023 #include <kis_image.h> 0024 #include <kis_selection.h> 0025 #include <kis_selection_mask.h> 0026 #include <kis_undo_adapter.h> 0027 #include <commands/kis_node_property_list_command.h> 0028 #include <kis_paint_layer.h> 0029 #include <kis_group_layer.h> 0030 #include <kis_projection_leaf.h> 0031 #include <kis_shape_controller.h> 0032 0033 #include "kis_dummies_facade_base.h" 0034 #include "kis_node_dummies_graph.h" 0035 #include "kis_model_index_converter.h" 0036 #include "kis_model_index_converter_show_all.h" 0037 #include "kis_node_selection_adapter.h" 0038 #include "kis_node_insertion_adapter.h" 0039 #include "kis_node_manager.h" 0040 #include <KisSelectionActionsAdapter.h> 0041 #include <KisNodeDisplayModeAdapter.h> 0042 0043 #include "kis_config.h" 0044 #include "kis_config_notifier.h" 0045 #include "kis_signal_auto_connection.h" 0046 #include "kis_signal_compressor.h" 0047 0048 0049 struct KisNodeModel::Private 0050 { 0051 Private() : updateCompressor(100, KisSignalCompressor::FIRST_ACTIVE) {} 0052 0053 KisImageWSP image; 0054 KisShapeController *shapeController = 0; 0055 KisNodeSelectionAdapter *nodeSelectionAdapter = 0; 0056 KisNodeInsertionAdapter *nodeInsertionAdapter = 0; 0057 KisSelectionActionsAdapter *selectionActionsAdapter = 0; 0058 KisNodeDisplayModeAdapter *nodeDisplayModeAdapter = 0; 0059 KisNodeManager *nodeManager = 0; 0060 0061 KisSignalAutoConnectionsStore nodeDisplayModeAdapterConnections; 0062 0063 QList<KisNodeDummy*> updateQueue; 0064 KisSignalCompressor updateCompressor; 0065 0066 KisModelIndexConverterBase *indexConverter = 0; 0067 QPointer<KisDummiesFacadeBase> dummiesFacade = 0; 0068 bool needFinishRemoveRows = false; 0069 bool needFinishInsertRows = false; 0070 bool showRootLayer = false; 0071 bool showGlobalSelection = false; 0072 int dummyColumns {0}; 0073 QPersistentModelIndex activeNodeIndex; 0074 0075 QPointer<KisNodeDummy> parentOfRemovedNode = 0; 0076 0077 QSet<quintptr> dropEnabled; 0078 }; 0079 0080 KisNodeModel::KisNodeModel(QObject * parent, int clonedColumns) 0081 : QAbstractItemModel(parent) 0082 , m_d(new Private) 0083 { 0084 m_d->dummyColumns = qMax(0, clonedColumns); 0085 connect(&m_d->updateCompressor, SIGNAL(timeout()), SLOT(processUpdateQueue())); 0086 } 0087 0088 KisNodeModel::~KisNodeModel() 0089 { 0090 delete m_d->indexConverter; 0091 delete m_d; 0092 } 0093 0094 KisNodeSP KisNodeModel::nodeFromIndex(const QModelIndex &index) const 0095 { 0096 Q_ASSERT(index.isValid()); 0097 0098 KisNodeDummy *dummy = m_d->indexConverter->dummyFromIndex(index); 0099 if (dummy) { 0100 return dummy->node(); 0101 } 0102 return 0; 0103 } 0104 0105 QModelIndex KisNodeModel::indexFromNode(KisNodeSP node) const 0106 { 0107 KisNodeDummy *dummy = m_d->dummiesFacade->dummyForNode(node); 0108 if(dummy) 0109 return m_d->indexConverter->indexFromDummy(dummy); 0110 return QModelIndex(); 0111 } 0112 0113 bool KisNodeModel::belongsToIsolatedGroup(KisImageSP image, KisNodeSP node, KisDummiesFacadeBase *dummiesFacade) 0114 { 0115 KisNodeSP isolatedRoot = image->isolationRootNode(); 0116 if (!isolatedRoot) return true; 0117 0118 KisNodeDummy *isolatedRootDummy = 0119 dummiesFacade->dummyForNode(isolatedRoot); 0120 KisNodeDummy *dummy = 0121 dummiesFacade->dummyForNode(node); 0122 0123 while (dummy) { 0124 if (dummy == isolatedRootDummy) { 0125 return true; 0126 } 0127 dummy = dummy->parent(); 0128 } 0129 0130 return false; 0131 } 0132 0133 bool KisNodeModel::belongsToIsolatedGroup(KisNodeSP node) const 0134 { 0135 return belongsToIsolatedGroup(m_d->image, node, m_d->dummiesFacade); 0136 } 0137 0138 void KisNodeModel::resetIndexConverter() 0139 { 0140 delete m_d->indexConverter; 0141 m_d->indexConverter = 0; 0142 0143 if(m_d->dummiesFacade) { 0144 m_d->indexConverter = createIndexConverter(); 0145 } 0146 } 0147 0148 KisModelIndexConverterBase *KisNodeModel::createIndexConverter() 0149 { 0150 if(m_d->showRootLayer) { 0151 return new KisModelIndexConverterShowAll(m_d->dummiesFacade, this); 0152 } else { 0153 return new KisModelIndexConverter(m_d->dummiesFacade, this, m_d->showGlobalSelection); 0154 } 0155 } 0156 0157 void KisNodeModel::regenerateItems(KisNodeDummy *dummy) 0158 { 0159 const QModelIndex &index = m_d->indexConverter->indexFromDummy(dummy); 0160 emit dataChanged(index.siblingAtColumn(0), index.siblingAtColumn(m_d->dummyColumns)); 0161 0162 dummy = dummy->firstChild(); 0163 while (dummy) { 0164 regenerateItems(dummy); 0165 dummy = dummy->nextSibling(); 0166 } 0167 } 0168 0169 void KisNodeModel::slotIsolatedModeChanged() 0170 { 0171 regenerateItems(m_d->dummiesFacade->rootDummy()); 0172 } 0173 0174 bool KisNodeModel::showGlobalSelection() const 0175 { 0176 return m_d->nodeDisplayModeAdapter ? 0177 m_d->nodeDisplayModeAdapter->showGlobalSelectionMask() : 0178 false; 0179 } 0180 0181 void KisNodeModel::setShowGlobalSelection(bool value) 0182 { 0183 if (m_d->nodeDisplayModeAdapter) { 0184 m_d->nodeDisplayModeAdapter->setShowGlobalSelectionMask(value); 0185 } 0186 } 0187 0188 void KisNodeModel::slotNodeDisplayModeChanged(bool showRootNode, bool showGlobalSelectionMask) 0189 { 0190 const bool oldShowRootLayer = m_d->showRootLayer; 0191 const bool oldShowGlobalSelection = m_d->showGlobalSelection; 0192 m_d->showRootLayer = showRootNode; 0193 m_d->showGlobalSelection = showGlobalSelectionMask; 0194 0195 if (m_d->showRootLayer != oldShowRootLayer || m_d->showGlobalSelection != oldShowGlobalSelection) { 0196 resetIndexConverter(); 0197 beginResetModel(); 0198 endResetModel(); 0199 } 0200 } 0201 0202 void KisNodeModel::progressPercentageChanged(int, const KisNodeSP node) 0203 { 0204 if(!m_d->dummiesFacade) return; 0205 0206 // Need to check here as the node might already be removed, but there might 0207 // still be some signals arriving from another thread 0208 if (m_d->dummiesFacade->hasDummyForNode(node)) { 0209 QModelIndex index = indexFromNode(node); 0210 0211 emit dataChanged(index, index); 0212 } 0213 } 0214 0215 KisModelIndexConverterBase * KisNodeModel::indexConverter() const 0216 { 0217 return m_d->indexConverter; 0218 } 0219 0220 KisDummiesFacadeBase *KisNodeModel::dummiesFacade() const 0221 { 0222 return m_d->dummiesFacade; 0223 } 0224 0225 void KisNodeModel::connectDummy(KisNodeDummy *dummy, bool needConnect) 0226 { 0227 KisNodeSP node = dummy->node(); 0228 if (!node) { 0229 qWarning() << "Dummy node has no node!" << dummy << dummy->node(); 0230 return; 0231 } 0232 KisNodeProgressProxy *progressProxy = node->nodeProgressProxy(); 0233 if(progressProxy) { 0234 if(needConnect) { 0235 connect(progressProxy, SIGNAL(percentageChanged(int,KisNodeSP)), 0236 SLOT(progressPercentageChanged(int,KisNodeSP))); 0237 } else { 0238 progressProxy->disconnect(this); 0239 } 0240 } 0241 } 0242 0243 void KisNodeModel::connectDummies(KisNodeDummy *dummy, bool needConnect) 0244 { 0245 connectDummy(dummy, needConnect); 0246 0247 dummy = dummy->firstChild(); 0248 while(dummy) { 0249 connectDummies(dummy, needConnect); 0250 dummy = dummy->nextSibling(); 0251 } 0252 } 0253 0254 void KisNodeModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, 0255 KisImageWSP image, 0256 KisShapeController *shapeController, 0257 KisSelectionActionsAdapter *selectionActionsAdapter, 0258 KisNodeManager *nodeManager) 0259 { 0260 QPointer<KisDummiesFacadeBase> oldDummiesFacade(m_d->dummiesFacade); 0261 KisShapeController *oldShapeController = m_d->shapeController; 0262 0263 m_d->shapeController = shapeController; 0264 m_d->nodeManager = nodeManager; 0265 m_d->nodeSelectionAdapter = nodeManager ? nodeManager->nodeSelectionAdapter() : nullptr; 0266 m_d->nodeInsertionAdapter = nodeManager ? nodeManager->nodeInsertionAdapter() : nullptr; 0267 m_d->selectionActionsAdapter = selectionActionsAdapter; 0268 0269 m_d->nodeDisplayModeAdapterConnections.clear(); 0270 m_d->nodeDisplayModeAdapter = nodeManager ? nodeManager->nodeDisplayModeAdapter() : nullptr; 0271 if (m_d->nodeDisplayModeAdapter) { 0272 m_d->nodeDisplayModeAdapterConnections.addConnection( 0273 m_d->nodeDisplayModeAdapter, SIGNAL(sigNodeDisplayModeChanged(bool,bool)), 0274 this, SLOT(slotNodeDisplayModeChanged(bool,bool))); 0275 0276 // cold initialization 0277 m_d->showGlobalSelection = m_d->nodeDisplayModeAdapter->showGlobalSelectionMask(); 0278 m_d->showRootLayer = false; 0279 } 0280 0281 if (oldDummiesFacade && m_d->image) { 0282 m_d->image->disconnect(this); 0283 oldDummiesFacade->disconnect(this); 0284 connectDummies(m_d->dummiesFacade->rootDummy(), false); 0285 } 0286 0287 m_d->image = image; 0288 m_d->dummiesFacade = dummiesFacade; 0289 m_d->parentOfRemovedNode = 0; 0290 resetIndexConverter(); 0291 0292 if (m_d->dummiesFacade) { 0293 KisNodeDummy *rootDummy = m_d->dummiesFacade->rootDummy(); 0294 if (rootDummy) { 0295 connectDummies(rootDummy, true); 0296 } 0297 0298 connect(m_d->dummiesFacade, SIGNAL(sigBeginInsertDummy(KisNodeDummy*,int,QString)), 0299 SLOT(slotBeginInsertDummy(KisNodeDummy*,int,QString))); 0300 connect(m_d->dummiesFacade, SIGNAL(sigEndInsertDummy(KisNodeDummy*)), 0301 SLOT(slotEndInsertDummy(KisNodeDummy*))); 0302 connect(m_d->dummiesFacade, SIGNAL(sigBeginRemoveDummy(KisNodeDummy*)), 0303 SLOT(slotBeginRemoveDummy(KisNodeDummy*))); 0304 connect(m_d->dummiesFacade, SIGNAL(sigEndRemoveDummy()), 0305 SLOT(slotEndRemoveDummy())); 0306 0307 connect(m_d->dummiesFacade, SIGNAL(sigDummyChanged(KisNodeDummy*)), 0308 SLOT(slotDummyChanged(KisNodeDummy*))); 0309 0310 if (m_d->image.isValid()) { 0311 connect(m_d->image, SIGNAL(sigIsolatedModeChanged()), SLOT(slotIsolatedModeChanged())); 0312 } 0313 } 0314 0315 if (m_d->dummiesFacade != oldDummiesFacade || m_d->shapeController != oldShapeController) { 0316 beginResetModel(); 0317 endResetModel(); 0318 } 0319 } 0320 0321 void KisNodeModel::slotBeginInsertDummy(KisNodeDummy *parent, int index, const QString &metaObjectType) 0322 { 0323 int row = 0; 0324 QModelIndex parentIndex; 0325 0326 bool willAdd = 0327 m_d->indexConverter->indexFromAddedDummy(parent, index, 0328 metaObjectType, 0329 parentIndex, row); 0330 0331 if(willAdd) { 0332 beginInsertRows(parentIndex, row, row); 0333 m_d->needFinishInsertRows = true; 0334 } 0335 } 0336 0337 void KisNodeModel::slotEndInsertDummy(KisNodeDummy *dummy) 0338 { 0339 if(m_d->needFinishInsertRows) { 0340 connectDummy(dummy, true); 0341 endInsertRows(); 0342 m_d->needFinishInsertRows = false; 0343 } 0344 } 0345 0346 void KisNodeModel::slotBeginRemoveDummy(KisNodeDummy *dummy) 0347 { 0348 if (!dummy) return; 0349 0350 // FIXME: is it really what we want? 0351 m_d->updateCompressor.stop(); 0352 m_d->updateQueue.clear(); 0353 0354 m_d->parentOfRemovedNode = dummy->parent(); 0355 0356 QModelIndex parentIndex; 0357 if (m_d->parentOfRemovedNode) { 0358 parentIndex = m_d->indexConverter->indexFromDummy(m_d->parentOfRemovedNode); 0359 } 0360 0361 QModelIndex itemIndex = m_d->indexConverter->indexFromDummy(dummy); 0362 0363 if (itemIndex.isValid()) { 0364 connectDummy(dummy, false); 0365 emit sigBeforeBeginRemoveRows(parentIndex, itemIndex.row(), itemIndex.row()); 0366 beginRemoveRows(parentIndex, itemIndex.row(), itemIndex.row()); 0367 m_d->needFinishRemoveRows = true; 0368 } 0369 } 0370 0371 void KisNodeModel::slotEndRemoveDummy() 0372 { 0373 if(m_d->needFinishRemoveRows) { 0374 endRemoveRows(); 0375 m_d->needFinishRemoveRows = false; 0376 } 0377 } 0378 0379 void KisNodeModel::slotDummyChanged(KisNodeDummy *dummy) 0380 { 0381 if (!m_d->updateQueue.contains(dummy)) { 0382 m_d->updateQueue.append(dummy); 0383 } 0384 m_d->updateCompressor.start(); 0385 } 0386 0387 void addChangedIndex(const QModelIndex &idx, QSet<QModelIndex> *indexes) 0388 { 0389 if (!idx.isValid() || indexes->contains(idx)) return; 0390 0391 indexes->insert(idx); 0392 0393 const int rowCount = idx.model()->rowCount(idx); 0394 for (int i = 0; i < rowCount; i++) { 0395 addChangedIndex(idx.model()->index(i, 0, idx), indexes); 0396 } 0397 } 0398 0399 0400 void KisNodeModel::processUpdateQueue() 0401 { 0402 QSet<QModelIndex> indexes; 0403 0404 Q_FOREACH (KisNodeDummy *dummy, m_d->updateQueue) { 0405 QModelIndex index = m_d->indexConverter->indexFromDummy(dummy); 0406 addChangedIndex(index, &indexes); 0407 } 0408 0409 Q_FOREACH (const QModelIndex &index, indexes) { 0410 emit dataChanged(index.siblingAtColumn(0), index.siblingAtColumn(m_d->dummyColumns)); 0411 } 0412 0413 m_d->updateQueue.clear(); 0414 } 0415 0416 QModelIndex KisNodeModel::index(int row, int col, const QModelIndex &parent) const 0417 { 0418 if(!m_d->dummiesFacade || !hasIndex(row, col, parent)) return QModelIndex(); 0419 0420 QModelIndex itemIndex; 0421 0422 KisNodeDummy *dummy = m_d->indexConverter->dummyFromRow(row, parent); 0423 if(dummy) { 0424 itemIndex = m_d->indexConverter->indexFromDummy(dummy); 0425 } 0426 0427 if (itemIndex.isValid() && itemIndex.column() != col) { 0428 itemIndex = createIndex(itemIndex.row(), col, itemIndex.internalPointer()); 0429 } 0430 0431 return itemIndex; 0432 } 0433 0434 int KisNodeModel::rowCount(const QModelIndex &parent) const 0435 { 0436 if(!m_d->dummiesFacade) return 0; 0437 if (parent.column() > 0) { 0438 return 0; 0439 } 0440 return m_d->indexConverter->rowCount(parent); 0441 } 0442 0443 int KisNodeModel::columnCount(const QModelIndex &parent) const 0444 { 0445 if (parent.column() > 0) { 0446 return 0; 0447 } 0448 return 1 + m_d->dummyColumns; 0449 } 0450 0451 QModelIndex KisNodeModel::parent(const QModelIndex &index) const 0452 { 0453 if(!m_d->dummiesFacade || !index.isValid()) return QModelIndex(); 0454 0455 KisNodeDummy *dummy = m_d->indexConverter->dummyFromIndex(index); 0456 KisNodeDummy *parentDummy = dummy->parent(); 0457 0458 QModelIndex parentIndex; 0459 0460 if(parentDummy) { 0461 parentIndex = m_d->indexConverter->indexFromDummy(parentDummy); 0462 } 0463 0464 return parentIndex; 0465 } 0466 0467 QModelIndex KisNodeModel::sibling(int row, int column, const QModelIndex &idx) const 0468 { 0469 // if it's just a different clone column, there's no need to lookup anything 0470 if (row == idx.row()) { 0471 if (column == idx.column()) { 0472 return idx; 0473 } 0474 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(idx.model() == this, QModelIndex()); 0475 return createIndex(row, column, idx.internalPointer()); 0476 } 0477 return index(row, column, parent(idx)); 0478 } 0479 0480 QVariant KisNodeModel::data(const QModelIndex &index, int role) const 0481 { 0482 if (!m_d->dummiesFacade || !index.isValid() || !m_d->image.isValid()) return QVariant(); 0483 0484 KisNodeSP node = nodeFromIndex(index); 0485 0486 switch (role) { 0487 case Qt::DisplayRole: return node->name(); 0488 case Qt::DecorationRole: return node->icon(); 0489 case Qt::EditRole: return node->name(); 0490 case Qt::SizeHintRole: return m_d->image->size(); // FIXME 0491 case Qt::TextColorRole: 0492 return belongsToIsolatedGroup(node) && 0493 !node->projectionLeaf()->isDroppedNode() ? QVariant() : QVariant(QColor(Qt::gray)); 0494 case Qt::FontRole: { 0495 QFont baseFont; 0496 if (node->projectionLeaf()->isDroppedNode()) { 0497 baseFont.setStrikeOut(true); 0498 } 0499 if (m_d->activeNodeIndex == index) { 0500 baseFont.setBold(true); 0501 } 0502 return baseFont; 0503 } 0504 case KisNodeModel::PropertiesRole: return QVariant::fromValue(node->sectionModelProperties()); 0505 case KisNodeModel::AspectRatioRole: return double(m_d->image->width()) / m_d->image->height(); 0506 case KisNodeModel::ProgressRole: { 0507 KisNodeProgressProxy *proxy = node->nodeProgressProxy(); 0508 return proxy ? proxy->percentage() : -1; 0509 } 0510 case KisNodeModel::ActiveRole: { 0511 return m_d->activeNodeIndex == index; 0512 } 0513 case KisNodeModel::ShouldGrayOutRole: { 0514 return !node->visible(true); 0515 } 0516 case KisNodeModel::ColorLabelIndexRole: { 0517 return node->colorLabelIndex(); 0518 } 0519 case KisNodeModel::DropReasonRole: { 0520 QString result; 0521 KisProjectionLeaf::NodeDropReason reason = node->projectionLeaf()->dropReason(); 0522 0523 if (reason == KisProjectionLeaf::DropPassThroughMask) { 0524 result = i18nc("@info:tooltip", "Disabled: masks on pass-through groups are not supported!"); 0525 } else if (reason == KisProjectionLeaf::DropPassThroughClone) { 0526 result = i18nc("@info:tooltip", "Disabled: cloning pass-through groups is not supported!"); 0527 } 0528 0529 return result; 0530 } 0531 case KisNodeModel::IsAnimatedRole: { 0532 return node->isAnimated(); 0533 } 0534 default: 0535 0536 /** 0537 * The dummies are removed from the model asynchronously to the image operations, 0538 * therefore we should make sure that `node->graphListener()` is still valid and 0539 * this node is still present in the node graph. 0540 */ 0541 if (role >= int(KisNodeModel::BeginThumbnailRole) && 0542 belongsToIsolatedGroup(node) && 0543 node->graphListener()) { 0544 0545 /** 0546 * WARNING: there is still a possible theoretical race condition if the node is 0547 * removed from the image right here. We consider that as "improbaple" atm. 0548 */ 0549 0550 const int maxSize = role - int(KisNodeModel::BeginThumbnailRole); 0551 return node->createThumbnail(maxSize, maxSize, Qt::KeepAspectRatio); 0552 } else { 0553 return QVariant(); 0554 } 0555 } 0556 0557 return QVariant(); 0558 } 0559 0560 Qt::ItemFlags KisNodeModel::flags(const QModelIndex &index) const 0561 { 0562 if(!m_d->dummiesFacade || !index.isValid()) return Qt::ItemIsDropEnabled; 0563 0564 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsEditable; 0565 // currently dummy columns are neither selectable nor drag&drop enabled 0566 if (index.column() == 0) { 0567 flags |= Qt::ItemIsDragEnabled | Qt::ItemIsSelectable; 0568 if (m_d->dropEnabled.contains(index.internalId())) { 0569 flags |= Qt::ItemIsDropEnabled; 0570 } 0571 0572 } 0573 0574 return flags; 0575 } 0576 0577 bool KisNodeModel::setData(const QModelIndex &index, const QVariant &value, int role) 0578 { 0579 if (role == KisNodeModel::DropEnabled) { 0580 const QMimeData *mimeData = static_cast<const QMimeData*>(value.value<void*>()); 0581 setDropEnabled(mimeData); 0582 return true; 0583 } 0584 0585 if (role == KisNodeModel::ActiveRole || role == KisNodeModel::AlternateActiveRole) { 0586 QModelIndex parentIndex; 0587 if (!index.isValid() && m_d->parentOfRemovedNode && m_d->dummiesFacade && m_d->indexConverter) { 0588 parentIndex = m_d->indexConverter->indexFromDummy(m_d->parentOfRemovedNode); 0589 m_d->parentOfRemovedNode = 0; 0590 } 0591 0592 KisNodeSP activatedNode; 0593 0594 if (index.isValid() && value.toBool()) { 0595 activatedNode = nodeFromIndex(index); 0596 } 0597 else if (parentIndex.isValid() && value.toBool()) { 0598 activatedNode = nodeFromIndex(parentIndex); 0599 } 0600 else { 0601 activatedNode = 0; 0602 } 0603 0604 QModelIndex newActiveNode = activatedNode ? indexFromNode(activatedNode) : QModelIndex(); 0605 if (role == KisNodeModel::ActiveRole && value.toBool() && 0606 m_d->activeNodeIndex == newActiveNode) { 0607 0608 return true; 0609 } 0610 0611 m_d->activeNodeIndex = newActiveNode; 0612 0613 if (m_d->nodeSelectionAdapter) { 0614 m_d->nodeSelectionAdapter->setActiveNode(activatedNode); 0615 } 0616 0617 if (role == KisNodeModel::AlternateActiveRole) { 0618 emit toggleIsolateActiveNode(); 0619 } 0620 0621 emit dataChanged(index.siblingAtColumn(0), index.siblingAtColumn(m_d->dummyColumns)); 0622 return true; 0623 } 0624 0625 if(!m_d->dummiesFacade || !index.isValid()) return false; 0626 0627 bool result = true; 0628 bool shouldUpdate = true; 0629 bool shouldUpdateRecursively = false; 0630 KisNodeSP node = nodeFromIndex(index); 0631 0632 switch (role) { 0633 case Qt::DisplayRole: 0634 case Qt::EditRole: 0635 m_d->nodeManager->setNodeName(node, value.toString()); 0636 break; 0637 case KisNodeModel::PropertiesRole: 0638 { 0639 // don't record undo/redo for visibility, locked or alpha locked changes 0640 KisBaseNode::PropertyList proplist = value.value<KisBaseNode::PropertyList>(); 0641 m_d->nodeManager->trySetNodeProperties(node, m_d->image, proplist); 0642 shouldUpdateRecursively = true; 0643 0644 break; 0645 } 0646 case KisNodeModel::SelectOpaqueRole: 0647 if (node && m_d->selectionActionsAdapter) { 0648 SelectionAction action = SelectionAction(value.toInt()); 0649 m_d->selectionActionsAdapter->selectOpaqueOnNode(node, action); 0650 } 0651 shouldUpdate = false; 0652 break; 0653 default: 0654 result = false; 0655 } 0656 0657 if (result && shouldUpdate) { 0658 if (shouldUpdateRecursively) { 0659 QSet<QModelIndex> indexes; 0660 addChangedIndex(index, &indexes); 0661 Q_FOREACH (const QModelIndex &idx, indexes) { 0662 emit dataChanged(idx.siblingAtColumn(0), idx.siblingAtColumn(m_d->dummyColumns)); 0663 } 0664 } else { 0665 emit dataChanged(index.siblingAtColumn(0), index.siblingAtColumn(m_d->dummyColumns)); 0666 } 0667 } 0668 0669 return result; 0670 } 0671 0672 Qt::DropActions KisNodeModel::supportedDragActions() const 0673 { 0674 return Qt::CopyAction | Qt::MoveAction; 0675 } 0676 0677 Qt::DropActions KisNodeModel::supportedDropActions() const 0678 { 0679 return Qt::MoveAction | Qt::CopyAction; 0680 } 0681 0682 bool KisNodeModel::hasDummiesFacade() 0683 { 0684 return m_d->dummiesFacade != 0; 0685 } 0686 0687 QStringList KisNodeModel::mimeTypes() const 0688 { 0689 QStringList types; 0690 types << QLatin1String("application/x-krita-node-internal-pointer"); 0691 types << QLatin1String("application/x-qt-image"); 0692 types << QLatin1String("application/x-color"); 0693 types << QLatin1String("krita/x-colorsetentry"); 0694 return types; 0695 } 0696 0697 QMimeData * KisNodeModel::mimeData(const QModelIndexList &indexes) const 0698 { 0699 bool hasLockedLayer = false; 0700 KisNodeList nodes; 0701 Q_FOREACH (const QModelIndex &idx, indexes) { 0702 // Although clone columns should not be selectable, make sure we only use column 0, 0703 // because nodeFromIndex doesn't like duplicate list entries. 0704 if (idx.column() != 0) { 0705 continue; 0706 } 0707 0708 KisNodeSP node = nodeFromIndex(idx); 0709 0710 nodes << node; 0711 hasLockedLayer |= !node->isEditable(false); 0712 } 0713 0714 return KisMimeData::mimeForLayers(nodes, m_d->image, hasLockedLayer); 0715 } 0716 0717 bool KisNodeModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) 0718 { 0719 Q_UNUSED(column); 0720 0721 bool copyNode = (action == Qt::CopyAction); 0722 0723 KisNodeDummy *parentDummy = 0; 0724 KisNodeDummy *aboveThisDummy = 0; 0725 0726 parentDummy = parent.isValid() ? 0727 m_d->indexConverter->dummyFromIndex(parent) : 0728 m_d->dummiesFacade->rootDummy(); 0729 0730 if (row == -1) { 0731 aboveThisDummy = parent.isValid() ? parentDummy->lastChild() : 0; 0732 } 0733 else { 0734 aboveThisDummy = row < m_d->indexConverter->rowCount(parent) ? m_d->indexConverter->dummyFromRow(row, parent) : 0; 0735 } 0736 0737 return KisMimeData::insertMimeLayers(data, 0738 m_d->image, 0739 m_d->shapeController, 0740 parentDummy, 0741 aboveThisDummy, 0742 copyNode, 0743 m_d->nodeInsertionAdapter); 0744 } 0745 0746 bool KisNodeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const { 0747 if (parent.isValid()) { 0748 // drop occurred on an item. always return true as returning false will mess up 0749 // QT5's drag handling (see KisNodeModel::setDropEnabled). 0750 return true; 0751 } else { 0752 return QAbstractItemModel::canDropMimeData(data, action, row, column, parent); 0753 } 0754 } 0755 0756 void KisNodeModel::setDropEnabled(const QMimeData *data) { 0757 // what happens here should really happen in KisNodeModel::canDropMimeData(), but QT5 0758 // will mess up if an item's Qt::ItemIsDropEnabled does not match what is returned by 0759 // canDropMimeData; specifically, if we set the flag, but decide in canDropMimeData() 0760 // later on that an "onto" drag is not allowed, QT will display an drop indicator for 0761 // insertion, but not perform any drop when the mouse is released. 0762 0763 // the only robust implementation seems to set all flags correctly, which is done here. 0764 0765 bool copyNode = false; 0766 KisNodeList nodes = KisMimeData::loadNodesFast(data, m_d->image, m_d->shapeController, copyNode); 0767 m_d->dropEnabled.clear(); 0768 updateDropEnabled(nodes); 0769 } 0770 0771 void KisNodeModel::updateDropEnabled(const QList<KisNodeSP> &nodes, QModelIndex parent) { 0772 for (int r = 0; r < rowCount(parent); r++) { 0773 QModelIndex idx = index(r, 0, parent); 0774 0775 KisNodeSP target = nodeFromIndex(idx); 0776 0777 bool dropEnabled = true; 0778 Q_FOREACH (const KisNodeSP &node, nodes) { 0779 if (!target->allowAsChild(node) || !target->isEditable(false)) { 0780 dropEnabled = false; 0781 break; 0782 } 0783 } 0784 if (dropEnabled) { 0785 m_d->dropEnabled.insert(idx.internalId()); 0786 } 0787 emit dataChanged(idx, idx); // indicate to QT that flags() have changed 0788 0789 if (hasChildren(idx)) { 0790 updateDropEnabled(nodes, idx); 0791 } 0792 } 0793 }