File indexing completed on 2024-12-22 04:15:11
0001 /* 0002 * SPDX-FileCopyrightText: 2021 Agata Cacko <cacko.azh@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "ResourceImporter.h" 0008 0009 #include <QItemSelection> 0010 #include <QPainter> 0011 #include <QStandardPaths> 0012 #include <QMessageBox> 0013 0014 #include <KoFileDialog.h> 0015 0016 #include <KisResourceModel.h> 0017 #include <kis_assert.h> 0018 #include <KisResourceTypes.h> 0019 #include <KisMainWindow.h> 0020 #include <KisResourceTypeModel.h> 0021 #include <KisResourceLoaderRegistry.h> 0022 #include <KisMimeDatabase.h> 0023 #include <KisStorageModel.h> 0024 #include <KisResourceLocator.h> 0025 #include <kis_config.h> 0026 #include <KisResourceUserOperations.h> 0027 0028 #include "DlgResourceTypeForFile.h" 0029 0030 // ------------ Warnings dialog --------------- 0031 class FailureReasonsDialog : public KoDialog 0032 { 0033 0034 public: 0035 0036 FailureReasonsDialog(QWidget* parent, QMap<ResourceImporter::ImportFailureReason, QStringList> failureReasons) 0037 : KoDialog(parent) 0038 { 0039 setCaption(i18n("Import of some files failed")); 0040 setBaseSize(QSize(0, 0)); 0041 setButtons(ButtonCode::Ok); 0042 0043 QVBoxLayout* layout = new QVBoxLayout(parent); 0044 QWidget* widget = new QWidget(parent); 0045 widget->setBaseSize(QSize(0, 0)); 0046 0047 QList<ResourceImporter::ImportFailureReason> keys = failureReasons.keys(); 0048 for (int i = 0; i < keys.size(); i++) { 0049 if (failureReasons[keys[i]].size() > 0) { 0050 QLabel* label = new QLabel(widget); 0051 QString text; 0052 if (keys[i] == ResourceImporter::ResourceCannotBeLoaded) { 0053 label->setText(i18nc("Warning message after failed attempt to import resources, after this label there is a box with a list of files", 0054 "The following files couldn't be opened as resources:")); 0055 } else if (keys[i] == ResourceImporter::MimetypeResourceTypeUnknown) { 0056 label->setText(i18nc("Warning message after failed attempt to import resources, after this label there is a box with a list of files", 0057 "The resource type of following files is unknown:")); 0058 } else if (keys[i] == ResourceImporter::CancelledByTheUser) { 0059 label->setText(i18nc("Warning message after failed attempt to import resources, after this label there is a box with a list of files", 0060 "The import of following files has been cancelled:")); 0061 } else if (keys[i] == ResourceImporter::StorageAlreadyExists) { 0062 label->setText(i18nc("Warning message after failed attempt to import resources, after this label there is a box with a list of files", 0063 "A resources bundle, an ASL or an ABR file with the same name already exists in the resources folder:")); 0064 } 0065 0066 label->setWordWrap(true); 0067 layout->addWidget(label); 0068 0069 0070 QPlainTextEdit* textBox = new QPlainTextEdit(widget); 0071 textBox->setBaseSize(0, 0); 0072 for (int j = 0; j < failureReasons[keys[i]].size(); j++) { 0073 textBox->appendPlainText(failureReasons[keys[i]][j]); 0074 } 0075 textBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); 0076 0077 layout->addWidget(textBox, 0); 0078 0079 } 0080 } 0081 0082 0083 widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); 0084 widget->setLayout(layout); 0085 widget->setGeometry(QRect(QPoint(0,0), layout->sizeHint())); 0086 this->setMainWidget(widget); 0087 0088 this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); 0089 } 0090 0091 }; 0092 0093 0094 0095 // ------------ Resource Importer -------------- 0096 0097 ResourceImporter::ResourceImporter(QWidget *parent) 0098 : m_widgetParent(parent) 0099 { 0100 initialize(); 0101 } 0102 0103 0104 ResourceImporter::~ResourceImporter() 0105 { 0106 qDeleteAll(m_resourceModelsForResourceType); 0107 } 0108 0109 void ResourceImporter::importResources(QString startPath) 0110 { 0111 // TODO: remove debug after finishing the importer 0112 bool debug = false; 0113 0114 if (startPath.isEmpty()) { 0115 startPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); 0116 } 0117 0118 KoFileDialog dialog(m_widgetParent, KoFileDialog::OpenFiles, "krita_resources"); 0119 dialog.setDefaultDir(startPath); 0120 dialog.setCaption(i18nc("Resource Importer file dialog title", "Import Resources and Resource Libraries")); 0121 dialog.setMimeTypeFilters(m_allMimetypes); 0122 QStringList filenames = dialog.filenames(); 0123 0124 0125 if (debug) qCritical() << "All filenames: " << filenames; 0126 0127 0128 QMap<QString, QString> troublesomeFiles; 0129 QStringList troublesomeMimetypes; 0130 0131 QMap<QString, QStringList> troublesomeFilesPerMimetype; 0132 0133 0134 0135 QMap<QString, QString> resourceTypePerFile; 0136 0137 QStringList successfullyImportedFiles; 0138 QMap<ImportFailureReason, QStringList> failedFiles; 0139 failedFiles.insert(StorageAlreadyExists, QStringList()); 0140 failedFiles.insert(MimetypeResourceTypeUnknown, QStringList()); 0141 failedFiles.insert(ResourceCannotBeLoaded, QStringList()); 0142 failedFiles.insert(CancelledByTheUser, QStringList()); 0143 0144 0145 for (int i = 0; i < filenames.count(); i++) { 0146 QString mimetype = KisMimeDatabase::mimeTypeForFile(filenames[i]); 0147 if (m_storagesMimetypes.contains(mimetype)) { 0148 if (debug) qCritical() << "We're loading a storage here: " << filenames[i]; 0149 0150 // import the bundle/asl/abr storage 0151 0152 KisStorageModel::StorageImportOption importMode = KisStorageModel::None; 0153 if (debug) qCritical() << "checking for storage" << filenames[i] << QFileInfo(filenames[i]).fileName(); 0154 // TODO: three options in case of the same filename: cancel; overwrite; rename; 0155 // but for now, let's just skip 0156 bool skip = false; 0157 if (KisResourceLocator::instance()->hasStorage(QFileInfo(filenames[i]).fileName())) { 0158 skip = true; 0159 /* 0160 if (QMessageBox::warning(m_widgetParent, i18nc("@title:window", "Warning"), 0161 i18n("There is already a resource library with this name installed. Do you want to overwrite it? Resource library name: "), 0162 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) { 0163 importMode = KisStorageModel::Rename; 0164 } 0165 else { 0166 importMode = KisStorageModel::Overwrite; 0167 } 0168 */ 0169 } 0170 if (skip || !KisStorageModel::instance()->importStorage(filenames[i], importMode)) { 0171 failedFiles[StorageAlreadyExists] << filenames[i]; 0172 } else { 0173 successfullyImportedFiles << filenames[i]; 0174 } 0175 0176 } else if (m_zipMimetypes.contains(mimetype)) { 0177 // TODO: unpack a zip file and then proceed the same way like with others 0178 } else if (m_resourceTypesForMimetype.contains(mimetype)) { 0179 bool differentResourceTypes = m_resourceTypesForMimetype.value(mimetype).count() > 1; 0180 if (differentResourceTypes) { 0181 if (debug) qCritical() << "We have a difficult situation here!" << filenames[i]; 0182 0183 troublesomeFiles.insert(filenames[i], mimetype); 0184 if (!troublesomeMimetypes.contains(mimetype)) { 0185 troublesomeMimetypes.append(mimetype); 0186 } 0187 0188 if (!troublesomeFilesPerMimetype.contains(mimetype)) { 0189 troublesomeFilesPerMimetype.insert(mimetype, QStringList()); 0190 } 0191 troublesomeFilesPerMimetype[mimetype] = troublesomeFilesPerMimetype[mimetype] << filenames[i]; 0192 0193 } else { 0194 if (debug) qCritical() << "We're loading a" << mimetype << " here: " << filenames[i]; 0195 resourceTypePerFile.insert(filenames[i], m_resourceTypesForMimetype[mimetype][0]); 0196 } 0197 } else { 0198 failedFiles[MimetypeResourceTypeUnknown] << filenames[i]; 0199 } 0200 0201 } 0202 0203 QMap<QString, QStringList> troublesomeResourceTypesPerMimetype; 0204 for (int i = 0; i < troublesomeMimetypes.size(); i++) { 0205 troublesomeResourceTypesPerMimetype.insert(troublesomeMimetypes[i], m_resourceTypesForMimetype[troublesomeMimetypes[i]]); 0206 } 0207 0208 if (troublesomeMimetypes.count() > 0) { 0209 DlgResourceTypeForFile dlg(m_widgetParent, troublesomeResourceTypesPerMimetype); 0210 if (dlg.exec() == QDialog::Accepted) { 0211 for (int i = 0; i < troublesomeMimetypes.count(); i++) { 0212 QString resourceType = dlg.getResourceTypeForMimetype(troublesomeMimetypes[i]); 0213 QString mimetype = troublesomeMimetypes[i]; 0214 if (troublesomeFilesPerMimetype.contains(mimetype)) { 0215 for (int j = 0; j < troublesomeFilesPerMimetype[mimetype].size(); j++) { 0216 resourceTypePerFile.insert(troublesomeFilesPerMimetype[mimetype][j], resourceType); 0217 } 0218 } 0219 } 0220 } else { 0221 for (int i = 0; i < troublesomeMimetypes.count(); i++) { 0222 for (int j = 0; j < troublesomeFilesPerMimetype[troublesomeMimetypes[i]].size(); j++) { 0223 failedFiles[CancelledByTheUser] << troublesomeFilesPerMimetype[troublesomeMimetypes[i]][j]; 0224 } 0225 } 0226 } 0227 } 0228 0229 0230 0231 0232 if (debug) qCritical() << "Resource types for mimetype: "; 0233 0234 for (int i = 0; i < m_resourceTypesForMimetype.keys().size(); i++) { 0235 if (m_resourceTypesForMimetype[m_resourceTypesForMimetype.keys()[i]].size() > 1) { 0236 if (debug) qCritical() << m_resourceTypesForMimetype.keys()[i] << m_resourceTypesForMimetype[m_resourceTypesForMimetype.keys()[i]]; 0237 } 0238 } 0239 0240 QString resourceLocationBase = KisResourceLocator::instance()->resourceLocationBase(); 0241 0242 QStringList resourceFiles = resourceTypePerFile.keys(); 0243 for (int i = 0; i < resourceFiles.count(); i++) { 0244 QString resourceType = resourceTypePerFile[resourceFiles[i]]; 0245 if (debug) qCritical() << "Loading " << resourceFiles[i] << "as" << resourceType; 0246 if (m_resourceModelsForResourceType.contains(resourceType)) { 0247 if (debug) qCritical() << "We do have a resource model for that!"; 0248 KisResourceModel* model = m_resourceModelsForResourceType[resourceType]; 0249 0250 bool allowOverwrite = false; 0251 0252 // first check if we are going to overwrite anything 0253 if (model->importWillOverwriteResource(resourceFiles[i])) { 0254 if(!KisResourceUserOperations::userAllowsOverwrite(m_widgetParent, resourceFiles[i])) { 0255 continue; 0256 } else { 0257 allowOverwrite = true; 0258 } 0259 } 0260 0261 KoResourceSP res = model->importResourceFile(resourceFiles[i], allowOverwrite); 0262 if (res.isNull()) { 0263 if (debug) qCritical() << "But the resource is null :( "; 0264 failedFiles[ResourceCannotBeLoaded] << resourceFiles[i]; 0265 } else { 0266 if (debug) qCritical() << "The resource isn't null, great!"; 0267 successfullyImportedFiles << resourceFiles[i]; 0268 } 0269 } else { 0270 failedFiles[MimetypeResourceTypeUnknown] << resourceFiles[i]; 0271 } 0272 } 0273 0274 if (debug) qCritical() << "Failed files: " << failedFiles; 0275 if (debug) qCritical() << "Successfully imported files: " << successfullyImportedFiles; 0276 0277 QList<ImportFailureReason> keys = failedFiles.keys(); 0278 int failedFilesCount = 0; 0279 for (int i = 0; i < keys.size(); i++) { 0280 failedFilesCount += failedFiles[keys[i]].size(); 0281 } 0282 if (failedFilesCount > 0) { 0283 FailureReasonsDialog dlg(m_widgetParent, failedFiles); 0284 dlg.exec(); 0285 } 0286 0287 } 0288 0289 void ResourceImporter::prepareTypesMaps() 0290 { 0291 m_storagesMimetypes = QStringList() << "application/x-krita-bundle" 0292 << "image/x-adobe-brushlibrary" 0293 << "application/x-photoshop-style-library"; 0294 0295 m_zipMimetypes = QStringList(); // << "application/zip"; // TODO: implement for zip archives 0296 0297 QStringList resourceTypes; 0298 KisResourceTypeModel model; 0299 for (int i = 0; i < model.rowCount(); i++) { 0300 QModelIndex idx = model.index(i, 0); 0301 resourceTypes << model.data(idx, Qt::UserRole + KisResourceTypeModel::ResourceType).toString(); 0302 } 0303 qCritical() << "resource types = " << resourceTypes; 0304 0305 m_mimetypeForResourceType.clear(); 0306 m_resourceTypesForMimetype.clear(); 0307 0308 QStringList mimetypes; 0309 for (int i = 0; i < resourceTypes.count(); i++) { 0310 QStringList mime = KisResourceLoaderRegistry::instance()->mimeTypes(resourceTypes[i]); 0311 0312 // remove mypaint brushes for now, because we'd need to figure out an UX for them 0313 // probably 1) remove all the _prev.png files from a list to import as patterns or brush tips 0314 // and 2) when importing a .myb file, find a proper .png file alongside it to import together 0315 mime.removeAll("application/x-mypaint-brush"); 0316 0317 m_mimetypeForResourceType.insert(resourceTypes[i], mime); 0318 mimetypes << mime; 0319 for (int j = 0; j < mime.count(); j++) { 0320 if (m_resourceTypesForMimetype.contains(mime[j])) { 0321 if (!m_resourceTypesForMimetype[mime[j]].contains(resourceTypes[i])) { 0322 m_resourceTypesForMimetype[mime[j]].append(resourceTypes[i]); 0323 } 0324 } else { 0325 m_resourceTypesForMimetype.insert(mime[j], QStringList() << resourceTypes[i]); 0326 } 0327 } 0328 } 0329 0330 m_allMimetypes << m_storagesMimetypes; 0331 m_allMimetypes << m_zipMimetypes; 0332 m_allMimetypes << mimetypes; 0333 0334 0335 m_allMimetypes.removeDuplicates(); 0336 0337 0338 0339 } 0340 0341 void ResourceImporter::prepareModelsMap() 0342 { 0343 KisResourceTypeModel model; 0344 for (int i = 0; i < model.rowCount(); i++) { 0345 QModelIndex idx = model.index(i, 0); 0346 QString resourceType = model.data(idx, Qt::UserRole + KisResourceTypeModel::ResourceType).toString(); 0347 if (!m_resourceModelsForResourceType.contains(resourceType)) { 0348 KisResourceModel* model = new KisResourceModel(resourceType); 0349 if (model) { 0350 m_resourceModelsForResourceType.insert(resourceType, model); 0351 } else { 0352 dbgResources << "There is no KisResourceModel available for " << resourceType; 0353 } 0354 } 0355 } 0356 } 0357 0358 void ResourceImporter::initialize() 0359 { 0360 if (!m_isInitialized) { 0361 prepareTypesMaps(); 0362 prepareModelsMap(); 0363 m_isInitialized = true; 0364 } 0365 } 0366 0367 0368 0369 0370 0371 0372 0373