Krita Source Code Documentation
Loading...
Searching...
No Matches
dlg_bundle_manager.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2014 Victor Lafon metabolic.ewilan @hotmail.fr
3 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
4 * SPDX-FileCopyrightText: 2023 Srirupa Datta <srirupa.sps@gmail.com>
5 *
6 * SPDX-License-Identifier: LGPL-2.0-or-later
7 */
9
10#include "resourcemanager.h"
11#include "dlg_create_bundle.h"
12
13#include <QPainter>
14#include <QPixmap>
15#include <QMessageBox>
16#include <QInputDialog>
17#include <QItemSelectionModel>
18#include <QStringLiteral>
19
20#include <kconfiggroup.h>
21#include <ksharedconfig.h>
22#include <KoIcon.h>
23#include <KoFileDialog.h>
24
25#include <kis_icon.h>
26#include "kis_action.h"
27#include <KisResourceStorage.h>
29#include <KisStorageModel.h>
31#include <kis_config.h>
32#include <KisResourceLocator.h>
33#include <KisKineticScroller.h>
35#include "KisBundleStorage.h"
36
37#include <KisMainWindow.h>
38#include <KisPart.h>
39
40#ifdef Q_OS_ANDROID
42#endif
43
45 : QStyledItemDelegate(parent)
46 , m_bundleManagerProxyModel(proxy)
47{
48
49}
50
51QSize DlgBundleManager::ItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
52{
53 Q_UNUSED(option);
54 Q_UNUSED(index);
55
56 return QSize(100, 30);
57}
58
59void DlgBundleManager::ItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
60{
61 if (!index.isValid()) {
62 return;
63 }
64
65 QModelIndex sourceIndex = m_bundleManagerProxyModel->mapToSource(index);
66
67 int minMargin = 3;
68 int textMargin = 10;
69
70 painter->save();
71
72 // paint background
73 QColor bgColor = option.state & QStyle::State_Selected ?
74 qApp->palette().color(QPalette::Highlight) :
75 qApp->palette().color(QPalette::Base);
76
77 QBrush oldBrush(painter->brush());
78 QPen oldPen = painter->pen();
79
80 painter->setBrush(QBrush(bgColor));
81 painter->setPen(Qt::NoPen);
82 painter->drawRect(option.rect);
83
84 QRect paintRect = kisGrowRect(option.rect, -minMargin);
85 int height = paintRect.height();
86
87 // make border around active ones
88 bool active = KisStorageModel::instance()->data(sourceIndex, Qt::UserRole + KisStorageModel::Active).toBool();
89
90 if (active) {
91 QColor borderColor = option.state & QStyle::State_Selected ?
92 qApp->palette().color(QPalette::HighlightedText) :
93 qApp->palette().color(QPalette::Text);
94 painter->setBrush(Qt::NoBrush);
95 painter->setPen(QPen(borderColor));
96
97 QRect borderRect = kisGrowRect(paintRect, -painter->pen().widthF());
98
99 painter->drawRect(borderRect);
100
101 painter->setBrush(oldBrush);
102 painter->setPen(oldPen);
103
104 }
105
106 // paint the image
107 QImage thumbnail = KisStorageModel::instance()->data(sourceIndex, Qt::UserRole + KisStorageModel::Thumbnail).value<QImage>();
108
109 QRect iconRect = paintRect;
110 iconRect.setWidth(height);
111 painter->drawImage(iconRect, thumbnail);
112
113 QRect nameRect = paintRect;
114 nameRect.setX(paintRect.x() + height + textMargin);
115 nameRect.setWidth(paintRect.width() - height - 1.5*textMargin); // should be 2, but with 1.5 it's more clear that it's cropped when it is
116
117 QColor textColor = option.state & QStyle::State_Selected ?
118 qApp->palette().color(QPalette::HighlightedText) :
119 qApp->palette().color(QPalette::Text);
120 painter->setPen(QPen(textColor));
121
122 QTextOption textCenterOption;
123 textCenterOption.setAlignment(Qt::AlignVCenter);
124 textCenterOption.setWrapMode(QTextOption::NoWrap);
125 QString name = KisStorageModel::instance()->data(sourceIndex, Qt::UserRole + KisStorageModel::DisplayName).toString();
126 painter->drawText(nameRect, name, textCenterOption);
127
128 painter->restore();
129
130}
131
132
134 : KoDialog(parent)
135{
136 setCaption(i18n("Manage Resource Libraries"));
137
138 m_ui = new WdgDlgBundleManager(this);
139 Q_CHECK_PTR(m_ui);
141
142 m_ui->bnAdd->setIcon(KisIconUtils::loadIcon("list-add"));
143 m_ui->bnAdd->setText(i18nc("In bundle manager; press button to import a resource library", "Import"));
144 connect(m_ui->bnAdd, SIGNAL(clicked(bool)), SLOT(addBundle()));
145
146 m_ui->bnToggle->setIcon(KisIconUtils::loadIcon("edit-delete"));
147 m_ui->bnToggle->setText(i18nc("In bundle manager; press button to deactivate the resource library"
148 "(remove resources from the resource library from the available resources)", "Deactivate"));
149 connect(m_ui->bnToggle, SIGNAL(clicked(bool)), SLOT(toggleBundle()));
150
151 m_ui->bnNew->setIcon(KisIconUtils::loadIcon("document-new"));
152 m_ui->bnNew->setText(i18nc("In bundle manager; press button to create a new bundle", "Create Bundle"));
153 connect(m_ui->bnNew, SIGNAL(clicked(bool)), SLOT(createBundle()));
154
155 m_ui->bnEdit->setIcon(KisIconUtils::loadIcon("document-new"));
156 m_ui->bnEdit->setText(i18nc("In bundle manager; press button to edit existing bundle", "Edit Bundle"));
157 connect(m_ui->bnEdit, SIGNAL(clicked(bool)), SLOT(editBundle()));
158
159#ifdef Q_OS_ANDROID
160 connect(m_ui->bnSupport, &QPushButton::clicked, this, &DlgBundleManager::slotShowSupporterBundlesDialog);
161#else
162 m_ui->bnSupport->hide();
163#endif
164
166
168 m_proxyModel->setSourceModel(KisStorageModel::instance());
174
175 m_ui->listView->setModel(m_proxyModel);
176 m_ui->listView->setItemDelegate(new ItemDelegate(this, m_proxyModel));
177 QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(m_ui->listView);
178 if (scroller) {
179 connect(scroller, &QScroller::stateChanged, this, [&](QScroller::State state) {
181 });
182 }
183
184 QItemSelectionModel* selectionModel = m_ui->listView->selectionModel();
185 connect(selectionModel, &QItemSelectionModel::currentChanged, this, &DlgBundleManager::currentCellSelectedChanged);
186
187 connect(KisStorageModel::instance(), &KisStorageModel::modelAboutToBeReset, this, &DlgBundleManager::slotModelAboutToBeReset);
188 connect(KisStorageModel::instance(), &KisStorageModel::modelReset, this, &DlgBundleManager::slotModelReset);
189 connect(KisStorageModel::instance(), &KisStorageModel::rowsRemoved, this, &DlgBundleManager::slotRowsRemoved);
190 connect(KisStorageModel::instance(), &KisStorageModel::rowsInserted, this, &DlgBundleManager::slotRowsInserted);
191
192 updateToggleButton(m_proxyModel->data(m_ui->listView->currentIndex(), Qt::UserRole + KisStorageModel::Active).toBool());
193}
194
196{
198 if (mw) {
199 QString warning;
200 if (!mw->checkActiveBundlesAvailable()) {
201 warning = i18n("You don't have any resource bundles enabled.");
202 }
203
204 if (!mw->checkPaintOpAvailable()) {
205 warning += i18n("\nThere are no brush presets available. Please enable a bundle that has presets before continuing.\nIf there are no bundles, please import a bundle before continuing.");
206 QMessageBox::critical(this, i18nc("@title:window", "Krita"), warning);
207 return;
208 }
209
210 if (!mw->checkActiveBundlesAvailable()) {
211 QMessageBox::warning(this, i18nc("@title:window", "Krita"), warning + i18n("\nOnly your local resources are available."));
212 }
213 }
214 KoDialog::done(res);
215}
216
218{
219 KoFileDialog dlg(this, KoFileDialog::OpenFiles, i18n("Choose the resource library to import"));
220 dlg.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
222 {"application/x-krita-bundle", "image/x-adobe-brushlibrary", "application/x-photoshop-style-library"});
223 dlg.setCaption(i18n("Select the bundle"));
224
225 Q_FOREACH(const QString &filename, dlg.filenames()) {
226 if (!filename.isEmpty()) {
227 KisCursorOverrideLock cursorLock(Qt::BusyCursor);
228
229 // 0. Validate bundle
230 {
232 KIS_ASSERT(!storage.isNull());
233
234 if (!storage->valid()) {
235 cursorLock.unlock();
236 qWarning() << "Attempted to import an invalid bundle!" << filename;
237 QMessageBox::warning(this,
238 i18nc("@title:window", "Krita"),
239 i18n("Could not load bundle %1.", filename));
240 continue;
241 }
242 }
243
244 // 1. Copy the bundle to the resource folder
245 QFileInfo oldFileInfo(filename);
246
247 QString newDir = KoResourcePaths::getAppDataLocation();
248 QString newName = oldFileInfo.fileName();
249 const QString newLocation = QStringLiteral("%1/%2").arg(newDir, newName);
250
251 QFileInfo newFileInfo(newLocation);
252 if (newFileInfo.exists()) {
253 cursorLock.unlock();
254 if (QMessageBox::warning(
255 this,
256 i18nc("@title:window", "Warning"),
257 i18n("There is already a bundle with this name installed. Do you want to overwrite it?"),
258 QMessageBox::Ok | QMessageBox::Cancel)
259 == QMessageBox::Cancel) {
260 continue;
261 } else {
262 QFile::remove(newLocation);
263 }
264 cursorLock.lock();
265 }
266 QFile::copy(filename, newLocation);
267
268 // 2. Add the bundle as a storage/update database
270 KIS_ASSERT(!storage.isNull());
271 if (!KisResourceLocator::instance()->addStorage(newLocation, storage)) {
272 qWarning() << "Could not add bundle to the storages" << newLocation;
273 }
274 }
275 }
276}
277
279{
280 DlgCreateBundle* dlg = new DlgCreateBundle(0, this);
281 dlg->exec();
282}
283
285{
286 KoFileDialog dlg(this, KoFileDialog::OpenFiles, i18n("Choose the bundle to edit"));
287 dlg.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
289 {"application/x-krita-bundle", "image/x-adobe-brushlibrary", "application/x-photoshop-style-library"});
290 dlg.setCaption(i18n("Select the bundle"));
291
292
293 Q_FOREACH(const QString &filename, dlg.filenames()) {
294 if (!filename.isEmpty()) {
295
296 {
298 KIS_ASSERT(!storage.isNull());
299
300 if (!storage->valid()) {
301 qApp->restoreOverrideCursor();
302 qWarning() << "Attempted to edit an invalid bundle!" << filename;
303 QMessageBox::warning(this,
304 i18nc("@title:window", "Krita"),
305 i18n("Could not load bundle %1.", filename));
306 qApp->setOverrideCursor(Qt::BusyCursor);
307 continue;
308 }
309 }
310
312 if (storage.isNull()){continue;}
313 if (storage->valid()) {
314 if (!KisResourceLocator::instance()->hasStorage(filename)) {
315 if (!KisResourceLocator::instance()->addStorage(filename, storage)) {
316 qWarning() << "Could not add bundle to the storages" << filename;
317 }
318 }
319
320 KoResourceBundle *bundle = new KoResourceBundle(filename);
321 bool importedBundle = bundle->load();
322
323 KoResourceBundleSP bundleSP(bundle);
324 DlgCreateBundle* dlgBC = new DlgCreateBundle(bundleSP, this);
325 int response = dlgBC->exec();
326 }
327 }
328 }
329}
330
331#ifdef Q_OS_ANDROID
332void DlgBundleManager::slotShowSupporterBundlesDialog()
333{
334 QScopedPointer<KisSupporterBundlesDialog> dlg(new KisSupporterBundlesDialog(this));
335 dlg->exec();
336}
337#endif
338
340{
341 QModelIndex idx = m_ui->listView->currentIndex();
343
344 if (!idx.isValid()) {
345 ENTER_FUNCTION() << "Index is invalid\n";
346 return;
347 }
348
349 bool active = m_proxyModel->data(idx, Qt::UserRole + KisStorageModel::Active).toBool();
350 idx = m_proxyModel->index(idx.row(), 0);
351 m_proxyModel->setData(idx, QVariant(!active), Qt::CheckStateRole);
352
353 if (active) {
354 m_ui->bnEdit->setEnabled(false);
355 } else {
356 m_ui->bnEdit->setEnabled(true);
357 }
358
360
362 if (mw) {
363 QString warning;
364 if (!mw->checkActiveBundlesAvailable()) {
365 warning = i18n("You don't have any resource bundles enabled.");
366 }
367
368 if (!mw->checkPaintOpAvailable()) {
369 button(KoDialog::Close)->setEnabled(false);
370
371 warning += i18n("\nThere are no brush presets available. Please enable a bundle that has presets before continuing.\nIf there are no bundles, please import a bundle before continuing.");
372 QMessageBox::critical(this, i18nc("@title:window", "Krita"), warning);
373 return;
374 }
375
376 if (!mw->checkActiveBundlesAvailable()) {
377 QMessageBox::warning(this, i18nc("@title:window", "Krita"), warning + i18n("\nOnly your local resources are available."));
378 }
379 }
380 button(KoDialog::Close)->setEnabled(true);
381}
382
384{
386 lastIndex = QPersistentModelIndex(m_proxyModel->mapToSource(m_ui->listView->currentIndex()));
387 ENTER_FUNCTION() << ppVar(lastIndex) << ppVar(lastIndex.isValid());
388}
389
390void DlgBundleManager::slotRowsRemoved(const QModelIndex &parent, int start, int end)
391{
392 m_proxyModel->removeRows(start, end - start + 1, parent);
393}
394
395void DlgBundleManager::slotRowsInserted(const QModelIndex &parent, int start, int end)
396{
397 m_proxyModel->insertRows(start, end - start + 1, parent);
398}
399
401{
403 ENTER_FUNCTION() << ppVar(lastIndex) << ppVar(lastIndex.isValid());
404 if (lastIndex.isValid()) {
405 ENTER_FUNCTION() << "last index valid!";
406 m_ui->listView->setCurrentIndex(m_proxyModel->mapToSource(lastIndex));
407 }
408 lastIndex = QModelIndex();
409}
410
411void DlgBundleManager::currentCellSelectedChanged(QModelIndex current, QModelIndex previous)
412{
413 Q_UNUSED(previous);
415 if (!current.isValid()) {
416 ENTER_FUNCTION() << "Index is invalid\n";
417 return;
418 }
419 bool active = m_proxyModel->data(current, Qt::UserRole + KisStorageModel::Active).toBool();
420 updateToggleButton(active);
422}
423
425{
426 if (active) {
427 m_ui->bnToggle->setIcon(KisIconUtils::loadIcon("edit-delete"));
428 m_ui->bnToggle->setText(i18nc("In bundle manager; press button to deactivate the bundle "
429 "(remove resources from the bundle from the available resources)","Deactivate"));
430 m_ui->bnEdit->setEnabled(true);
431 } else {
432 m_ui->bnToggle->setIcon(QIcon());
433 m_ui->bnToggle->setText(i18nc("In bundle manager; press button to activate the bundle "
434 "(add resources from the bundle to the available resources)","Activate"));
435 m_ui->bnEdit->setEnabled(false);
436 }
437}
438
440{
443
444 m_ui->detailsPanel->hide();
445 m_ui->lblDescription->hide();
446
447 m_ui->BundleSelectedGroupBox->setTitle(storage->name());
448
449 if (storage->type() == KisResourceStorage::StorageType::Bundle) {
450
451 m_ui->detailsPanel->show();
452 m_ui->lblDescription->show();
453
454 m_ui->lblAuthor->setText(storage->metaData(KisResourceStorage::s_meta_author).toString());
455 QString date = storage->metaData(KisResourceStorage::s_meta_creation_date).toString();
456 m_ui->lblCreated->setText(date);
457 QString date2 = storage->metaData(KisResourceStorage::s_meta_dc_date).toString();
458 m_ui->lblUpdated->setText(date2);
459
460 m_ui->lblDescription->setPlainText(storage->metaData(KisResourceStorage::s_meta_description).toString());
461 m_ui->lblEmail->setText(storage->metaData(KisResourceStorage::s_meta_email).toString());
462 m_ui->lblLicense->setText(storage->metaData(KisResourceStorage::s_meta_license).toString());
463 m_ui->lblWebsite->setText(storage->metaData(KisResourceStorage::s_meta_website).toString());
464
465
466 }
467
468 QImage thumbnail = KisStorageModel::instance()->data(m_proxyModel->mapToSource(idx), Qt::UserRole + KisStorageModel::Thumbnail).value<QImage>();
469 m_ui->lblPreview->setPixmap(QPixmap::fromImage(thumbnail));
470 m_ui->lblType->setText(KisResourceStorage::storageTypeToString(storage->type()));
471}
472
473
QList< QString > QStringList
ItemDelegate(QObject *, KisStorageFilterProxyModel *)
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
void slotRowsRemoved(const QModelIndex &parent, int first, int last)
WdgDlgBundleManager * m_ui
void done(int res) override
void updateBundleInformation(QModelIndex idx)
KisStorageFilterProxyModel * m_proxyModel
QPersistentModelIndex lastIndex
void currentCellSelectedChanged(QModelIndex current, QModelIndex previous)
DlgBundleManager(QWidget *parent=0)
void updateToggleButton(bool active)
void slotRowsInserted(const QModelIndex &parent, int first, int last)
Main window for Krita.
bool checkPaintOpAvailable()
bool checkActiveBundlesAvailable()
checkActiveStorages checks whether there is at least one bundle available and at least one paintop pr...
static KisPart * instance()
Definition KisPart.cpp:131
KisMainWindow * currentMainwindow() const
Definition KisPart.cpp:459
static KisResourceLocator * instance()
static const QString s_meta_author
static const QString s_meta_creation_date
static const QString s_meta_email
static QString storageTypeToString(StorageType storageType)
static const QString s_meta_license
static QString storageTypeToUntranslatedString(StorageType storageType)
static const QString s_meta_description
static const QString s_meta_dc_date
static const QString s_meta_website
KisResourceStorageSP storageForIndex(QModelIndex index=QModelIndex()) const
@ ByStorageType
Pass a string list of storage types.
void setFilter(FilterType filterType, QVariant filter)
static KisStorageModel * instance()
QVariant data(const QModelIndex &index, int role) const override
A dialog base class with standard buttons and predefined layouts.
Definition KoDialog.h:116
QPushButton * button(ButtonCode id) const
Definition KoDialog.cpp:582
void setMainWidget(QWidget *widget)
Definition KoDialog.cpp:345
virtual void setCaption(const QString &caption)
Definition KoDialog.cpp:489
void setButtons(ButtonCodes buttonMask)
Definition KoDialog.cpp:195
@ Close
Show Close-button. (this button closes the dialog)
Definition KoDialog.h:131
void setDefaultDir(const QString &defaultDir, bool force=false)
setDefaultDir set the default directory to defaultDir.
QStringList filenames()
Get the file names the user selected in the file dialog.
void setCaption(const QString &caption)
void setMimeTypeFilters(const QStringList &mimeTypeList, QString defaultMimeType=QString())
setMimeTypeFilters Update the list of file filters from mime types.
A KoResourceBundle is a zip file that contains resources, some metadata about the creator of the bund...
bool load()
load : Load this resource.
static QString getAppDataLocation()
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define ENTER_FUNCTION()
Definition kis_debug.h:178
#define ppVar(var)
Definition kis_debug.h:155
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
QIcon loadIcon(const QString &name)
KRITAWIDGETUTILS_EXPORT void updateCursor(QWidget *source, QScroller::State state)
KRITAWIDGETUTILS_EXPORT QScroller * createPreconfiguredScroller(QAbstractScrollArea *target)