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 }