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 }