Krita Source Code Documentation
Loading...
Searching...
No Matches
KisTagResourceModel.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2020 Boudewijn Rempt <boud@valdyas.org>
3 * SPDX-FileCopyrightText: 2023 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
8
9#include <QFont>
10#include <QSqlError>
11#include <QSqlQuery>
12#include <QSqlDatabase>
13#include <QMap>
14
15#include <KisResourceLocator.h>
16#include <KisResourceModel.h>
19#include <KisStorageModel.h>
20#include <kis_assert.h>
21
28
29
30KisAllTagResourceModel::KisAllTagResourceModel(const QString &resourceType, QObject *parent)
31 : QAbstractTableModel(parent)
32 , d(new Private())
33{
34 d->resourceType = resourceType;
35 resetQuery();
36
37 connect(KisResourceLocator::instance(), SIGNAL(storageAdded(const QString&)), this, SLOT(addStorage(const QString&)));
38 connect(KisResourceLocator::instance(), SIGNAL(storageRemoved(const QString&)), this, SLOT(removeStorage(const QString&)));
39 connect(KisStorageModel::instance(), SIGNAL(storageEnabled(const QString&)), this, SLOT(addStorage(const QString&)));
40 connect(KisStorageModel::instance(), SIGNAL(storageDisabled(const QString&)), this, SLOT(removeStorage(const QString&)));
41 connect(KisResourceLocator::instance(), SIGNAL(resourceActiveStateChanged(const QString&, int)), this, SLOT(slotResourceActiveStateChanged(const QString&, int)));
42
48}
49
54
55int KisAllTagResourceModel::rowCount(const QModelIndex &parent) const
56{
57 if (parent.isValid()) {
58 return 0;
59 }
60
61 if (d->cachedRowCount < 0) {
62 QSqlQuery q;
63 const bool r = q.prepare("SELECT COUNT(DISTINCT resource_tags.tag_id || resources.name || resources.filename || resources.md5sum)\n"
64 "FROM resource_tags\n"
65 ", resources\n"
66 ", resource_types\n"
67 "WHERE resource_tags.resource_id = resources.id\n"
68 "AND resources.resource_type_id = resource_types.id\n"
69 "AND resource_types.name = :resource_type\n"
70 "AND resource_tags.active = 1\n");
71
72 if (!r) {
73 qWarning() << "Could not execute resource/tags rowcount query" << q.lastError();
74 return 0;
75 }
76
77 q.bindValue(":resource_type", d->resourceType);
78
79 if (!q.exec()) {
80 qWarning() << "Could not execute resource/tags rowcount query" << q.lastError();
81 return 0;
82 }
83
84 q.first();
85
86 const_cast<KisAllTagResourceModel*>(this)->d->cachedRowCount = q.value(0).toInt();
87 }
88 return d->cachedRowCount;
89}
90
91int KisAllTagResourceModel::columnCount(const QModelIndex &parent) const
92{
93 if (parent.isValid()) {
94 return 0;
95 }
96
97 return d->columnCount;
98}
99
100QVariant KisAllTagResourceModel::data(const QModelIndex &index, int role) const
101{
102 QVariant v;
103
104 if (!index.isValid()) { return v; }
105 if (index.row() > rowCount()) { return v; }
106 if (index.column() > d->columnCount) { return v;}
107
108 bool pos = const_cast<KisAllTagResourceModel*>(this)->d->query.seek(index.row());
109 if (!pos) {return v;}
110
111 if (role < Qt::UserRole + TagId && index.column() < TagId) {
112 return KisResourceQueryMapper::variantFromResourceQuery(d->query, index.column(), role, true);
113 }
114
115 if (role == Qt::FontRole) {
116 return QFont();
117 }
118
119 if (index.column() >= TagId) {
120 // trick to get the correct value without writing everything again
121 // this is used for example in case of sorting
122 role = Qt::UserRole + index.column();
123 }
124
125 // These are not shown, but needed for the filter
126 switch(role) {
127 case Qt::UserRole + TagId:
128 {
129 v = d->query.value("tag_id");
130 break;
131 }
132 case Qt::UserRole + ResourceId:
133 {
134 v = d->query.value("resource_id");
135 break;
136 }
137 case Qt::UserRole + Tag:
138 {
139 KisTagSP tag = KisResourceLocator::instance()->tagForUrl(d->query.value("tag_url").toString(), d->resourceType);
140 v = QVariant::fromValue(tag);
141 break;
142 }
143 case Qt::UserRole + Resource:
144 {
145 v = QVariant::fromValue(KisResourceLocator::instance()->resourceForId(d->query.value("resource_id").toInt()));
146 break;
147 }
148 case Qt::UserRole + ResourceActive:
149 {
150 v = d->query.value("resource_active");
151 break;
152 }
153 case Qt::UserRole + TagActive:
154 {
155 v = d->query.value("tag_active");
156 break;
157 }
158 case Qt::UserRole + ResourceStorageActive:
159 {
160 v = d->query.value("resource_storage_active");
161 break;
162 }
163 case Qt::UserRole + ResourceName:
164 {
165 v = d->query.value("resource_name");
166 break;
167 }
168 case Qt::UserRole + TagName:
169 {
170 v = d->query.value("translated_name");
171 if (v.isNull()) {
172 v = d->query.value("tag_name");
173 }
174 break;
175 }
176
177 default:
178 ;
179 }
180 return v;
181}
182
184{
185 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(tag && tag->valid() && tag->id() >= 0, false);
186
187 // notes for performance:
188 // the only two costly parts are:
189 // - executing the query constructed by createQuery()
190 // - running endInsertRows() (because it updates all the views and filter proxies etc...)
191
192 QVector<int> resourceIdsToAdd;
193 QVector<int> resourceIdsToUpdate;
194
195 // looks expensive but actually isn't
196 for (int i = 0; i < resourceIds.count(); i++) {
197 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(resourceIds[i] >= 0, false);
198 int taggedState = isResourceTagged(tag, resourceIds[i]);
199 switch (taggedState) {
200 case -1:
201 // never tagged
202 resourceIdsToAdd.append(resourceIds[i]);
203 break;
204 case 0:
205 // tagged but then untagged
206 resourceIdsToUpdate.append(resourceIds[i]);
207 break;
208 case 1:
209 // it means the resource is already tagged; do nothing
210 break;
211 }
212 }
213
214 if (resourceIdsToAdd.isEmpty() && resourceIdsToUpdate.isEmpty()) {
215 // Is already tagged, let's do nothing
216 return true;
217 }
218
219 int howManyTimesBeginInsertedCalled = 0;
220
221 if (resourceIdsToUpdate.count() > 0) {
222
223 QSqlQuery allIndices;
224 if (!allIndices.prepare(createQuery(false, true))) {
225 qWarning() << "Could not prepare tagResource-allIndices query" << allIndices.lastError();
226 }
227
228 allIndices.bindValue(":resource_type", d->resourceType);
229 allIndices.bindValue(":language", KisTag::currentLocale());
230
231 if (!allIndices.exec()) {
232 qWarning() << "Could not execute tagResource-allIndices query" << allIndices.lastError();
233 }
234
235 int activesRowId = -1;
236 int lastActiveRowId = -1;
237
238 // needed for beginInsertRows calculations
239 QMap<int, int> resourcesCountForLastActiveRowId;
240
241 int idd = 0;
242
243 while (allIndices.next()) {
244 idd++;
245 bool isActive = allIndices.value("resource_tags_pair_active").toBool();
246 if (isActive) {
247 activesRowId++;
248 lastActiveRowId = activesRowId;
249 } else {
250 bool variantSuccess = true;
251 int rowTagId = allIndices.value("tag_id").toInt(&variantSuccess);
252 KIS_SAFE_ASSERT_RECOVER(variantSuccess) { rowTagId = -1; }
253 int rowResourceId = allIndices.value("resource_id").toInt(&variantSuccess);
254 KIS_SAFE_ASSERT_RECOVER(variantSuccess) { rowResourceId = -1; }
255 if (rowTagId == tag->id() && resourceIdsToUpdate.contains(rowResourceId)) {
256 if (!resourcesCountForLastActiveRowId.contains(lastActiveRowId)) {
257 resourcesCountForLastActiveRowId[lastActiveRowId] = 1;
258 } else {
259 resourcesCountForLastActiveRowId[lastActiveRowId] = resourcesCountForLastActiveRowId[lastActiveRowId] + 1;
260 }
261 }
262 }
263 }
264
265 Q_FOREACH(const int key, resourcesCountForLastActiveRowId.keys()) {
266 // when having multiple beginInsertRows:
267 // let's say you have this model:
268 // 0 A
269 // 1 A
270 // 2 A
271 // <- put 2 Bs here
272 // 3 A
273 // <- put one B here
274 // 4 A
275 // and you want to add the two Bs and then add another B
276 // then the signals should be:
277 // beginRemoveRows(3, 4) <- new indices of the two Bs
278 // beginRemoveRows(4, 4) <- new index of the one B ignoring the action before
279
280
281 beginInsertRows(QModelIndex(), key + 1, key + resourcesCountForLastActiveRowId[key]);
282 howManyTimesBeginInsertedCalled++;
283 }
284
285 // Resource was tagged, then untagged. Tag again;
286 QSqlQuery q;
287
288 if (!q.prepare("UPDATE resource_tags\n"
289 "SET active = 1\n"
290 "WHERE resource_id = :resource_id\n"
291 "AND tag_id = :tag_id")) {
292
293
294 qWarning() << "Could not prepare update resource_tags to active statement" << q.lastError();
295
296 return false;
297 }
298
299 QVariantList resourceIdsVariants;
300 QVariantList tagIdVariants;
301
302 for (int i = 0; i < resourceIdsToUpdate.count(); i++) {
303 resourceIdsVariants << QVariant(resourceIdsToUpdate[i]);
304 tagIdVariants << QVariant(tag->id());
305 }
306
307 q.bindValue(":resource_id", resourceIdsVariants);
308 q.bindValue(":tag_id", tagIdVariants);
309
310 if (!q.execBatch()) {
311 qWarning() << "Could not execute update resource_tags to active statement" << q.lastError();
312 for (int i = 0; i < howManyTimesBeginInsertedCalled; i++) {
313 endInsertRows();
314 }
315 return false;
316 }
317
318 }
319
320 if (resourceIdsToAdd.count() > 0) {
321
322 beginInsertRows(QModelIndex(), rowCount(), rowCount() + resourceIdsToAdd.count() - 1);
323 howManyTimesBeginInsertedCalled++;
324
325 // Resource was never tagged before, insert it. The active column is DEFAULT 1
326 QSqlQuery q;
327
328
329 QString values;
330 for (int i = 0; i < resourceIdsToAdd.count(); i++) {
331 if (i > 0) {
332 values.append(", ");
333 }
334 values.append("(?, ?, ?)");
335 }
336
337 if (!q.prepare(QString("INSERT INTO resource_tags\n"
338 "(resource_id, tag_id, active)\n"
339 "VALUES ") + values + QString(";\n"))) {
340 qWarning() << "Could not prepare insert into resource tags statement" << q.lastError();
341 for (int i = 0; i < howManyTimesBeginInsertedCalled; i++) {
342 endInsertRows();
343 }
344 return false;
345 }
346
347 for (int i = 0; i < resourceIdsToAdd.count(); i++) {
348 q.addBindValue(resourceIdsToAdd[i]);
349 q.addBindValue(tag->id());
350 q.addBindValue(true);
351
352 }
353
354 if (!q.exec()) {
355 qWarning() << "Could not execute insert into resource tags statement" << q.boundValues() << q.lastError();
356 for (int i = 0; i < howManyTimesBeginInsertedCalled; i++) {
357 endInsertRows();
358 }
359 return false;
360 }
361 }
362
363 resetQuery();
364
365 for (int i = 0; i < howManyTimesBeginInsertedCalled; i++) {
366 endInsertRows();
367 }
368
369 return true;
370}
371
373{
374 if (!tag || !tag->valid()) return false;
375 if (!d->query.isSelect()) return false;
376 if (rowCount() < 1) return false;
377
378 int beginRemoveRowsCount = 0;
379
380 QSqlQuery q;
381
382 if (!q.prepare("UPDATE resource_tags\n"
383 "SET active = 0\n"
384 "WHERE tag_id = :tag_id\n"
385 "AND resource_id = :resource_id")) {
386 qWarning() << "Could not prepare untagResource-update query" << q.lastError();
387 return false;
388 }
389
390 QSqlQuery allIndices;
391 if (!allIndices.prepare(createQuery(true, true))) {
392 qWarning() << "Could not prepare untagResource-allIndices query " << allIndices.lastError();
393 }
394
395 allIndices.bindValue(":resource_type", d->resourceType);
396 allIndices.bindValue(":language", KisTag::currentLocale());
397
398 if (!allIndices.exec()) {
399 qCritical() << "Could not exec untagResource-allIndices query " << allIndices.lastError();
400 }
401
402 int activesRowId = -1;
403 int lastActiveRowId = -1;
404
405 // needed for beginInsertRows indices calculations
406 QMap<int, int> resourcesCountForLastActiveRowId;
407
408 int idd = 0;
409
410 while (allIndices.next()) {
411 idd++;
412 bool variantSuccess = true;
413
414 bool isActive = true; // all of them are active!
415 KIS_SAFE_ASSERT_RECOVER(variantSuccess) { isActive = false; }
416
417 int rowTagId = allIndices.value("tag_id").toInt(&variantSuccess);
418 KIS_SAFE_ASSERT_RECOVER(variantSuccess) { rowTagId = -1; }
419 int rowResourceId = allIndices.value("resource_id").toInt(&variantSuccess);
420 KIS_SAFE_ASSERT_RECOVER(variantSuccess) { rowResourceId = -1; }
421
422 bool willStayActive = isActive && (rowTagId != tag->id() || !resourceIds.contains(rowResourceId));
423 activesRowId++;
424 if (willStayActive) {
425 lastActiveRowId = activesRowId;
426 } else if (isActive) {
427 // means we're removing it
428 if (!resourcesCountForLastActiveRowId.contains(lastActiveRowId)) {
429 resourcesCountForLastActiveRowId[lastActiveRowId] = 0;
430 }
431 resourcesCountForLastActiveRowId[lastActiveRowId]++;
432 }
433 }
434
435 Q_FOREACH(const int key, resourcesCountForLastActiveRowId.keys()) {
436 // when having multiple beginRemoveRows:
437 // let's say you have this model:
438 // 0 A
439 // 1 A
440 // 2 A
441 // 3 *B*
442 // 4 *B*
443 // 5 A
444 // 6 *B*
445 // 7 A
446 // and you want to remove all Bs
447 // then the signals should be:
448 // beginRemoveRows(3, 4) <- first two indices in the obvious way
449 // beginRemoveRows(4, 4) <- next index as if the first action was already done
450 // (so `5 A` already became `3 A`, so the *B* is `4 B`, not `6 B`)
451
452 beginRemoveRows(QModelIndex(), key + 1, key + resourcesCountForLastActiveRowId[key]);
453 beginRemoveRowsCount++;
454 }
455
456 QSqlDatabase::database().transaction();
457 for (int i = 0; i < resourceIds.count(); i++) {
458 int resourceId = resourceIds[i];
459
460 if (resourceId < 0) continue;
461 if (isResourceTagged(tag, resourceId) < 1) continue;
462
463 q.bindValue(":tag_id", tag->id());
464 q.bindValue(":resource_id", resourceId);
465
466 if (!q.exec()) {
467 qWarning() << "Could not execute untagResource-update query" << q.lastError() << q.boundValues();
468 for (int i = 0; i < beginRemoveRowsCount; i++) {
469 endRemoveRows();
470 }
471 QSqlDatabase::database().rollback();
472 return false;
473 }
474 }
475 QSqlDatabase::database().commit();
476
477 if (beginRemoveRowsCount > 0) {
478 resetQuery();
479 for (int i = 0; i < beginRemoveRowsCount; i++) {
480 endRemoveRows();
481 }
482 }
483
484 return true;
485}
486
487int KisAllTagResourceModel::isResourceTagged(const KisTagSP tag, const int resourceId)
488{
489 QSqlQuery query;
490 bool r = query.prepare("SELECT resource_tags.active\n"
491 "FROM resource_tags\n"
492 "WHERE resource_tags.resource_id = :resource_id\n"
493 "AND resource_tags.tag_id = :tag_id\n");
494
495 if (!r) {
496 qWarning() << "Could not prepare bool KisAllTagResourceModel::checkResourceTaggedState query" << query.lastError();
497 return false;
498 }
499
500 query.bindValue(":resource_id", resourceId);
501 query.bindValue(":tag_id", tag->id());
502
503 if (!query.exec()) {
504 qWarning() << "Could not execute is resource tagged with a specific tag query" << query.boundValues() << query.lastError();
505 return false;
506 }
507
508 r = query.first();
509 if (!r) {
510 // Resource was not tagged
511 return -1;
512 }
513
514 return query.value(0).toInt() > 0;
515}
516
517void KisAllTagResourceModel::addStorage(const QString &location)
518{
519 Q_UNUSED(location);
520 beginInsertRows(QModelIndex(), rowCount(), rowCount());
521 resetQuery();
522 endInsertRows();
523}
524
525void KisAllTagResourceModel::removeStorage(const QString &location)
526{
527 Q_UNUSED(location);
528 beginRemoveRows(QModelIndex(), rowCount(), rowCount());
529 resetQuery();
530 endRemoveRows();
531}
532
533void KisAllTagResourceModel::slotResourceActiveStateChanged(const QString &resourceType, int resourceId)
534{
535 if (resourceType != d->resourceType) return;
536 if (resourceId < 0) return;
537
538 resetQuery();
539
542 QVector<QModelIndex> indexes;
543
544 for (int i = 0; i < rowCount(); ++i) {
545 const QModelIndex idx = this->index(i, 0);
546 KIS_ASSERT_RECOVER(idx.isValid()) { continue; }
547
548 if (idx.data(Qt::UserRole + KisAllTagResourceModel::ResourceId).toInt() == resourceId) {
549 indexes << idx;
550 }
551 }
552
553 Q_FOREACH(const QModelIndex &index, indexes) {
554 Q_EMIT dataChanged(index, index, {Qt::CheckStateRole, Qt::UserRole + KisAllTagResourceModel::ResourceActive});
555 }
556}
557
558QString KisAllTagResourceModel::createQuery(bool onlyActive, bool returnADbIndexToo)
559{
560 QString query = QString("WITH initial_selection AS (\n"
561 " SELECT tags.id\n"
562 " , resources.name\n"
563 " , resources.filename\n"
564 " , resources.md5sum\n"
565 " , resource_types.id as resource_type_id\n"
566 " , resource_types.name as resource_type_name\n"
567 " , min(resources.id) as resource_id\n"
568 ) + (returnADbIndexToo ? QString(", resource_tags.id as resource_tags_row_id\n") : QString("")) + QString( // include r_t row id
569 ) + (onlyActive ? QString("") : QString(", resource_tags.active as resource_tags_pair_active\n")) + QString( // include r_t row active info
570 " FROM resource_types\n"
571 " JOIN resource_tags\n ON resource_tags.resource_id = resources.id\n"
572 ) + (onlyActive ? QString(" AND resource_tags.active = 1\n") : QString("")) + QString( // make sure only active tags are used
573 " JOIN resources ON resources.resource_type_id = resource_types.id\n"
574 " JOIN tags ON tags.id = resource_tags.tag_id\n"
575 " AND tags.resource_type_id = resource_types.id\n"
576 " WHERE resource_types.name = :resource_type\n"
577 " GROUP BY tags.id\n"
578 " , resources.name\n"
579 " , resources.filename\n"
580 " , resources.md5sum\n"
581 " , resource_types.id\n"
582 " ORDER BY resource_tags.id\n"
583 ")\n"
584 "SELECT \n"
585 " initial_selection.id as tag_id\n"
586 ", initial_selection.name as resource_name\n"
587 ", initial_selection.filename as resource_filename\n"
588 ", initial_selection.md5sum as resource_md5sum\n"
589 ", initial_selection.resource_id as resource_id\n"
590 ", tags.url as tag_url"
591 ", tags.active as tag_active"
592 ", tags.name as tag_name"
593 ", tags.comment as tag_comment"
594 ", resources.status as resource_active\n"
595 ", resources.tooltip as resource_tooltip\n"
596 ", resources.status as resource_active\n"
597 ", resources.storage_id as storage_id\n"
598 ", storages.active as resource_storage_active\n"
599 ", storages.location as location\n"
600 ", tag_translations.name as translated_name\n"
601 ", tag_translations.comment as translated_comment\n"
602 ", initial_selection.resource_type_name as resource_type\n"
603 ) + (returnADbIndexToo ? QString(", initial_selection.resource_tags_row_id as resource_tags_row_id\n") : QString("")) + QString(
604 ) + (onlyActive ? QString("") : QString(", initial_selection.resource_tags_pair_active as resource_tags_pair_active\n")) + QString(
605 "FROM initial_selection\n"
606 "JOIN tags ON tags.id = initial_selection.id\n"
607 " AND tags.resource_type_id = initial_selection.resource_type_id\n"
608 "JOIN resources ON resources.id = resource_id\n"
609 "JOIN storages ON storages.id = resources.storage_id\n"
610 "LEFT JOIN tag_translations ON tag_translations.tag_id = initial_selection.id\n"
611 " AND tag_translations.language = :language\n");
612
613 return query;
614}
615
616// Keep in sync with KisAllResourcesModel::headerData for sections 1-16
617// then KisAllTagResourceModel::data for 16-25
618QVariant KisAllTagResourceModel::headerData(int section, Qt::Orientation orientation, int role) const
619{
620 if (orientation != Qt::Horizontal) {
621 return {};
622 }
623 if (role != Qt::DisplayRole) {
624 return {};
625 }
626
627 switch (section) {
629 return i18n("Id");
631 return i18n("Storage ID");
633 return i18n("Name");
635 return i18n("File Name");
637 return i18n("Tooltip");
639 return i18n("Image");
641 return i18n("Status");
643 return i18n("Location");
645 return i18n("Resource Type");
647 return i18n("Active");
649 return i18n("Storage Active");
651 return i18n("md5sum");
653 return i18n("Tags");
655 return i18n("Large Thumbnail");
657 return i18n("Dirty");
659 return i18n("Metadata");
660 default:
661 role = Qt::UserRole + section;
662 break;
663 }
664
665 switch (role) {
666 case Qt::UserRole + KisAllTagResourceModel::TagId:
667 return i18n("Tag ID");
668 case Qt::UserRole + KisAllTagResourceModel::ResourceId:
669 return i18n("Resource ID");
670 case Qt::UserRole + KisAllTagResourceModel::Tag:
671 return i18n("Tag Url");
672 case Qt::UserRole + KisAllTagResourceModel::Resource:
673 return i18n("Resource");
674 case Qt::UserRole + ResourceActive:
675 return i18n("Resource Active");
676 case Qt::UserRole + TagActive:
677 return i18n("Tag Active");
678 case Qt::UserRole + ResourceStorageActive:
679 return i18n("Storage Active");
680 case Qt::UserRole + ResourceName:
681 return i18n("Resource Name");
682 case Qt::UserRole + TagName:
683 return i18n("Tag File Name");
684 default:
685 break;
686 }
687
688 return {};
689}
690
692{
693 d->query.clear();
694}
695
697{
698 bool r = d->query.prepare(createQuery(true));
699
700 if (!r) {
701 qWarning() << "Could not prepare KisAllTagResourcesModel query" << d->query.lastError();
702 }
703
704 d->query.bindValue(":resource_type", d->resourceType);
705 d->query.bindValue(":language", KisTag::currentLocale());
706
707 r = d->query.exec();
708
709 if (!r) {
710 qWarning() << "Could not execute KisAllTagResourcesModel query" << d->query.lastError();
711 }
712
713 d->cachedRowCount = -1;
714
715 return r;
716}
717
718
728
729
730KisTagResourceModel::KisTagResourceModel(const QString &resourceType, QObject *parent)
731 : QSortFilterProxyModel(parent)
732 , d(new Private())
733{
734 d->resourceType = resourceType;
736 setSourceModel(d->sourceModel);
737
738 connect(KisResourceLocator::instance(), SIGNAL(storageAdded(const QString &)), this, SLOT(storageChanged(const QString &)));
739 connect(KisResourceLocator::instance(), SIGNAL(storageRemoved(const QString &)), this, SLOT(storageChanged(const QString &)));
740 connect(KisStorageModel::instance(), SIGNAL(storageEnabled(const QString &)), this, SLOT(storageChanged(const QString &)));
741 connect(KisStorageModel::instance(), SIGNAL(storageDisabled(const QString &)), this, SLOT(storageChanged(const QString &)));
742}
743
748
750{
751 d->tagFilter = filter;
752 invalidateFilter();
753}
754
756{
757 d->resourceFilter = filter;
758 invalidateFilter();
759}
760
762{
763 d->storageFilter = filter;
764 invalidateFilter();
765}
766
767bool KisTagResourceModel::tagResources(const KisTagSP tag, const QVector<int> &resourceIds)
768{
769 bool r = d->sourceModel->tagResources(tag, resourceIds);
770 return r;
771}
772
774{
775 return d->sourceModel->untagResources(tag, resourceIds);
776}
777
778int KisTagResourceModel::isResourceTagged(const KisTagSP tag, const int resourceId)
779{
780 return d->sourceModel->isResourceTagged(tag, resourceId);
781}
782
784{
785 d->tagIds = tagIds;
786 invalidateFilter();
787}
788
790{
791 d->resourceIds = resourceIds;
792 invalidateFilter();
793}
794
796{
797 d->tagIds.clear();
798 Q_FOREACH(const KisTagSP tag, tags) {
799 if (tag && tag->valid() && tag->id() > -1) {
800 d->tagIds << tag->id();
801 }
802 }
803 invalidateFilter();
804}
805
807{
808 d->resourceIds.clear();
809 Q_FOREACH(const KoResourceSP resource, resources) {
810 if (resource->valid() && resource->resourceId() > -1) {
811 d->resourceIds << resource->resourceId();
812 }
813 }
814 invalidateFilter();
815}
816
817bool KisTagResourceModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
818{
819 QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
820 if (!idx.isValid()) return false;
821
822 int tagId = idx.data(Qt::UserRole + KisAllTagResourceModel::TagId).toInt();
823 int resourceId = idx.data(Qt::UserRole + KisAllTagResourceModel::ResourceId).toInt();
824 bool tagActive = idx.data(Qt::UserRole + KisAllTagResourceModel::TagActive).toBool();
825 bool resourceActive = idx.data(Qt::UserRole + KisAllTagResourceModel::ResourceActive).toBool();
826 bool resourceStorageActive = idx.data(Qt::UserRole + KisAllTagResourceModel::ResourceStorageActive).toBool();
827
829 return ((d->tagIds.contains(tagId) || d->tagIds.isEmpty()) &&
830 (d->resourceIds.contains(resourceId) || d->resourceIds.isEmpty()));
831 }
832
833 if ((d->tagFilter == ShowActiveTags && !tagActive)
834 || (d->tagFilter == ShowInactiveTags && tagActive)) {
835 return false;
836 }
837
838 if ((d->resourceFilter == ShowActiveResources && !resourceActive)
839 || (d->resourceFilter == ShowInactiveResources && resourceActive)) {
840 return false;
841 }
842
843 if ((d->storageFilter == ShowActiveStorages && !resourceStorageActive)
844 || (d->storageFilter == ShowInactiveStorages && resourceStorageActive)) {
845 return false;
846 }
847
848 return ((d->tagIds.contains(tagId) || d->tagIds.isEmpty())
849 && (d->resourceIds.contains(resourceId) || d->resourceIds.isEmpty()));
850}
851
852bool KisTagResourceModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
853{
854 QString nameLeft = sourceModel()->data(source_left, Qt::UserRole + KisAllTagResourceModel::ResourceName).toString();
855 QString nameRight = sourceModel()->data(source_right, Qt::UserRole + KisAllTagResourceModel::ResourceName).toString();
856 return nameLeft.toLower() < nameRight.toLower();
857}
858
859void KisTagResourceModel::storageChanged(const QString &location)
860{
861 Q_UNUSED(location);
862 invalidateFilter();
863}
864
866{
867 int id = data(index, Qt::UserRole + KisAllTagResourceModel::ResourceId).toInt();
868 if (id < 1) return nullptr;
870 return res;
871}
872
874{
875 if (!resource || !resource->valid() || resource->resourceId() < 0) return QModelIndex();
876
877 for (int i = 0; i < rowCount(); ++i) {
878 QModelIndex idx = index(i, Qt::UserRole + KisAllTagResourceModel::ResourceId);
879 Q_ASSERT(idx.isValid());
880 if (idx.data(Qt::UserRole + KisAllTagResourceModel::ResourceId).toInt() == resource->resourceId()) {
881 return idx;
882 }
883 }
884 return QModelIndex();
885}
886
887QModelIndex KisTagResourceModel::indexForResourceId(int resourceId) const
888{
889 if (resourceId < 0) return QModelIndex();
890 for (int i = 0; i < rowCount(); ++i) {
891 QModelIndex idx = index(i, Qt::UserRole + KisAllTagResourceModel::ResourceId);
892 Q_ASSERT(idx.isValid());
893 if (idx.data(Qt::UserRole + KisAllTagResourceModel::ResourceId).toInt() == resourceId) {
894 return idx;
895 }
896 }
897 return QModelIndex();
898}
899
900bool KisTagResourceModel::setResourceActive(const QModelIndex &index, bool value)
901{
902 KisResourceModel resourceModel(d->resourceType);
903 QModelIndex idx = resourceModel.indexForResource(resourceForIndex(index));
904 return resourceModel.setResourceActive(idx, value);
905}
906
907KoResourceSP KisTagResourceModel::importResourceFile(const QString &filename, const bool allowOverwrite, const QString &storageId)
908{
909 // Since we're importing the resource, there's no reason to add rows to the tags::resources table,
910 // because the resource is untagged.
911 KisResourceModel resourceModel(d->resourceType);
912 return resourceModel.importResourceFile(filename, allowOverwrite, storageId);
913}
914
915KoResourceSP KisTagResourceModel::importResource(const QString &filename, QIODevice *device, const bool allowOverwrite, const QString &storageId)
916{
917 // Since we're importing the resource, there's no reason to add rows to the tags::resources table,
918 // because the resource is untagged.
919 KisResourceModel resourceModel(d->resourceType);
920 return resourceModel.importResource(filename, device, allowOverwrite, storageId);
921}
922
923bool KisTagResourceModel::importWillOverwriteResource(const QString &fileName, const QString &storageLocation) const
924{
925 KisResourceModel resourceModel(d->resourceType);
926 return resourceModel.importWillOverwriteResource(fileName, storageLocation);
927}
928
929bool KisTagResourceModel::exportResource(KoResourceSP resource, QIODevice *device)
930{
931 KisResourceModel resourceModel(d->resourceType);
932 return resourceModel.exportResource(resource, device);
933}
934
935bool KisTagResourceModel::addResource(KoResourceSP resource, const QString &storageId)
936{
937 // Since we're importing the resource, there's no reason to add rows to the tags::resources table,
938 // because the resource is untagged.
939 KisResourceModel resourceModel(d->resourceType);
940 return resourceModel.addResource(resource, storageId);
941}
942
944{
945 KisResourceModel resourceModel(d->resourceType);
946 bool r = resourceModel.updateResource(resource);
947 if (r) {
948 QModelIndex index = indexForResource(resource);
949 if (index.isValid()) {
950 Q_EMIT dataChanged(index, index, {Qt::EditRole});
951 }
952 }
953 return r;
954}
955
957{
958 KisResourceModel resourceModel(d->resourceType);
959 bool r = resourceModel.reloadResource(resource);
960 if (r) {
961 QModelIndex index = indexForResource(resource);
962 if (index.isValid()) {
963 Q_EMIT dataChanged(index, index, {Qt::EditRole});
964 }
965 }
966 return r;
967}
968
969bool KisTagResourceModel::renameResource(KoResourceSP resource, const QString &name)
970{
971 KisResourceModel resourceModel(d->resourceType);
972 bool r = resourceModel.renameResource(resource, name);
973 if (r) {
974 QModelIndex index = indexForResource(resource);
975 if (index.isValid()) {
976 Q_EMIT dataChanged(index, index, {Qt::EditRole});
977 }
978 }
979 return r;
980}
981
982bool KisTagResourceModel::setResourceMetaData(KoResourceSP resource, QMap<QString, QVariant> metadata)
983{
984 KisResourceModel resourceModel(d->resourceType);
985 return resourceModel.setResourceMetaData(resource, metadata);
986}
987
988QVariant KisTagResourceModel::headerData(int section, Qt::Orientation orientation, int role) const
989{
990 return d->sourceModel->headerData(section, orientation, role);
991}
float value(const T *src, size_t ch)
qreal v
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
@ Status
Whether the resource is active. Duplicate of ResourceActive.
@ ResourceActive
Whether the current resource is active.
@ Dirty
A dirty resource is one that has been modified locally but not saved.
@ LargeThumbnail
A larger thumbnail for displaying in a tooltip. 200x200 or so.
@ MetaData
MetaData is a map of key, value pairs that is associated with this resource.
@ StorageActive
Whether the current resource's storage is active.
KisAllTagResourceModel(const QString &resourceType, QObject *parent=0)
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
void removeStorage(const QString &location)
int rowCount(const QModelIndex &parent=QModelIndex()) const override
int isResourceTagged(const KisTagSP tag, const int resourceId) override
isResourceTagged
void slotResourceActiveStateChanged(const QString &resourceType, int resourceId)
int columnCount(const QModelIndex &parent=QModelIndex()) const override
void addStorage(const QString &location)
QString createQuery(bool onlyAchieve=true, bool returnADbIndexToo=false)
bool tagResources(const KisTagSP tag, const QVector< int > &resourceIds) override
bool untagResources(const KisTagSP tag, const QVector< int > &resourceId) override
QVariant data(const QModelIndex &index, int role) const override
Note: only role is significant, column is not.
KisTagSP tagForUrl(const QString &tagUrl, const QString resourceType)
tagForUrl create a tag from the database
KoResourceSP resourceForId(int resourceId)
resourceForId returns the resource with the given id, or 0 if no such resource exists....
static KisResourceLocator * instance()
static KisAllTagResourceModel * tagResourceModel(const QString &resourceType)
The KisResourceModel class provides the main access to resources. It is possible to filter the resour...
bool setResourceMetaData(KoResourceSP resource, QMap< QString, QVariant > metadata) override
setResourceMetaData
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 reloadResource(KoResourceSP resource) override
reloadResource
bool renameResource(KoResourceSP resource, const QString &name) override
renameResource name the given resource. The resource will have its name field reset,...
bool exportResource(KoResourceSP resource, QIODevice *device) override
exportResource exports a resource into a QIODevice
bool updateResource(KoResourceSP resource) override
updateResource creates a new version of the resource in the storage and in the database....
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...
QModelIndex indexForResource(KoResourceSP resource) const override
indexFromResource
bool setResourceActive(const QModelIndex &index, bool value) override
setResourceActive changes 'active' state of the resource
KoResourceSP importResource(const QString &filename, QIODevice *device, const bool allowOverwrite, const QString &storageId=QString("")) override
importResource imports a resource from a QIODevice
static QVariant variantFromResourceQuery(const QSqlQuery &query, int column, int role, bool useResourcePrefix)
variantFromResourceQuery returns a QVariant for the given column and or role
static KisStorageModel * instance()
bool setResourceMetaData(KoResourceSP resource, QMap< QString, QVariant > metadata) override
setResourceMetaData
void setResourceFilter(ResourceFilter filter) override
QModelIndex indexForResourceId(int resourceId) const override
indexFromResource
bool untagResources(const KisTagSP tag, const QVector< int > &resourceIds) override
bool setResourceActive(const QModelIndex &index, bool value) override
setResourceActive changes 'active' state of the resource
KoResourceSP importResource(const QString &filename, QIODevice *device, const bool allowOverwrite, const QString &storageId=QString()) override
importResource imports a resource from a QIODevice
bool addResource(KoResourceSP resource, const QString &storageId) override
addResource adds the given resource to the database and storage. If the resource already exists in th...
void storageChanged(const QString &location)
void setTagFilter(TagFilter filter)
KisTagResourceModel(const QString &resourceType, QObject *parent=0)
bool tagResources(const KisTagSP tag, const QVector< int > &resourceIds) override
void setTagsFilter(const QVector< int > tagIds)
QModelIndex indexForResource(KoResourceSP resource) const override
indexFromResource
void setResourcesFilter(const QVector< int > resourceIds)
bool renameResource(KoResourceSP resource, const QString &name) override
renameResource name the given resource. The resource will have its name field reset,...
KoResourceSP importResourceFile(const QString &filename, const bool allowOverwrite, const QString &storageId=QString()) override
importResourceFile
bool reloadResource(KoResourceSP resource) override
reloadResource
int isResourceTagged(const KisTagSP tag, const int resourceId) override
isResourceTagged
bool updateResource(KoResourceSP resource) override
updateResource creates a new version of the resource in the storage and in the database....
bool importWillOverwriteResource(const QString &fileName, const QString &storageLocation) const override
importWillOverwriteResource checks is importing a resource with this filename will overwrite anything
KoResourceSP resourceForIndex(QModelIndex index) const override
resourceForIndex returns a properly versioned and id'ed resource object
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
bool exportResource(KoResourceSP resource, QIODevice *device) override
exportResource exports a resource into a QIODevice
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
void setStorageFilter(StorageFilter filter) override
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override
static QString currentLocale()
Definition KisTag.cpp:84
#define KIS_ASSERT_RECOVER(cond)
Definition kis_assert.h:55
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
KisAllTagResourceModel * sourceModel