Krita Source Code Documentation
Loading...
Searching...
No Matches
ResourceImporter.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2021 Agata Cacko <cacko.azh@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7#include "ResourceImporter.h"
8
9#include <QItemSelection>
10#include <QPainter>
11#include <QStandardPaths>
12#include <QMessageBox>
13
14#include <KoFileDialog.h>
15
16#include <KisResourceModel.h>
17#include <kis_assert.h>
18#include <KisResourceTypes.h>
19#include <KisMainWindow.h>
22#include <KisMimeDatabase.h>
23#include <KisStorageModel.h>
24#include <KisResourceLocator.h>
25#include <kis_config.h>
27
29
30// ------------ Warnings dialog ---------------
32{
33
34public:
35
36 FailureReasonsDialog(QWidget* parent, QMap<ResourceImporter::ImportFailureReason, QStringList> failureReasons)
37 : KoDialog(parent)
38 {
39 setCaption(i18n("Import of some files failed"));
40 setBaseSize(QSize(0, 0));
42
43 QVBoxLayout* layout = new QVBoxLayout(parent);
44 QWidget* widget = new QWidget(parent);
45 widget->setBaseSize(QSize(0, 0));
46
47 QList<ResourceImporter::ImportFailureReason> keys = failureReasons.keys();
48 for (int i = 0; i < keys.size(); i++) {
49 if (failureReasons[keys[i]].size() > 0) {
50 QLabel* label = new QLabel(widget);
51 QString text;
53 label->setText(i18nc("Warning message after failed attempt to import resources, after this label there is a box with a list of files",
54 "The following files couldn't be opened as resources:"));
56 label->setText(i18nc("Warning message after failed attempt to import resources, after this label there is a box with a list of files",
57 "The resource type of following files is unknown:"));
58 } else if (keys[i] == ResourceImporter::CancelledByTheUser) {
59 label->setText(i18nc("Warning message after failed attempt to import resources, after this label there is a box with a list of files",
60 "The import of following files has been cancelled:"));
61 } else if (keys[i] == ResourceImporter::StorageAlreadyExists) {
62 label->setText(i18nc("Warning message after failed attempt to import resources, after this label there is a box with a list of files",
63 "A resources bundle, an ASL or an ABR file with the same name already exists in the resources folder:"));
64 }
65
66 label->setWordWrap(true);
67 layout->addWidget(label);
68
69
70 QPlainTextEdit* textBox = new QPlainTextEdit(widget);
71 textBox->setBaseSize(0, 0);
72 for (int j = 0; j < failureReasons[keys[i]].size(); j++) {
73 textBox->appendPlainText(failureReasons[keys[i]][j]);
74 }
75 textBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
76
77 layout->addWidget(textBox, 0);
78
79 }
80 }
81
82
83 widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
84 widget->setLayout(layout);
85 widget->setGeometry(QRect(QPoint(0,0), layout->sizeHint()));
86 this->setMainWidget(widget);
87
88 this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
89 }
90
91};
92
93
94
95// ------------ Resource Importer --------------
96
98 : m_widgetParent(parent)
99{
100 initialize();
101}
102
103
108
110{
111 // TODO: remove debug after finishing the importer
112 bool debug = false;
113
114 if (startPath.isEmpty()) {
115 startPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
116 }
117
118 KoFileDialog dialog(m_widgetParent, KoFileDialog::OpenFiles, "krita_resources");
119 dialog.setDefaultDir(startPath);
120 dialog.setCaption(i18nc("Resource Importer file dialog title", "Import Resources and Resource Libraries"));
121 dialog.setMimeTypeFilters(m_allMimetypes);
122 QStringList filenames = dialog.filenames();
123
124
125 if (debug) qCritical() << "All filenames: " << filenames;
126
127
128 QMap<QString, QString> troublesomeFiles;
129 QStringList troublesomeMimetypes;
130
131 QMap<QString, QStringList> troublesomeFilesPerMimetype;
132
133
134
135 QMap<QString, QString> resourceTypePerFile;
136
137 QStringList successfullyImportedFiles;
138 QMap<ImportFailureReason, QStringList> failedFiles;
139 failedFiles.insert(StorageAlreadyExists, QStringList());
140 failedFiles.insert(MimetypeResourceTypeUnknown, QStringList());
141 failedFiles.insert(ResourceCannotBeLoaded, QStringList());
142 failedFiles.insert(CancelledByTheUser, QStringList());
143
144
145 for (int i = 0; i < filenames.count(); i++) {
146 QString mimetype = KisMimeDatabase::mimeTypeForFile(filenames[i]);
147 if (m_storagesMimetypes.contains(mimetype)) {
148 if (debug) qCritical() << "We're loading a storage here: " << filenames[i];
149
150 // import the bundle/asl/abr storage
151
153 if (debug) qCritical() << "checking for storage" << filenames[i] << QFileInfo(filenames[i]).fileName();
154 // TODO: three options in case of the same filename: cancel; overwrite; rename;
155 // but for now, let's just skip
156 bool skip = false;
157 if (KisResourceLocator::instance()->hasStorage(QFileInfo(filenames[i]).fileName())) {
158 skip = true;
159 /*
160 if (QMessageBox::warning(m_widgetParent, i18nc("@title:window", "Warning"),
161 i18n("There is already a resource library with this name installed. Do you want to overwrite it? Resource library name: "),
162 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) {
163 importMode = KisStorageModel::Rename;
164 }
165 else {
166 importMode = KisStorageModel::Overwrite;
167 }
168 */
169 }
170 if (skip || !KisStorageModel::instance()->importStorage(filenames[i], importMode)) {
171 failedFiles[StorageAlreadyExists] << filenames[i];
172 } else {
173 successfullyImportedFiles << filenames[i];
174 }
175
176 } else if (m_zipMimetypes.contains(mimetype)) {
177 // TODO: unpack a zip file and then proceed the same way like with others
178 } else if (m_resourceTypesForMimetype.contains(mimetype)) {
179 bool differentResourceTypes = m_resourceTypesForMimetype.value(mimetype).count() > 1;
180 if (differentResourceTypes) {
181 if (debug) qCritical() << "We have a difficult situation here!" << filenames[i];
182
183 troublesomeFiles.insert(filenames[i], mimetype);
184 if (!troublesomeMimetypes.contains(mimetype)) {
185 troublesomeMimetypes.append(mimetype);
186 }
187
188 if (!troublesomeFilesPerMimetype.contains(mimetype)) {
189 troublesomeFilesPerMimetype.insert(mimetype, QStringList());
190 }
191 troublesomeFilesPerMimetype[mimetype] = troublesomeFilesPerMimetype[mimetype] << filenames[i];
192
193 } else {
194 if (debug) qCritical() << "We're loading a" << mimetype << " here: " << filenames[i];
195 resourceTypePerFile.insert(filenames[i], m_resourceTypesForMimetype[mimetype][0]);
196 }
197 } else {
198 failedFiles[MimetypeResourceTypeUnknown] << filenames[i];
199 }
200
201 }
202
203 QMap<QString, QStringList> troublesomeResourceTypesPerMimetype;
204 for (int i = 0; i < troublesomeMimetypes.size(); i++) {
205 troublesomeResourceTypesPerMimetype.insert(troublesomeMimetypes[i], m_resourceTypesForMimetype[troublesomeMimetypes[i]]);
206 }
207
208 if (troublesomeMimetypes.count() > 0) {
209 DlgResourceTypeForFile dlg(m_widgetParent, troublesomeResourceTypesPerMimetype);
210 if (dlg.exec() == QDialog::Accepted) {
211 for (int i = 0; i < troublesomeMimetypes.count(); i++) {
212 QString resourceType = dlg.getResourceTypeForMimetype(troublesomeMimetypes[i]);
213 QString mimetype = troublesomeMimetypes[i];
214 if (troublesomeFilesPerMimetype.contains(mimetype)) {
215 for (int j = 0; j < troublesomeFilesPerMimetype[mimetype].size(); j++) {
216 resourceTypePerFile.insert(troublesomeFilesPerMimetype[mimetype][j], resourceType);
217 }
218 }
219 }
220 } else {
221 for (int i = 0; i < troublesomeMimetypes.count(); i++) {
222 for (int j = 0; j < troublesomeFilesPerMimetype[troublesomeMimetypes[i]].size(); j++) {
223 failedFiles[CancelledByTheUser] << troublesomeFilesPerMimetype[troublesomeMimetypes[i]][j];
224 }
225 }
226 }
227 }
228
229
230
231
232 if (debug) qCritical() << "Resource types for mimetype: ";
233
234 for (int i = 0; i < m_resourceTypesForMimetype.keys().size(); i++) {
235 if (m_resourceTypesForMimetype[m_resourceTypesForMimetype.keys()[i]].size() > 1) {
236 if (debug) qCritical() << m_resourceTypesForMimetype.keys()[i] << m_resourceTypesForMimetype[m_resourceTypesForMimetype.keys()[i]];
237 }
238 }
239
240 QString resourceLocationBase = KisResourceLocator::instance()->resourceLocationBase();
241
242 QStringList resourceFiles = resourceTypePerFile.keys();
243 for (int i = 0; i < resourceFiles.count(); i++) {
244 QString resourceType = resourceTypePerFile[resourceFiles[i]];
245 if (debug) qCritical() << "Loading " << resourceFiles[i] << "as" << resourceType;
246 if (m_resourceModelsForResourceType.contains(resourceType)) {
247 if (debug) qCritical() << "We do have a resource model for that!";
249
250 bool allowOverwrite = false;
251
252 // first check if we are going to overwrite anything
253 if (model->importWillOverwriteResource(resourceFiles[i])) {
255 continue;
256 } else {
257 allowOverwrite = true;
258 }
259 }
260
261 KoResourceSP res = model->importResourceFile(resourceFiles[i], allowOverwrite);
262 if (res.isNull()) {
263 if (debug) qCritical() << "But the resource is null :( ";
264 failedFiles[ResourceCannotBeLoaded] << resourceFiles[i];
265 } else {
266 if (debug) qCritical() << "The resource isn't null, great!";
267 successfullyImportedFiles << resourceFiles[i];
268 }
269 } else {
270 failedFiles[MimetypeResourceTypeUnknown] << resourceFiles[i];
271 }
272 }
273
274 if (debug) qCritical() << "Failed files: " << failedFiles;
275 if (debug) qCritical() << "Successfully imported files: " << successfullyImportedFiles;
276
277 QList<ImportFailureReason> keys = failedFiles.keys();
278 int failedFilesCount = 0;
279 for (int i = 0; i < keys.size(); i++) {
280 failedFilesCount += failedFiles[keys[i]].size();
281 }
282 if (failedFilesCount > 0) {
283 FailureReasonsDialog dlg(m_widgetParent, failedFiles);
284 dlg.exec();
285 }
286
287}
288
290{
291 m_storagesMimetypes = QStringList() << "application/x-krita-bundle"
292 << "image/x-adobe-brushlibrary"
293 << "application/x-photoshop-style-library";
294
295 m_zipMimetypes = QStringList(); // << "application/zip"; // TODO: implement for zip archives
296
297 QStringList resourceTypes;
299 for (int i = 0; i < model.rowCount(); i++) {
300 QModelIndex idx = model.index(i, 0);
301 resourceTypes << model.data(idx, Qt::UserRole + KisResourceTypeModel::ResourceType).toString();
302 }
303 qCritical() << "resource types = " << resourceTypes;
304
307
308 QStringList mimetypes;
309 for (int i = 0; i < resourceTypes.count(); i++) {
311
312 // remove mypaint brushes for now, because we'd need to figure out an UX for them
313 // probably 1) remove all the _prev.png files from a list to import as patterns or brush tips
314 // and 2) when importing a .myb file, find a proper .png file alongside it to import together
315 mime.removeAll("application/x-mypaint-brush");
316
317 m_mimetypeForResourceType.insert(resourceTypes[i], mime);
318 mimetypes << mime;
319 for (int j = 0; j < mime.count(); j++) {
320 if (m_resourceTypesForMimetype.contains(mime[j])) {
321 if (!m_resourceTypesForMimetype[mime[j]].contains(resourceTypes[i])) {
322 m_resourceTypesForMimetype[mime[j]].append(resourceTypes[i]);
323 }
324 } else {
325 m_resourceTypesForMimetype.insert(mime[j], QStringList() << resourceTypes[i]);
326 }
327 }
328 }
329
332 m_allMimetypes << mimetypes;
333
334
335 m_allMimetypes.removeDuplicates();
336
337
338
339}
340
342{
344 for (int i = 0; i < model.rowCount(); i++) {
345 QModelIndex idx = model.index(i, 0);
346 QString resourceType = model.data(idx, Qt::UserRole + KisResourceTypeModel::ResourceType).toString();
347 if (!m_resourceModelsForResourceType.contains(resourceType)) {
348 KisResourceModel* model = new KisResourceModel(resourceType);
349 if (model) {
350 m_resourceModelsForResourceType.insert(resourceType, model);
351 } else {
352 dbgResources << "There is no KisResourceModel available for " << resourceType;
353 }
354 }
355 }
356}
357
359{
360 if (!m_isInitialized) {
363 m_isInitialized = true;
364 }
365}
366
367
368
369
370
371
372
373
QList< QString > QStringList
QString getResourceTypeForMimetype(QString mimetype)
FailureReasonsDialog(QWidget *parent, QMap< ResourceImporter::ImportFailureReason, QStringList > failureReasons)
static QString mimeTypeForFile(const QString &file, bool checkExistingFiles=true)
Find the mimetype for the given filename. The filename must include a suffix.
static KisResourceLoaderRegistry * instance()
QStringList mimeTypes(const QString &resourceType) const
QString resourceLocationBase() const
resourceLocationBase is the place where all resource storages (folder, bundles etc....
static KisResourceLocator * instance()
The KisResourceModel class provides the main access to resources. It is possible to filter the resour...
KoResourceSP importResourceFile(const QString &filename, const bool allowOverwrite, const QString &storageId=QString("")) override
importResourceFile
bool importWillOverwriteResource(const QString &fileName, const QString &storageLocation=QString()) const override
importWillOverwriteResource checks is importing a resource with this filename will overwrite anything
QVariant data(const QModelIndex &index, int role) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
static bool userAllowsOverwrite(QWidget *widgetParent, QString resourceFilepath)
static KisStorageModel * instance()
A dialog base class with standard buttons and predefined layouts.
Definition KoDialog.h:116
void setMainWidget(QWidget *widget)
Definition KoDialog.cpp:354
virtual void setCaption(const QString &caption)
Definition KoDialog.cpp:498
void setButtons(ButtonCodes buttonMask)
Definition KoDialog.cpp:195
@ Ok
Show Ok button. (this button accept()s the dialog; result set to QDialog::Accepted)
Definition KoDialog.h:127
void importResources(QString startPath="")
QStringList m_storagesMimetypes
ResourceImporter(QWidget *parent)
QMap< QString, QStringList > m_mimetypeForResourceType
QStringList m_zipMimetypes
QMap< QString, KisResourceModel * > m_resourceModelsForResourceType
QMap< QString, QStringList > m_resourceTypesForMimetype
QStringList m_allMimetypes
#define dbgResources
Definition kis_debug.h:43