Warning, file /graphics/glaxnimate/src/gui/settings/document_templates.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_templates.hpp" 0008 0009 #include <numeric> 0010 0011 #include <QAction> 0012 0013 #include "app/application.hpp" 0014 0015 #include "io/glaxnimate/glaxnimate_format.hpp" 0016 #include "model/assets/assets.hpp" 0017 0018 using namespace glaxnimate::gui; 0019 using namespace glaxnimate; 0020 0021 0022 settings::DocumentTemplate::DocumentTemplate(const QString& filename, bool* loaded) 0023 : filename(filename), document(load(loaded)) 0024 {} 0025 0026 QSize settings::DocumentTemplate::size() const 0027 { 0028 if ( auto comp = main_comp() ) 0029 return comp->size(); 0030 return {}; 0031 } 0032 0033 model::Composition* settings::DocumentTemplate::main_comp() const 0034 { 0035 if ( document->assets()->compositions->values.empty() ) 0036 return nullptr; 0037 return document->assets()->compositions->values[0]; 0038 } 0039 0040 model::FrameTime settings::DocumentTemplate::duration() const 0041 { 0042 if ( auto comp = main_comp() ) 0043 return comp->animation->last_frame.get() - comp->animation->first_frame.get(); 0044 0045 return 0; 0046 } 0047 0048 QString settings::DocumentTemplate::name() const 0049 { 0050 if ( auto comp = main_comp() ) 0051 return comp->name.get(); 0052 return i18n("Unnamed"); 0053 } 0054 0055 QString settings::DocumentTemplate::long_name() const 0056 { 0057 return name_template(main_comp()).subs(name()).toString(); 0058 } 0059 0060 float settings::DocumentTemplate::fps() const 0061 { 0062 if ( auto comp = main_comp() ) 0063 return comp->fps.get(); 0064 0065 return 60; 0066 } 0067 0068 std::unique_ptr<model::Document> settings::DocumentTemplate::create(bool* ok) const 0069 { 0070 std::unique_ptr<model::Document> document; 0071 document = load(ok); 0072 document->assets()->refresh_uuid(); 0073 for ( const auto& comp : document->assets()->compositions->values ) 0074 document->set_best_name(comp.get()); 0075 return document; 0076 } 0077 0078 std::unique_ptr<model::Document> settings::DocumentTemplate::load(bool* ok) const 0079 { 0080 std::unique_ptr<model::Document> document = std::make_unique<model::Document>(""); 0081 QFile file(filename); 0082 *ok = io::glaxnimate::GlaxnimateFormat().open(file, filename, document.get(), {}); 0083 return document; 0084 } 0085 0086 bool settings::DocumentTemplate::operator<(const settings::DocumentTemplate& other) const 0087 { 0088 QString n1 = name(); 0089 QString n2 = other.name(); 0090 if ( n1 == n2 ) 0091 { 0092 if ( size().width() == other.size().width() ) 0093 return fps() > other.fps(); 0094 return size().width() > other.size().width(); 0095 } 0096 return n1 < n2; 0097 } 0098 0099 QString settings::DocumentTemplate::aspect_ratio() const 0100 { 0101 return aspect_ratio(size()); 0102 } 0103 0104 QString settings::DocumentTemplate::aspect_ratio(const QSize& size) 0105 { 0106 if ( size.width() > 0 && size.height() > 0 ) 0107 { 0108 int gcd = std::gcd(size.width(), size.height()); 0109 return QString("%1:%2").arg(size.width()/gcd).arg(size.height()/gcd); 0110 } 0111 0112 return {}; 0113 } 0114 0115 KLocalizedString settings::DocumentTemplate::name_template(model::Composition* comp) 0116 { 0117 if ( !comp ) 0118 return ki18n("%1"); 0119 0120 QString aspect; 0121 auto w = comp->width.get(); 0122 auto h = comp->height.get(); 0123 0124 //: 0125 return ki18nc("%5 is the file name, %1x%2 is the size, %3 is the aspect ratio, %4 is the frame rate", "%5 - %1x%2 %3 %4fps") 0126 .subs(w) 0127 .subs(h) 0128 .subs(aspect_ratio(QSize(w, h))) 0129 .subs(comp->fps.get()) 0130 ; 0131 } 0132 0133 QAction* settings::DocumentTemplates::create_action(const DocumentTemplate& templ, QObject *parent) 0134 { 0135 QAction* action = new QAction(QIcon::fromTheme("document-new-from-template"), templ.long_name(), parent); 0136 connect(action, &QAction::triggered, this, [&templ, this]{ 0137 Q_EMIT create_from(templ); 0138 }); 0139 return action; 0140 } 0141 0142 void settings::DocumentTemplates::load() 0143 { 0144 templates_.clear(); 0145 names.clear(); 0146 0147 for ( QDir dir : app::Application::instance()->data_paths("templates") ) 0148 { 0149 for ( const auto& filename : dir.entryList(QDir::Files|QDir::Readable) ) 0150 { 0151 bool ok = false; 0152 DocumentTemplate templ(dir.absoluteFilePath(filename), &ok); 0153 if ( ok && !names.count(templ.long_name()) ) 0154 { 0155 names.insert(templ.long_name()); 0156 templates_.push_back(std::move(templ)); 0157 } 0158 } 0159 } 0160 0161 std::sort(templates_.begin(), templates_.end()); 0162 0163 Q_EMIT loaded(templates_); 0164 } 0165 0166 settings::DocumentTemplates::DocumentTemplates() 0167 { 0168 load(); 0169 } 0170 0171 bool settings::DocumentTemplates::save_as_template(model::Document* document) 0172 { 0173 if ( document->assets()->compositions->values.empty() ) 0174 return false; 0175 0176 model::Composition* comp = document->assets()->compositions->values[0]; 0177 0178 QString name_base = comp ? comp->name.get() : ""; 0179 if ( name_base.isEmpty() ) 0180 name_base = i18n("Template"); 0181 0182 // Find a nice name, avoiding duplicates 0183 auto name_full_template = DocumentTemplate::name_template(comp); 0184 KLocalizedString name_template = ki18n("%1 %2").subs(name_base); 0185 QString name = name_base; 0186 QString name_full = name_full_template.subs(name).toString(); 0187 for ( int count = 1; names.count(name_full); count++ ) 0188 { 0189 name = name_template.subs(count).toString(); 0190 name_full = name_full_template.subs(name).toString(); 0191 } 0192 0193 // Generate a file name 0194 QString basename; 0195 for ( const QChar& ch : name ) 0196 { 0197 if ( ch.isLetterOrNumber() || ch == '_' || ch == '-' ) 0198 basename += ch; 0199 else 0200 basename += '_'; 0201 } 0202 0203 io::glaxnimate::GlaxnimateFormat format; 0204 QDir dirname = app::Application::instance()->writable_data_path("templates"); 0205 QString path = dirname.absoluteFilePath(basename + "." + format.extensions()[0]); 0206 0207 if ( !dirname.exists() ) 0208 { 0209 dirname.cdUp(); 0210 dirname.mkpath("templates"); 0211 } 0212 0213 QFile file(path); 0214 if ( !format.save(file, path, comp, {}) ) 0215 return false; 0216 file.close(); 0217 0218 bool ok = false; 0219 DocumentTemplate templ(path, &ok); 0220 if ( !ok ) 0221 return false; 0222 0223 auto it = std::lower_bound(templates_.begin(), templates_.end(), templ); 0224 templates_.insert(it, std::move(templ)); 0225 names.insert(name_full); 0226 0227 Q_EMIT loaded(templates_); 0228 0229 return true; 0230 } 0231 0232 settings::DocumentTemplates & settings::DocumentTemplates::instance() 0233 { 0234 static settings::DocumentTemplates instance; 0235 return instance; 0236 } 0237 0238 const std::vector<settings::DocumentTemplate> & settings::DocumentTemplates::templates() const 0239 { 0240 return templates_; 0241 }