File indexing completed on 2024-05-12 15:59:57

0001 /*
0002  *    This file is part of the KDE project
0003  *    SPDX-FileCopyrightText: 2021 Agata Cacko <cacko.azh@gmail.com>
0004  *
0005  *    SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "KisResourceUserOperations.h"
0009 
0010 #include <QMessageBox>
0011 #include <QFileInfo>
0012 #include <QBuffer>
0013 
0014 #include <klocalizedstring.h>
0015 
0016 #include <KisResourceLocator.h>
0017 #include <KisResourceModel.h>
0018 #include <KisResourceCacheDb.h>
0019 #include <kis_assert.h>
0020 #include <KisGlobalResourcesInterface.h>
0021 
0022 
0023 bool KisResourceUserOperations::userAllowsOverwrite(QWidget* widgetParent, QString resourceFilepath)
0024 {
0025     return QMessageBox::question(widgetParent, i18nc("Dialog title", "Overwrite the file?"),
0026                           i18nc("Question in a dialog/messagebox", "This resource file already exists in the resource folder. "
0027                                                                    "Do you want to overwrite it?\nResource filename: %1", QFileInfo(resourceFilepath).fileName()),
0028                                  QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Cancel;
0029 }
0030 
0031 bool KisResourceUserOperations::resourceNameIsAlreadyUsed(KisResourceModel *resourceModel, QString resourceName, int resourceIdToIgnore)
0032 {
0033     auto sizeFilteredById = [resourceIdToIgnore] (QVector<KoResourceSP> list) {
0034         int sumHere = 0;
0035         if (resourceIdToIgnore < 0) {
0036             return list.size();
0037         }
0038 
0039         for (int i = 0; i < list.size(); i++) {
0040             if (list[i]->resourceId() != resourceIdToIgnore) {
0041                 sumHere++;
0042             }
0043         }
0044         return sumHere;
0045     };
0046 
0047     QVector<KoResourceSP> resourcesWithTheSameExactName = resourceModel->resourcesForName(resourceName);
0048     if (sizeFilteredById(resourcesWithTheSameExactName) > 0) {
0049         return true;
0050     }
0051 
0052     QVector<KoResourceSP> resourcesWithSpacesReplacedByUnderlines = resourceModel->resourcesForName(resourceName.replace(" ", "_"));
0053     if (sizeFilteredById(resourcesWithSpacesReplacedByUnderlines) > 0) {
0054         return true;
0055     }
0056 
0057     return false;
0058 }
0059 
0060 KoResourceSP KisResourceUserOperations::importResourceFileWithUserInput(QWidget *widgetParent, QString storageLocation, QString resourceType, QString resourceFilepath)
0061 {
0062     KisResourceModel resourceModel(resourceType);
0063     resourceModel.setResourceFilter(KisResourceModel::ShowActiveResources); // inactive don't count here
0064 
0065     KoResourceSP resource = resourceModel.importResourceFile(resourceFilepath, false, storageLocation);
0066     if (resource.isNull() && storageLocation == "" && resourceModel.importWillOverwriteResource(resourceFilepath, storageLocation)) {
0067         if (KisResourceUserOperations::userAllowsOverwrite(widgetParent, resourceFilepath)) {
0068             resource = resourceModel.importResourceFile(resourceFilepath, true, storageLocation);
0069         } else {
0070             return nullptr; // the user doesn't want to import the file anymore because they don't want to overwrite it
0071         }
0072     }
0073     if (!resource) {
0074         QMessageBox::warning(widgetParent, i18nc("@title:window", "Failed to import the resource"), i18nc("Warning message", "Failed to import the resource."));
0075     }
0076     return resource;
0077 }
0078 
0079 bool KisResourceUserOperations::renameResourceWithUserInput(QWidget *widgetParent, KoResourceSP resource, QString resourceName)
0080 {
0081     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(resource, false);
0082     KisResourceModel resourceModel(resource->resourceType().first);
0083     resourceModel.setResourceFilter(KisResourceModel::ShowActiveResources); // inactive don't count here
0084 
0085     if (resourceNameIsAlreadyUsed(&resourceModel, resourceName, resource->resourceId())) {
0086         bool userWantsRename = QMessageBox::question(widgetParent, i18nc("@title:window", "Rename the resource?"),
0087                               i18nc("Question in a dialog/messagebox", "This name is already used for another resource. "
0088                                                                        "Do you want to use the same name for multiple resources?"
0089                                                                        "(If you decline now, the resource won't be renamed)."),
0090                                      QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Cancel;
0091         if (!userWantsRename) {
0092             return false;
0093         }
0094     }
0095     bool res = resourceModel.renameResource(resource, resourceName);
0096     if (!res) {
0097         QMessageBox::warning(widgetParent, i18nc("@title:window", "Failed to rename the resource"), i18nc("Warning message", "Failed to rename the resource."));
0098     }
0099     return res;
0100 }
0101 
0102 bool KisResourceUserOperations::addResourceWithUserInput(QWidget *widgetParent, KoResourceSP resource, QString storageLocation)
0103 {
0104     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(resource, false);
0105     KisResourceModel resourceModel(resource->resourceType().first);
0106 
0107     resourceModel.setResourceFilter(KisResourceModel::ShowAllResources); // we want to consider all resources later when searching for the same name
0108 
0109     // check if adding the resource is possible: it is not if there is a resource with the same filename in the storage the user want to save the resource to
0110 
0111     typedef enum {ADD, OVERWRITE, CANCEL} Action;
0112     Action action = ADD;
0113 
0114     int resourceWithThatFilenameId;
0115 
0116     if (KisResourceCacheDb::getResourceIdFromVersionedFilename(resource->filename(), resource->resourceType().first, storageLocation, resourceWithThatFilenameId)) {
0117 
0118         KoResourceSP resource = resourceModel.resourceForId(resourceWithThatFilenameId);
0119         bool userWantsOverwrite = QMessageBox::question(widgetParent, i18nc("@title:window", "Overwrite the resource?"),
0120                               i18nc("Question in a dialog/messagebox", "This filename is already used for another resource. "
0121                                                                        "Do you want to overwrite that resource?\n"
0122                                                                        "(If you decline now, nothing will be done)."),
0123                                      QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Cancel;
0124         if (userWantsOverwrite) {
0125             action = OVERWRITE;
0126         } else {
0127             return false;
0128         }
0129     }
0130     // check if there are any other resources with the same name, even in different storages or with different filenames
0131     else if (resourceNameIsAlreadyUsed(&resourceModel, resource->name()))
0132     {
0133         bool userWantsAdd = QMessageBox::question(widgetParent, i18nc("@title:window", "Add the resource?"),
0134                               i18nc("Question in a dialog/messagebox", "This name is already used for another resource. "
0135                                                                        "Do you want to use the same name for multiple resources? "
0136                                                                        "(If you decline now, the resource won't be added)."),
0137                                      QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Cancel;
0138 
0139         if (!userWantsAdd) {
0140             action = CANCEL;
0141             return false;
0142         } else {
0143             action = ADD;
0144         }
0145     }
0146 
0147     if (action == ADD) {
0148         bool res = resourceModel.addResource(resource, storageLocation);
0149         if (!res) {
0150             QMessageBox::warning(widgetParent, i18nc("@title:window", "Failed to add resource"), i18nc("Warning message", "Failed to add the resource."));
0151         }
0152         return res;
0153     } else if (action == OVERWRITE) {
0154         bool res = updateResourceWithUserInput(widgetParent, resource);
0155         // no error message, because it's handled in that function
0156         return res;
0157     }
0158     // shouldn't get here
0159     return false;
0160 }
0161 
0162 bool KisResourceUserOperations::updateResourceWithUserInput(QWidget *widgetParent, KoResourceSP resource)
0163 {
0164     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(resource, false);
0165     KisResourceModel resourceModel(resource->resourceType().first);
0166     // allow inactive, because this code is used in addResourceWithUserInput
0167     resourceModel.setResourceFilter(KisResourceModel::ShowAllResources);
0168 
0169     if (resource->resourceId() < 0) {
0170         // that's a resource that didn't come from a database
0171         // we assume that filename and storageLocation are correct, though
0172         if (QFileInfo(resource->storageLocation()).isRelative()) {
0173             QString storageLocation = resource->storageLocation();
0174             resource->setStorageLocation(KisResourceLocator::instance()->makeStorageLocationAbsolute(storageLocation));
0175         }
0176 
0177         int outResourceId;
0178         // note that we need to check for any file that exists so we can't use KisResourceModel here
0179         // because the model only keeps the current resource filename
0180         bool result = KisResourceCacheDb::getResourceIdFromVersionedFilename(resource->filename(), resource->resourceType().first,
0181                                                                KisResourceLocator::instance()->makeStorageLocationRelative(resource->storageLocation()), outResourceId);
0182         if (!result) {
0183             qWarning() << "Could not get resource id from versioned filename"
0184                        << resource->filename()
0185                        << resource->resourceType().first;
0186         }
0187         KoResourceSP cachedPointer;
0188         if (outResourceId >= 0) {
0189             cachedPointer = resourceModel.resourceForId(outResourceId);
0190         }
0191 
0192         if (!cachedPointer || !resource->isSerializable() || !cachedPointer->isSerializable()) {
0193             QMessageBox::warning(widgetParent, i18nc("@title:window", "Failed to overwrite the resource"), i18nc("Warning message", "Failed to overwrite the resource."));
0194             return false;
0195         }
0196         // now we need to move data from the provided pointer to the pointer from the database
0197         QBuffer buffer;
0198         buffer.open(QIODevice::ReadWrite);
0199 
0200         resource->saveToDevice(&buffer);
0201         buffer.close();
0202         buffer.open(QIODevice::ReadWrite);
0203 
0204         cachedPointer->loadFromDevice(&buffer, KisGlobalResourcesInterface::instance());
0205         buffer.close();
0206         resource = cachedPointer;
0207     }
0208 
0209     QString oldName = resourceModel.data(resourceModel.indexForResourceId(resource->resourceId()), Qt::UserRole + KisAllResourcesModel::Name).toString();
0210     if (resource->name() != oldName) {
0211         // rename in action
0212         if (resourceNameIsAlreadyUsed(&resourceModel, resource->name(), resource->resourceId())) {
0213             bool userWantsRename = QMessageBox::question(widgetParent, i18nc("@title:window", "Rename the resource?"),
0214                                   i18nc("Question in a dialog/messagebox", "This name is already used for another resource. Do you want to overwrite "
0215                                                                            "and use the same name for multiple resources?"
0216                                                                            "\nIf you cancel, your changes won't be saved."),
0217                                          QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Cancel;
0218             if (!userWantsRename) {
0219                 return false;
0220             }
0221         }
0222     }
0223 
0224     bool res = resourceModel.updateResource(resource);
0225     if (!res) {
0226         QMessageBox::warning(widgetParent, i18nc("@title:window", "Failed to overwrite the resource"), i18nc("Warning message", "Failed to overwrite the resource."));
0227     }
0228     return res;
0229 }