Krita Source Code Documentation
Loading...
Searching...
No Matches
KisResourceUserOperations.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the KDE project
3 * SPDX-FileCopyrightText: 2021 Agata Cacko <cacko.azh@gmail.com>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
9
10#include <QMessageBox>
11#include <QFileInfo>
12#include <QBuffer>
13
14#include <klocalizedstring.h>
15
16#include <KisResourceLocator.h>
17#include <KisResourceModel.h>
18#include <KisResourceCacheDb.h>
19#include <kis_assert.h>
21
22
23bool KisResourceUserOperations::userAllowsOverwrite(QWidget* widgetParent, QString resourceFilepath)
24{
25 return QMessageBox::question(widgetParent, i18nc("Dialog title", "Overwrite the file?"),
26 i18nc("Question in a dialog/messagebox", "This resource file already exists in the resource folder. "
27 "Do you want to overwrite it?\nResource filename: %1", QFileInfo(resourceFilepath).fileName()),
28 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Cancel;
29}
30
31bool KisResourceUserOperations::resourceNameIsAlreadyUsed(KisResourceModel *resourceModel, QString resourceName, int resourceIdToIgnore)
32{
33 auto sizeFilteredById = [resourceIdToIgnore] (QVector<KoResourceSP> list) {
34#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
35 int sumHere = 0;
36#else
37 qsizetype sumHere = 0;
38#endif
39 if (resourceIdToIgnore < 0) {
40 return list.size();
41 }
42
43 for (int i = 0; i < list.size(); i++) {
44 if (list[i]->resourceId() != resourceIdToIgnore) {
45 sumHere++;
46 }
47 }
48 return sumHere;
49 };
50
51 QVector<KoResourceSP> resourcesWithTheSameExactName = resourceModel->resourcesForName(resourceName);
52 if (sizeFilteredById(resourcesWithTheSameExactName) > 0) {
53 return true;
54 }
55
56 QVector<KoResourceSP> resourcesWithSpacesReplacedByUnderlines = resourceModel->resourcesForName(resourceName.replace(" ", "_"));
57 if (sizeFilteredById(resourcesWithSpacesReplacedByUnderlines) > 0) {
58 return true;
59 }
60
61 return false;
62}
63
64KoResourceSP KisResourceUserOperations::importResourceFileWithUserInput(QWidget *widgetParent, QString storageLocation, QString resourceType, QString resourceFilepath)
65{
66 KisResourceModel resourceModel(resourceType);
67 resourceModel.setResourceFilter(KisResourceModel::ShowActiveResources); // inactive don't count here
68
69 KoResourceSP resource = resourceModel.importResourceFile(resourceFilepath, false, storageLocation);
70 if (resource.isNull() && storageLocation == "" && resourceModel.importWillOverwriteResource(resourceFilepath, storageLocation)) {
71 if (KisResourceUserOperations::userAllowsOverwrite(widgetParent, resourceFilepath)) {
72 resource = resourceModel.importResourceFile(resourceFilepath, true, storageLocation);
73 } else {
74 return nullptr; // the user doesn't want to import the file anymore because they don't want to overwrite it
75 }
76 }
77 if (!resource) {
78 QMessageBox::warning(widgetParent, i18nc("@title:window", "Failed to import the resource"), i18nc("Warning message", "Failed to import the resource."));
79 }
80 return resource;
81}
82
83bool KisResourceUserOperations::renameResourceWithUserInput(QWidget *widgetParent, KoResourceSP resource, QString resourceName)
84{
86 KisResourceModel resourceModel(resource->resourceType().first);
87 resourceModel.setResourceFilter(KisResourceModel::ShowActiveResources); // inactive don't count here
88
89 if (resourceNameIsAlreadyUsed(&resourceModel, resourceName, resource->resourceId())) {
90 bool userWantsRename = QMessageBox::question(widgetParent, i18nc("@title:window", "Rename the resource?"),
91 i18nc("Question in a dialog/messagebox", "This name is already used for another resource. "
92 "Do you want to use the same name for multiple resources?"
93 "(If you decline now, the resource won't be renamed)."),
94 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Cancel;
95 if (!userWantsRename) {
96 return false;
97 }
98 }
99 bool res = resourceModel.renameResource(resource, resourceName);
100 if (!res) {
101 QMessageBox::warning(widgetParent, i18nc("@title:window", "Failed to rename the resource"), i18nc("Warning message", "Failed to rename the resource."));
102 }
103 return res;
104}
105
106bool KisResourceUserOperations::addResourceWithUserInput(QWidget *widgetParent, KoResourceSP resource, QString storageLocation)
107{
109 KisResourceModel resourceModel(resource->resourceType().first);
110
111 resourceModel.setResourceFilter(KisResourceModel::ShowAllResources); // we want to consider all resources later when searching for the same name
112
113 // 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
114
115 typedef enum {ADD, OVERWRITE, CANCEL} Action;
116 Action action = ADD;
117
118 int resourceWithThatFilenameId;
119
120 if (KisResourceCacheDb::getResourceIdFromVersionedFilename(resource->filename(), resource->resourceType().first, storageLocation, resourceWithThatFilenameId)) {
121
122 KoResourceSP resource = resourceModel.resourceForId(resourceWithThatFilenameId);
123 bool userWantsOverwrite = QMessageBox::question(widgetParent, i18nc("@title:window", "Overwrite the resource?"),
124 i18nc("Question in a dialog/messagebox", "This filename is already used for another resource. "
125 "Do you want to overwrite that resource?\n"
126 "(If you decline now, nothing will be done)."),
127 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Cancel;
128 if (userWantsOverwrite) {
129 action = OVERWRITE;
130 } else {
131 return false;
132 }
133 }
134 // check if there are any other resources with the same name, even in different storages or with different filenames
135 else if (resourceNameIsAlreadyUsed(&resourceModel, resource->name()))
136 {
137 bool userWantsAdd = QMessageBox::question(widgetParent, i18nc("@title:window", "Add the resource?"),
138 i18nc("Question in a dialog/messagebox", "This name is already used for another resource. "
139 "Do you want to use the same name for multiple resources? "
140 "(If you decline now, the resource won't be added)."),
141 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Cancel;
142
143 if (!userWantsAdd) {
144 action = CANCEL;
145 return false;
146 } else {
147 action = ADD;
148 }
149 }
150
151 if (action == ADD) {
152 bool res = resourceModel.addResource(resource, storageLocation);
153 if (!res) {
154 QMessageBox::warning(widgetParent, i18nc("@title:window", "Failed to add resource"), i18nc("Warning message", "Failed to add the resource."));
155 }
156 return res;
157 } else if (action == OVERWRITE) {
158 bool res = updateResourceWithUserInput(widgetParent, resource);
159 // no error message, because it's handled in that function
160 return res;
161 }
162 // shouldn't get here
163 return false;
164}
165
167{
169 KisResourceModel resourceModel(resource->resourceType().first);
170 // allow inactive, because this code is used in addResourceWithUserInput
172
173 if (resource->resourceId() < 0) {
174 // that's a resource that didn't come from a database
175 // we assume that filename and storageLocation are correct, though
176 if (QFileInfo(resource->storageLocation()).isRelative()) {
177 QString storageLocation = resource->storageLocation();
178 resource->setStorageLocation(KisResourceLocator::instance()->makeStorageLocationAbsolute(storageLocation));
179 }
180
181 int outResourceId;
182 // note that we need to check for any file that exists so we can't use KisResourceModel here
183 // because the model only keeps the current resource filename
184 bool result = KisResourceCacheDb::getResourceIdFromVersionedFilename(resource->filename(), resource->resourceType().first,
185 KisResourceLocator::instance()->makeStorageLocationRelative(resource->storageLocation()), outResourceId);
186 if (!result) {
187 qWarning() << "Could not get resource id from versioned filename"
188 << resource->filename()
189 << resource->resourceType().first;
190 }
191 KoResourceSP cachedPointer;
192 if (outResourceId >= 0) {
193 cachedPointer = resourceModel.resourceForId(outResourceId);
194 }
195
196 if (!cachedPointer || !resource->isSerializable() || !cachedPointer->isSerializable()) {
197 QMessageBox::warning(widgetParent, i18nc("@title:window", "Failed to overwrite the resource"), i18nc("Warning message", "Failed to overwrite the resource."));
198 return false;
199 }
200 // now we need to move data from the provided pointer to the pointer from the database
201 QBuffer buffer;
202 buffer.open(QIODevice::ReadWrite);
203
204 resource->saveToDevice(&buffer);
205 buffer.close();
206 buffer.open(QIODevice::ReadWrite);
207
208 cachedPointer->loadFromDevice(&buffer, KisGlobalResourcesInterface::instance());
209 buffer.close();
210 resource = cachedPointer;
211 }
212
213 QString oldName = resourceModel.data(resourceModel.indexForResourceId(resource->resourceId()), Qt::UserRole + KisAllResourcesModel::Name).toString();
214 if (resource->name() != oldName) {
215 // rename in action
216 if (resourceNameIsAlreadyUsed(&resourceModel, resource->name(), resource->resourceId())) {
217 bool userWantsRename = QMessageBox::question(widgetParent, i18nc("@title:window", "Rename the resource?"),
218 i18nc("Question in a dialog/messagebox", "This name is already used for another resource. Do you want to overwrite "
219 "and use the same name for multiple resources?"
220 "\nIf you cancel, your changes won't be saved."),
221 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Cancel;
222 if (!userWantsRename) {
223 return false;
224 }
225 }
226 }
227
228 bool res = resourceModel.updateResource(resource);
229 if (!res) {
230 QMessageBox::warning(widgetParent, i18nc("@title:window", "Failed to overwrite the resource"), i18nc("Warning message", "Failed to overwrite the resource."));
231 }
232 return res;
233}
static KisResourcesInterfaceSP instance()
static bool getResourceIdFromVersionedFilename(QString filename, QString resourceType, QString storageLocation, int &outResourceId)
Note that here you can put even the original filename - any filename from the versioned_resources - a...
QString makeStorageLocationRelative(QString location) const
static KisResourceLocator * instance()
The KisResourceModel class provides the main access to resources. It is possible to filter the resour...
KoResourceSP resourceForId(int id) const
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
bool renameResource(KoResourceSP resource, const QString &name) override
renameResource name the given resource. The resource will have its name field reset,...
bool updateResource(KoResourceSP resource) override
updateResource creates a new version of the resource in the storage and in the database....
QVector< KoResourceSP > resourcesForName(QString name) const
bool addResource(KoResourceSP resource, const QString &storageId=QString("")) override
addResource adds the given resource to the database and storage. If the resource already exists in th...
void setResourceFilter(ResourceFilter filter) override
QModelIndex indexForResourceId(int resourceId) const override
indexFromResource
static KoResourceSP importResourceFileWithUserInput(QWidget *widgetParent, QString storageLocation, QString resourceType, QString resourceFilepath)
static bool userAllowsOverwrite(QWidget *widgetParent, QString resourceFilepath)
static bool renameResourceWithUserInput(QWidget *widgetParent, KoResourceSP resource, QString resourceName)
static bool resourceNameIsAlreadyUsed(KisResourceModel *resourceModel, QString resourceName, int resourceIdToIgnore=-1)
static bool addResourceWithUserInput(QWidget *widgetParent, KoResourceSP resource, QString storageLocation="")
static bool updateResourceWithUserInput(QWidget *widgetParent, KoResourceSP resource)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129