Krita Source Code Documentation
Loading...
Searching...
No Matches
dlg_create_bundle.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2014 Victor Lafon metabolic.ewilan @hotmail.fr
3 * SPDX-FileCopyrightText: 2020 Agata Cacko cacko.azh @gmail.com
4 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
5 * SPDX-FileCopyrightText: 2023 Srirupa Datta <srirupa.sps@gmail.com>
6 *
7 * SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9#include "dlg_create_bundle.h"
11#include "page_tag_chooser.h"
12#include "page_metadata_info.h"
13#include "page_bundle_saver.h"
14#include "ui_wdgdlgcreatebundle.h"
15#include "wdg_side.h"
16
17#include <QDebug>
18#include <QFileInfo>
19#include <QGridLayout>
20#include <QHash>
21#include <QMessageBox>
22#include <QPainter>
23#include <QProcessEnvironment>
24#include <QStack>
25#include <QStandardPaths>
26#include <QTableWidget>
27#include <QVBoxLayout>
28
30#include <KoDocumentInfo.h>
31#include <KoFileDialog.h>
32#include <kis_config.h>
33#include <kis_icon.h>
34#include <KoResource.h>
35#include <KoResourceServer.h>
37#include <KoResource.h>
38#include <kstandardguiitem.h>
39
42#include <dlg_embed_tags.h>
45#include "KisBundleStorage.h"
46#include <KisResourceLocator.h>
47#include <KisResourceStorage.h>
48#include <QUuid>
49#include <KisStorageModel.h>
50#include <wdgtagselection.h>
51
52
54 : QWizard(parent)
55 , m_ui(new Ui::WdgDlgCreateBundle)
56 , m_bundle(bundle)
57 , m_storageAdded(false)
58{
59 m_ui->setupUi(this);
60 setWizardStyle(QWizard::ClassicStyle);
61
62 WdgSide *wdgSide = new WdgSide(m_bundle);
63
68
69 setPage(1, m_pageResourceChooser);
70 setPage(2, m_pageTagChooser);
71 setPage(3, m_pageMetadataInfo);
72 setPage(4, m_pageBundleSaver);
73
74 setSideWidget(wdgSide);
75 setButtonText(QWizard::FinishButton, i18n("Save"));
76
77 connect(this, SIGNAL(currentIdChanged(int)), wdgSide, SLOT(focusLabel(int)));
78 connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(updateTitle(int)));
79
80 KisResourceTypeModel resourceTypesModel;
81 for (int i = 0; i < resourceTypesModel.rowCount(); i++) {
82 QModelIndex idx = resourceTypesModel.index(i, 0);
83 QString resourceType = resourceTypesModel.data(idx, Qt::UserRole + KisResourceTypeModel::ResourceType).toString();
84 m_count.insert(resourceType, 0);
85 }
86
87 m_bundleCreaterMode = "Creator";
88
89 if (m_bundle) {
90 m_bundleCreaterMode = "Editor";
91 m_bundleStorage = new KisBundleStorage(m_bundle->filename());
92
94 #if defined HAVE_SEEXPR
95 resourceTypes << ResourceType::SeExprScripts;
96 #endif
98
99 for (int i = 0; i < resourceTypes.size(); i++) {
101 int count = 0;
102 while (iter->hasNext()) {
103 iter->next();
104 count++;
105 }
106 m_count[resourceTypes[i]] = count;
107
109 while (tagIter->hasNext()) {
110 tagIter->next();
111 m_tags.insert(tagIter->tag()->name());
112 }
113 }
114
115 }
116
117 connect(m_pageResourceChooser, SIGNAL(countUpdated()), m_pageBundleSaver, SLOT(onCountUpdated()));
118 connect(m_pageTagChooser, SIGNAL(tagsUpdated()), m_pageBundleSaver, SLOT(onTagsUpdated()));
119}
120
122{
123 if (!m_bundle) {
124 QString title = i18n("Create Resource Bundle");
125
126 switch(id) {
127 case 1: title = i18n("Choose Resources"); break;
128 case 2: title = i18n("Choose Tags"); break;
129 case 3: title = i18n("Enter Bundle Details"); break;
130 case 4: title = i18n("Enter Save Location"); break;
131 }
132
133 setWindowTitle(title);
134 } else {
135 QString title = i18n("Edit Resource Bundle");
136
137 switch(id) {
138 case 1: title = i18n("Edit Resources"); break;
139 case 2: title = i18n("Edit Tags"); break;
140 case 3: title = i18n("Edit Bundle Details"); break;
141 case 4: title = i18n("Edit Save Location"); break;
142 }
143
144 setWindowTitle(title);
145 }
146}
147
152
154{
155 QVector<KisTagSP> tagsToEmbed;
156 KisTagModel *tagModel = new KisTagModel(resourceType);
157
158 for (int i = 0; i < tagModel->rowCount(); i++) {
159 QModelIndex idx = tagModel->index(i, 0);
160 int id = tagModel->data(idx, Qt::UserRole + KisAllTagsModel::Id).toInt();
161 QString url = tagModel->data(idx, Qt::UserRole + KisAllTagsModel::Url).toString();
162 if (m_selectedTagIds.contains(id)) {
163 KisTagSP tag = tagModel->tagForUrl(url);
164 tagsToEmbed << tag;
165 }
166 }
167
168 return tagsToEmbed;
169}
170
172{
173 QMap<QString, QSharedPointer<KisResourceModel>> modelsPerResourceType;
174 KisResourceTypeModel resourceTypesModel;
175 for (int i = 0; i < resourceTypesModel.rowCount(); i++) {
176 QModelIndex idx = resourceTypesModel.index(i, 0);
177 QString resourceType = resourceTypesModel.data(idx, Qt::UserRole + KisResourceTypeModel::ResourceType).toString();
179 // BUG: 445408 Ensure potentially linked but disabled resources are visible
180 model->setResourceFilter(KisResourceModel::ShowAllResources);
181 modelsPerResourceType.insert(resourceType, model);
182 }
183
184 QStringList resourceTypes = modelsPerResourceType.keys();
186
187 QStack<int> allResourcesIds;
188 Q_FOREACH(int id, selectedResourcesIds) {
189 allResourcesIds << id;
190 }
191
192 // note: if there are repetitions, it's fine; the bundle will filter them out
193 QHash<QPair<QString, QString>, std::size_t> usedFilenames;
194
195 while(!allResourcesIds.isEmpty()) {
196 const int id = allResourcesIds.takeFirst();
197 KoResourceSP res;
198 QString resourceTypeHere = "";
200 for (const auto &type: resourceTypes) {
201 res = modelsPerResourceType[type]->resourceForId(id);
202 if (!res.isNull()) {
203 resModel = modelsPerResourceType[type];
204 resourceTypeHere = type;
205 break;
206 }
207 }
208 if (!res) {
209 warnKrita << "No resource for id " << id;
210 continue;
211 }
212 const auto prettyFilename = createPrettyFilenameFromName(res);
213
214 if (usedFilenames.value({res->resourceType().first, prettyFilename}, 0) > 0) {
215 QMessageBox::warning(
216 this,
217 i18nc("@title:window", "Krita"),
218 i18nc("Warning message", "More than one resource share the same file name '%1'. Please export them in separate bundles.", prettyFilename));
219 return false;
220 }
221
222 usedFilenames[{res->resourceType().first, prettyFilename}]+= 1;
223
225
226 QVector<KisTagSP> tags = getTagsForEmbeddingInResource(resModel->tagsForResource(id), res->resourceType().first);
227 bundle->addResource(res->resourceType().first, res->filename(), tags, res->md5Sum(), res->resourceId(), prettyFilename);
228
229 if (m_bundleCreaterMode == "Creator") {
230 QList<KoResourceLoadResult> linkedResources = res->linkedResources(KisGlobalResourcesInterface::instance());
231 Q_FOREACH(KoResourceLoadResult linkedResource, linkedResources) {
232 // we have requested linked resources, how can it be an embedded one?
234
235 KoResourceSP resource = linkedResource.resource();
236
237 if (!resource) {
238 qWarning() << "WARNING: DlgCreateBundle::putResourcesInTheBundle couldn't fetch a linked resource" << linkedResource.signature();
239 continue;
240 }
241
242 if (!allResourcesIds.contains(resource->resourceId())) {
243 allResourcesIds.append(resource->resourceId());
244 }
245 }
246 }
247 }
248 return true;
249}
250
252{
256 if (bundle->metaData(KisResourceStorage::s_meta_initial_creator, "").isEmpty()) {
258 }
263
264 bundle->setThumbnail(m_pageMetadataInfo->thumbnail());
265
266 // For compatibility
267 bundle->setMetaData("email", m_pageMetadataInfo->email());
268 bundle->setMetaData("license", m_pageMetadataInfo->license());
269 bundle->setMetaData("website", m_pageMetadataInfo->website());
270}
271
273{
274 QString resourceType = resource->resourceType().first;
275 if (resourceType == ResourceType::Patterns
276 || resourceType == ResourceType::Brushes) {
277 // don't change the filename, if the resource is likely to be linked
278 // by filename to another resource
279 return resource->filename();
280 }
281 // to make sure patterns are saved correctly
282 // note that for now (TM) there are no double-suffixes in resources (like '.tar.gz')
283 // otherwise this code should use completeSuffix() and remove the versioning number
284 // (since that's something we want to get rid of)
285 const auto fileInfo = QFileInfo(resource->filename());
286 const auto prefix = fileInfo.dir();
287 // remove the suffix if the name has a suffix (happens for png patterns)
288 const auto nameWithoutSuffix = QFileInfo(resource->name()).completeBaseName();
289 const auto suffix = fileInfo.suffix();
290 return QDir::cleanPath(prefix.filePath(nameWithoutSuffix + "." + suffix));
291}
292
294{
295 KisConfig cfg(false);
296 if (full) {
297 cfg.writeEntry<QString>("BundleName", m_pageMetadataInfo->bundleName());
298 cfg.writeEntry<QString>("BundleDescription", m_pageMetadataInfo->description());
299 cfg.writeEntry<QString>("BundleImage", m_pageMetadataInfo->previewImage());
300 } else {
301 cfg.writeEntry<QString>("BundleName", "");
302 cfg.writeEntry<QString>("BundleDescription", "");
303 cfg.writeEntry<QString>("BundleImage", "");
304 }
305 cfg.writeEntry<QString>("BundleExportLocation", m_pageBundleSaver->saveLocation());
306 cfg.writeEntry<QString>("BundleAuthorName", m_pageMetadataInfo->authorName());
307 cfg.writeEntry<QString>("BundleAuthorEmail", m_pageMetadataInfo->email());
308 cfg.writeEntry<QString>("BundleWebsite", m_pageMetadataInfo->website());
309 cfg.writeEntry<QString>("BundleLicense", m_pageMetadataInfo->license());
310}
311
313{
314 QString name = m_pageMetadataInfo->bundleName();
315 QString filename = QString("%1/%2.bundle").arg(m_pageBundleSaver->saveLocation(), name.replace(" ", "_"));
316
317 if (name.isEmpty()) {
319 QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("The resource bundle name cannot be empty."));
320 return;
321 }
323
324 if (m_pageBundleSaver->saveLocation().isEmpty() ){
326 QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("The save location cannot be empty."));
327 return;
328 }
329 else {
330 QFileInfo fileInfo(filename);
331
332 if (fileInfo.exists()) {
334
335 QMessageBox msgBox(this);
336 msgBox.setIcon(QMessageBox::Question);
337 msgBox.setText(i18nc("In a dialog asking whether to overwrite a bundle (resource pack)", "A bundle with this name already exists."));
338 msgBox.setInformativeText(i18nc("In a dialog regarding overwriting a bundle (resource pack)", "Do you want to overwrite the existing bundle?"));
339 msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
340 msgBox.setDefaultButton(QMessageBox::Cancel);
341 int ret = msgBox.exec();
342 if (ret == QMessageBox::Cancel) {
343 return;
344 }
345 }
346
347 if (!m_bundle) {
348 saveToConfiguration(false);
349
350 m_bundle.reset(new KoResourceBundle(filename));
353 return;
354 }
355 if (!m_bundle->save()) {
357 QMessageBox::critical(this,
358 i18nc("@title:window", "Krita"),
359 i18n("Could not open '%1' for saving.", filename));
360 m_bundle.reset();
361 return;
362 }
363 } else {
364 saveToConfiguration(false);
365
366 m_bundle.reset(new KoResourceBundle(filename));
369 return;
370 }
371 if (!m_bundle->save()) {
373 QMessageBox::critical(this,
374 i18nc("@title:window", "Krita"),
375 i18n("Could not open '%1' for saving.", filename));
376 m_bundle.reset();
377 return;
378 }
379 }
380 QWizard::accept();
381 }
382}
383
385{
386 if (!m_bundle) {
388 }
389
390 QWizard::reject();
391}
392
393
QList< QString > QStringList
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
void reject() override
bool putResourcesInTheBundle(KoResourceBundleSP bundle)
QList< int > m_selectedTagIds
PageMetadataInfo * m_pageMetadataInfo
void accept() override
Ui::WdgDlgCreateBundle * m_ui
PageTagChooser * m_pageTagChooser
QString createPrettyFilenameFromName(KoResourceSP resource) const
void putMetaDataInTheBundle(KoResourceBundleSP bundle) const
PageBundleSaver * m_pageBundleSaver
KoResourceBundleSP m_bundle
QVector< KisTagSP > getTagsForEmbeddingInResource(QVector< KisTagSP > resourceTags, QString resourceType) const
PageResourceChooser * m_pageResourceChooser
QMap< QString, int > m_count
void saveToConfiguration(bool full)
DlgCreateBundle(KoResourceBundleSP bundle=nullptr, QWidget *parent=0)
QSet< QString > m_tags
KisBundleStorage * m_bundleStorage
QSharedPointer< KisResourceStorage::ResourceIterator > resources(const QString &resourceType) override
QSharedPointer< KisResourceStorage::TagIterator > tags(const QString &resourceType) override
void writeEntry(const QString &name, const T &value)
Definition kis_config.h:779
static KisResourcesInterfaceSP instance()
The KisResourceModel class provides the main access to resources. It is possible to filter the resour...
static const QString s_meta_author
static const QString s_meta_title
static const QString s_meta_email
static const QString s_meta_license
static const QString s_meta_description
static const QString s_meta_website
static const QString s_meta_initial_creator
static const QString s_meta_creator
QVariant data(const QModelIndex &index, int role) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
KisTagSP tagForUrl(const QString &url) const override
Retrieve a tag by url.
A KoResourceBundle is a zip file that contains resources, some metadata about the creator of the bund...
KoResourceSP resource() const noexcept
KoResourceSignature signature() const
QString saveLocation() const
QString bundleName() const
QString previewImage() const
QString email() const
QString license() const
QString website() const
QString authorName() const
QImage thumbnail() const
QString description() const
QList< int > getSelectedResourcesIds()
QList< int > selectedTagIds()
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define warnKrita
Definition kis_debug.h:87
const QString Palettes
const QString Brushes
const QString GamutMasks
const QString Patterns
const QString SeExprScripts
const QString Gradients
const QString Workspaces
const QString PaintOpPresets