Warning, file /graphics/glaxnimate/src/core/model/document.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.hpp"
0008 
0009 #include <QRegularExpression>
0010 
0011 #include "io/glaxnimate/glaxnimate_format.hpp"
0012 #include "model/assets/assets.hpp"
0013 #include "model/assets/pending_asset.hpp"
0014 
0015 
0016 class glaxnimate::model::Document::Private
0017 {
0018 public:
0019     using NameIndex = unsigned long long;
0020 
0021     Private(Document* doc)
0022         : assets(doc)
0023     {
0024         io_options.format = io::glaxnimate::GlaxnimateFormat::instance();
0025     }
0026 
0027     std::pair<QString, NameIndex> name_index(const QString& name) const
0028     {
0029         static QRegularExpression detect_numbers("^(.*) ([0-9]+)$");
0030         QRegularExpressionMatch match = detect_numbers.match(name);
0031         if ( match.hasMatch() )
0032         {
0033             return {match.captured(1), match.captured(2).toULongLong()};
0034         }
0035 
0036         return {name, 0};
0037     }
0038 
0039     void increase(std::pair<QString, NameIndex> pair)
0040     {
0041         auto iter = name_indices.find(pair.first);
0042 
0043         if ( iter != name_indices.end() )
0044         {
0045             if ( iter->second < pair.second )
0046                 iter->second = pair.second;
0047         }
0048         else
0049         {
0050             name_indices.emplace(std::move(pair));
0051         }
0052     }
0053 
0054     void decrease(const std::pair<QString, NameIndex>& pair)
0055     {
0056         if ( pair.second == 0 )
0057             return;
0058 
0059         auto iter = name_indices.find(pair.first);
0060 
0061         if ( iter != name_indices.end() )
0062         {
0063             if ( iter->second == pair.second )
0064                 iter->second -= 1;
0065         }
0066     }
0067 
0068     QString name_suggestion(const QString& base_name)
0069     {
0070         auto index_pair = name_index(base_name);
0071         auto iter = name_indices.find(index_pair.first);
0072         if ( iter == name_indices.end() )
0073             return base_name;
0074 
0075         return QString("%1 %2").arg(iter->first).arg(iter->second + 1);
0076     }
0077 
0078     int add_pending_asset(QUrl url, QByteArray data, const QString& name_alias)
0079     {
0080         int id = max_pending_id;
0081         ++max_pending_id;
0082         pending_assets[id] = {id, std::move(url), std::move(data), name_alias};
0083         return id;
0084     }
0085 
0086     QUndoStack undo_stack;
0087     QVariantMap metadata;
0088     io::Options io_options;
0089     FrameTime current_time = 0;
0090     bool record_to_keyframe = false;
0091     Assets assets;
0092     glaxnimate::model::CompGraph comp_graph;
0093     std::unordered_map<QString, NameIndex> name_indices;
0094     std::map<int, PendingAsset> pending_assets;
0095     int max_pending_id = 0;
0096     DocumentInfo info;
0097     QUuid uuid;
0098 };
0099 
0100 
0101 glaxnimate::model::Document::Document(const QString& filename)
0102     : d ( std::make_unique<glaxnimate::model::Document::Private>(this) )
0103 {
0104     d->io_options.filename = filename;
0105     d->uuid = QUuid::createUuid();
0106 }
0107 
0108 glaxnimate::model::Document::~Document() = default;
0109 
0110 QString glaxnimate::model::Document::filename() const
0111 {
0112     return d->io_options.filename;
0113 }
0114 
0115 QUuid glaxnimate::model::Document::uuid() const
0116 {
0117     return d->uuid;
0118 }
0119 
0120 QVariantMap & glaxnimate::model::Document::metadata()
0121 {
0122     return d->metadata;
0123 }
0124 
0125 QUndoStack & glaxnimate::model::Document::undo_stack()
0126 {
0127     return d->undo_stack;
0128 }
0129 
0130 const glaxnimate::io::Options & glaxnimate::model::Document::io_options() const
0131 {
0132     return d->io_options;
0133 }
0134 
0135 void glaxnimate::model::Document::set_io_options(const io::Options& opt)
0136 {
0137     bool em = opt.filename != d->io_options.filename;
0138     d->io_options = opt;
0139     if ( em )
0140         Q_EMIT filename_changed(d->io_options.filename);
0141 }
0142 
0143 glaxnimate::model::DocumentNode * glaxnimate::model::Document::find_by_uuid(const QUuid& n) const
0144 {
0145     return d->assets.docnode_find_by_uuid(n);
0146 }
0147 
0148 glaxnimate::model::DocumentNode * glaxnimate::model::Document::find_by_name(const QString& name) const
0149 {
0150     return d->assets.docnode_find_by_name(name);
0151 }
0152 
0153 QVariantList glaxnimate::model::Document::find_by_type_name(const QString& type_name) const
0154 {
0155     return d->assets.find_by_type_name(type_name);
0156 }
0157 
0158 bool glaxnimate::model::Document::redo()
0159 {
0160     if ( ! d->undo_stack.canRedo() )
0161         return false;
0162     d->undo_stack.redo();
0163     return true;
0164 }
0165 
0166 bool glaxnimate::model::Document::undo()
0167 {
0168     if ( ! d->undo_stack.canUndo() )
0169         return false;
0170     d->undo_stack.undo();
0171     return true;
0172 }
0173 
0174 glaxnimate::model::FrameTime glaxnimate::model::Document::current_time() const
0175 {
0176     return d->current_time;
0177 }
0178 
0179 void glaxnimate::model::Document::set_current_time(glaxnimate::model::FrameTime t)
0180 {
0181     d->assets.set_time(t);
0182     Q_EMIT current_time_changed(d->current_time = t);
0183 }
0184 
0185 
0186 bool glaxnimate::model::Document::record_to_keyframe() const
0187 {
0188     return d->record_to_keyframe;
0189 }
0190 
0191 void glaxnimate::model::Document::set_record_to_keyframe(bool r)
0192 {
0193     Q_EMIT record_to_keyframe_changed(d->record_to_keyframe = r);
0194 }
0195 
0196 void glaxnimate::model::Document::push_command(QUndoCommand* cmd)
0197 {
0198     d->undo_stack.push(cmd);
0199 }
0200 
0201 QString glaxnimate::model::Document::get_best_name(glaxnimate::model::DocumentNode* node, const QString& suggestion) const
0202 {
0203     if ( !node )
0204         return {};
0205 
0206     if ( suggestion.isEmpty() )
0207         return d->name_suggestion(node->type_name_human());
0208 
0209     return d->name_suggestion(suggestion);
0210 
0211 }
0212 
0213 void glaxnimate::model::Document::set_best_name(glaxnimate::model::DocumentNode* node, const QString& suggestion) const
0214 {
0215     if ( node )
0216         node->name.set(get_best_name(node, suggestion));
0217 }
0218 
0219 glaxnimate::model::Assets * glaxnimate::model::Document::assets() const
0220 {
0221     return &d->assets;
0222 }
0223 
0224 glaxnimate::model::Object * glaxnimate::model::Document::assets_obj() const
0225 {
0226     return assets();
0227 }
0228 
0229 void glaxnimate::model::Document::set_metadata(const QVariantMap& meta)
0230 {
0231     d->metadata = meta;
0232 }
0233 
0234 glaxnimate::model::CompGraph & glaxnimate::model::Document::comp_graph()
0235 {
0236     return d->comp_graph;
0237 }
0238 
0239 void glaxnimate::model::Document::decrease_node_name(const QString& old_name)
0240 {
0241     if ( !old_name.isEmpty() )
0242         d->decrease(d->name_index(old_name));
0243 }
0244 
0245 void glaxnimate::model::Document::increase_node_name(const QString& new_name)
0246 {
0247     if ( !new_name.isEmpty() )
0248         d->increase(d->name_index(new_name));
0249 }
0250 
0251 
0252 void glaxnimate::model::Document::stretch_time(qreal multiplier)
0253 {
0254     qreal time = d->current_time;
0255     d->assets.stretch_time(multiplier);
0256     set_current_time(qRound(time * multiplier));
0257 }
0258 
0259 int glaxnimate::model::Document::add_pending_asset(const QString& name, const QByteArray& data)
0260 {
0261     return d->add_pending_asset({}, data, name);
0262 }
0263 
0264 int glaxnimate::model::Document::add_pending_asset(const QString& name, const QUrl& url)
0265 {
0266     return d->add_pending_asset(url, {}, name);
0267 }
0268 
0269 int glaxnimate::model::Document::add_pending_asset(const PendingAsset& ass)
0270 {
0271     return d->add_pending_asset(ass.url, ass.data, ass.name_alias);
0272 }
0273 
0274 void glaxnimate::model::Document::mark_asset_loaded(int id)
0275 {
0276     auto it = d->pending_assets.find(id);
0277     if ( it != d->pending_assets.end() )
0278         it->second.loaded = true;
0279 }
0280 
0281 std::vector<glaxnimate::model::PendingAsset> glaxnimate::model::Document::pending_assets()
0282 {
0283     std::vector<PendingAsset> assets;
0284     assets.reserve(d->pending_assets.size());
0285     for ( const auto& ass : d->pending_assets )
0286         assets.push_back(ass.second);
0287 
0288     return assets;
0289 }
0290 
0291 void glaxnimate::model::Document::clear_pending_assets()
0292 {
0293     for ( auto& ass : d->pending_assets )
0294         ass.second.loaded = true;
0295 }
0296 
0297 
0298 glaxnimate::model::Document::DocumentInfo & glaxnimate::model::Document::info()
0299 {
0300     return d->info;
0301 }