Warning, file /graphics/glaxnimate/src/gui/item_models/document_node_model.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include "document_node_model.hpp"
0008 
0009 #include <QMimeData>
0010 #include <QDataStream>
0011 #include <QSet>
0012 
0013 #include "command/property_commands.hpp"
0014 
0015 #include "model/shapes/shape.hpp"
0016 #include "model/assets/assets.hpp"
0017 
0018 using namespace glaxnimate::gui;
0019 using namespace glaxnimate;
0020 
0021 
0022 class item_models::DocumentNodeModel::Private
0023 {
0024 public:
0025     model::Document* document = nullptr;
0026 
0027 
0028     // Sometimes views send QModelIndex instances after they've been removed...
0029     // So we avoid accessing the internal pointer directly
0030     std::unordered_set<void*> ptrs;
0031 };
0032 
0033 item_models::DocumentNodeModel::DocumentNodeModel(QObject* parent)
0034     : DocumentModelBase(parent), d(std::make_unique<Private>())
0035 {
0036 }
0037 
0038 item_models::DocumentNodeModel::~DocumentNodeModel()
0039 {
0040 }
0041 
0042 void item_models::DocumentNodeModel::connect_node ( model::DocumentNode* node )
0043 {
0044     d->ptrs.insert(node);
0045 
0046     connect(node, &model::DocumentNode::docnode_child_add_begin, this, [this, node](int row) {
0047         int rows = node->docnode_child_count();
0048         beginInsertRows(node_index(node), rows - row, rows - row);
0049     });
0050     connect(node, &model::DocumentNode::docnode_child_add_end, this, [this](model::DocumentNode* child) {
0051         endInsertRows();
0052         connect_node(child);
0053     });
0054     connect(node, &model::DocumentNode::docnode_child_remove_begin, this, [this, node](int row) {
0055         int rows = node->docnode_child_count();
0056         beginRemoveRows(node_index(node), rows - row - 1, rows - row - 1);
0057     });
0058     connect(node, &model::DocumentNode::docnode_child_remove_end, this, [this](model::DocumentNode* child) {
0059         endRemoveRows();
0060         disconnect_node(child);
0061     });
0062     if ( auto visual = node->cast<model::VisualNode>() )
0063     {
0064         connect(visual, &model::VisualNode::docnode_visible_changed, this, [this, visual]() {
0065             QModelIndex ind = node_index(visual);
0066             QModelIndex par = node_index(visual->docnode_parent());
0067             QModelIndex changed = index(ind.row(), ColumnVisible, par);
0068             dataChanged(changed, changed, {Qt::DecorationRole});
0069         });
0070         connect(visual, &model::VisualNode::docnode_locked_changed, this, [this, visual]() {
0071             QModelIndex ind = node_index(visual);
0072             QModelIndex par = node_index(visual->docnode_parent());
0073             QModelIndex changed = index(ind.row(), ColumnLocked, par);
0074             dataChanged(changed, changed, {Qt::DecorationRole});
0075         });
0076         connect(visual, &model::VisualNode::docnode_group_color_changed, this, [this, visual]() {
0077             QModelIndex ind = node_index(visual);
0078             QModelIndex par = node_index(visual->docnode_parent());
0079             QModelIndex changed = index(ind.row(), ColumnColor, par);
0080             dataChanged(changed, changed, {Qt::BackgroundRole, Qt::EditRole, Qt::DisplayRole});
0081         });
0082     }
0083     connect(node, &model::DocumentNode::name_changed, this, [this, node]() {
0084         QModelIndex ind = node_index(node);
0085         QModelIndex par = node_index(node->docnode_parent());
0086         QModelIndex changed = index(ind.row(), ColumnName, par);
0087         dataChanged(changed, changed, {Qt::EditRole, Qt::DisplayRole});
0088     });
0089     connect(node, &model::DocumentNode::docnode_child_move_begin, this, [this, node](int a, int b) {
0090         int rows = node->docnode_child_count();
0091 
0092         int src = rows - a - 1;
0093         int dest = rows - b - 1;
0094         if ( src < dest )
0095             dest++;
0096 
0097         QModelIndex parent = node_index(node);
0098         beginMoveRows(parent, src, src, parent, dest);
0099     });
0100     connect(node, &model::DocumentNode::docnode_child_move_end, this, [this]() {
0101         endMoveRows();
0102     });
0103 
0104     for ( model::DocumentNode* child : node->docnode_children() )
0105         connect_node(child);
0106 
0107     connect(node, &QObject::destroyed, this, [this, node]{
0108         d->ptrs.erase(node);
0109     });
0110 }
0111 
0112 void item_models::DocumentNodeModel::disconnect_node ( model::DocumentNode* node )
0113 {
0114     d->ptrs.erase(node);
0115 
0116     disconnect(node, nullptr, this, nullptr);
0117 
0118     for ( model::DocumentNode* child : node->docnode_children() )
0119         disconnect_node(child);
0120 }
0121 
0122 int item_models::DocumentNodeModel::rowCount ( const QModelIndex& parent ) const
0123 {
0124     if ( !d->document )
0125         return 0;
0126 
0127     if ( !parent.isValid() )
0128         return 2;
0129 
0130     return node(parent)->docnode_child_count();
0131 }
0132 
0133 int item_models::DocumentNodeModel::columnCount ( const QModelIndex& ) const
0134 {
0135     return ColumnCount;
0136 }
0137 
0138 QModelIndex item_models::DocumentNodeModel::index ( int row, int column, const QModelIndex& parent ) const
0139 {
0140     if ( !d->document )
0141         return {};
0142 
0143     if ( !parent.isValid() )
0144     {
0145         if ( row == 0 )
0146             return createIndex(row, column, d->document->assets());
0147         return {};
0148     }
0149 
0150     auto n = node(parent);
0151     int rows = n->docnode_child_count();
0152     if ( !n || row < 0 || row >= rows )
0153         return {};
0154 
0155     return createIndex(row, column, n->docnode_child(rows - row - 1));
0156 }
0157 
0158 Qt::ItemFlags item_models::DocumentNodeModel::flags ( const QModelIndex& index ) const
0159 {
0160     if ( !d->document )
0161         return Qt::NoItemFlags;
0162 
0163 
0164     Qt::ItemFlags flags = QAbstractItemModel::flags(index);
0165 
0166     switch ( index.column() )
0167     {
0168         case ColumnName:
0169         case ColumnColor:
0170             flags |= Qt::ItemIsEditable;
0171             break;
0172 //         case ColumnLocked:
0173 //         case ColumnVisible:
0174 //             flags |= Qt::ItemIsUserCheckable;
0175 //             break;
0176     }
0177 
0178     auto n = qobject_cast<model::VisualNode*>(node(index));
0179     if ( n && !n->docnode_locked_recursive() )
0180     {
0181         if ( n->has("shapes") )
0182             flags |= Qt::ItemIsDropEnabled;
0183         flags |= Qt::ItemIsDragEnabled;
0184     }
0185 
0186     return flags;
0187 }
0188 
0189 QVariant item_models::DocumentNodeModel::data(const QModelIndex& index, int role) const
0190 {
0191     auto n = node(index);
0192     if ( !d->document || !index.isValid() || !n )
0193         return {};
0194 
0195     model::VisualNode* visual = nullptr;
0196 
0197 // Can't figure how to force this from the style
0198 #ifdef Q_OS_ANDROID
0199     if ( role == Qt::SizeHintRole && index.column() == ColumnVisible )
0200         return QSize(80, 80);
0201 #endif
0202 
0203     switch ( index.column() )
0204     {
0205         case ColumnColor:
0206             visual = n->cast<model::VisualNode>();
0207             if ( visual && (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::BackgroundRole) )
0208                 return visual->docnode_group_color();
0209             break;
0210         case ColumnName:
0211             if ( role == Qt::DisplayRole || role == Qt::EditRole )
0212                 return n->object_name();
0213 #ifndef Q_OS_ANDROID
0214             else if ( role == Qt::DecorationRole )
0215                 return n->tree_icon();
0216 #endif
0217             break;
0218         case ColumnVisible:
0219             visual = n->cast<model::VisualNode>();
0220             if ( visual && role == Qt::DecorationRole )
0221             {
0222                 if ( visual->visible.get() )
0223                     return QIcon::fromTheme("view-visible");
0224                 return QIcon::fromTheme("view-hidden");
0225             }
0226             break;
0227         case ColumnLocked:
0228             visual = n->cast<model::VisualNode>();
0229             if ( visual && role == Qt::DecorationRole )
0230             {
0231                 if ( visual->locked.get() )
0232                     return QIcon::fromTheme("object-locked");
0233                 return QIcon::fromTheme("object-unlocked");
0234             }
0235             break;
0236         case ColumnUsers:
0237             if ( role == Qt::DisplayRole )
0238                 return int(n->users().size());
0239             break;
0240     }
0241     if ( role == Qt::UserRole )
0242         return n->uuid.get();
0243     return {};
0244 }
0245 
0246 QVariant item_models::DocumentNodeModel::headerData(int section, Qt::Orientation orientation, int role) const
0247 {
0248     if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
0249     {
0250         switch ( section )
0251         {
0252             case ColumnColor:
0253                 return i18n("Color");
0254             case ColumnName:
0255                 return i18n("Name");
0256             case ColumnVisible:
0257                 return i18n("Visible");
0258             case ColumnLocked:
0259                 return i18n("Locked");
0260             case ColumnUsers:
0261                 return i18n("#");
0262         }
0263     }
0264 
0265     return {};
0266 }
0267 
0268 bool item_models::DocumentNodeModel::setData(const QModelIndex& index, const QVariant& value, int role)
0269 {
0270     Q_UNUSED(role);
0271     auto n = node(index);
0272     if ( !d->document || !index.isValid() || !n )
0273         return false;
0274 
0275     switch ( index.column() )
0276     {
0277         case ColumnColor:
0278             if ( auto visual = n->cast<model::VisualNode>() )
0279             {
0280                 d->document->undo_stack().push(new command::SetPropertyValue(&visual->group_color, visual->group_color.get(), value));
0281                 return true;
0282             }
0283             return false;
0284         case ColumnName:
0285             d->document->undo_stack().push(new command::SetPropertyValue(&n->name, n->name.get(), value));
0286             return true;
0287     }
0288 
0289     return false;
0290 }
0291 
0292 void item_models::DocumentNodeModel::set_document ( model::Document* doc )
0293 {
0294     beginResetModel();
0295 
0296     if ( d->document )
0297     {
0298         disconnect(d->document->assets(), nullptr, this, nullptr);
0299     }
0300 
0301     d->document = doc;
0302 
0303     if ( doc )
0304     {
0305         connect_node(doc->assets());
0306     }
0307     endResetModel();
0308 }
0309 
0310 void item_models::DocumentNodeModel::clear_document()
0311 {
0312     set_document(nullptr);
0313 }
0314 
0315 model::DocumentNode * item_models::DocumentNodeModel::node ( const QModelIndex& index ) const
0316 {
0317     auto ptr = (model::DocumentNode*)index.internalPointer();
0318     if ( d->ptrs.count(ptr) )
0319         return ptr;
0320     return nullptr;
0321 }
0322 
0323 QModelIndex item_models::DocumentNodeModel::parent ( const QModelIndex& index ) const
0324 {
0325     auto n = node(index);
0326     if ( !d->document || !index.isValid() || !n )
0327         return {};
0328 
0329     return node_index(n->docnode_parent());
0330 }
0331 
0332 QModelIndex item_models::DocumentNodeModel::node_index ( model::DocumentNode* node ) const
0333 {
0334     if ( !node )
0335         return {};
0336 
0337     auto parent = node->docnode_parent();
0338 
0339     if ( !parent )
0340     {
0341         if ( !d->document || node != d->document->assets() )
0342             return {};
0343 
0344         return createIndex(0, 0, node);
0345     }
0346 
0347     int rows = parent->docnode_child_count();
0348     for ( int i = 0; i < rows; i++ )
0349     {
0350         if ( parent->docnode_child(i) == node )
0351             return createIndex(rows - i - 1, 0, node);
0352     }
0353 
0354     return {};
0355 }
0356 
0357 bool item_models::DocumentNodeModel::moveRows ( const QModelIndex& sourceParent, int sourceRow, int count, const QModelIndex& destinationParent, int destinationChild )
0358 {
0359     Q_UNUSED(sourceParent); Q_UNUSED(sourceRow); Q_UNUSED(count); Q_UNUSED(destinationParent); Q_UNUSED(destinationChild);
0360     return false;
0361 }
0362 
0363 bool item_models::DocumentNodeModel::removeRows ( int row, int count, const QModelIndex& parent )
0364 {
0365     Q_UNUSED(row); Q_UNUSED(count); Q_UNUSED(parent);
0366     return false;
0367 }
0368 
0369 model::VisualNode* item_models::DocumentNodeModel::visual_node(const QModelIndex& index) const
0370 {
0371     return qobject_cast<model::VisualNode*>(node(index));
0372 }
0373 
0374 model::Document * item_models::DocumentNodeModel::document() const
0375 {
0376     return d->document;
0377 }