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 }