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
41 : QStyledItemDelegate(parent)
42 , m_bundleManagerProxyModel(proxy)
43{
44
45}
46
47QSize DlgBundleManager::ItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
48{
49 Q_UNUSED(option);
50 Q_UNUSED(index);
51
52 return QSize(100, 30);
53}
54
55void DlgBundleManager::ItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
56{
57 if (!index.isValid()) {
58 return;
59 }
60
61 QModelIndex sourceIndex = m_bundleManagerProxyModel->mapToSource(index);
62
63 int minMargin = 3;
64 int textMargin = 10;
65
66 painter->save();
67
68 // paint background
69 QColor bgColor = option.state & QStyle::State_Selected ?
70 qApp->palette().color(QPalette::Highlight) :
71 qApp->palette().color(QPalette::Base);
72
73 QBrush oldBrush(painter->brush());
74 QPen oldPen = painter->pen();
75
76 painter->setBrush(QBrush(bgColor));
77 painter->setPen(Qt::NoPen);
78 painter->drawRect(option.rect);
79
80 QRect paintRect = kisGrowRect(option.rect, -minMargin);
81 int height = paintRect.height();
82
83 // make border around active ones
84 bool active = KisStorageModel::instance()->data(sourceIndex, Qt::UserRole + KisStorageModel::Active).toBool();
85
86 if (active) {
87 QColor borderColor = option.state & QStyle::State_Selected ?
88 qApp->palette().color(QPalette::HighlightedText) :
89 qApp->palette().color(QPalette::Text);
90 painter->setBrush(Qt::NoBrush);
91 painter->setPen(QPen(borderColor));
92
93 QRect borderRect = kisGrowRect(paintRect, -painter->pen().widthF());
94
95 painter->drawRect(borderRect);
96
97 painter->setBrush(oldBrush);
98 painter->setPen(oldPen);
99
100 }
101
102 // paint the image
103 QImage thumbnail = KisStorageModel::instance()->data(sourceIndex, Qt::UserRole + KisStorageModel::Thumbnail).value<QImage>();
104
105 QRect iconRect = paintRect;
106 iconRect.setWidth(height);
107 painter->drawImage(iconRect, thumbnail);
108
109 QRect nameRect = paintRect;
110 nameRect.setX(paintRect.x() + height + textMargin);
111 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
112
113 QColor textColor = option.state & QStyle::State_Selected ?
114 qApp->palette().color(QPalette::HighlightedText) :
115 qApp->palette().color(QPalette::Text);
116 painter->setPen(QPen(textColor));
117
118 QTextOption textCenterOption;
119 textCenterOption.setAlignment(Qt::AlignVCenter);
120 textCenterOption.setWrapMode(QTextOption::NoWrap);
121 QString name = KisStorageModel::instance()->data(sourceIndex, Qt::UserRole + KisStorageModel::DisplayName).toString();
122 painter->drawText(nameRect, name, textCenterOption);
123
124 painter->restore();
125
126}
127
128
130 : KoDialog(parent)
131{
132 setCaption(i18n("Manage Resource Libraries"));
133
134 m_ui = new WdgDlgBundleManager(this);
135 Q_CHECK_PTR(m_ui);
137
138 m_ui->bnAdd->setIcon(KisIconUtils::loadIcon("list-add"));
139 m_ui->bnAdd->setText(i18nc("In bundle manager; press button to import a resource library", "Import"));
140 connect(m_ui->bnAdd, SIGNAL(clicked(bool)), SLOT(addBundle()));
141
142 m_ui->bnToggle->setIcon(KisIconUtils::loadIcon("edit-delete"));
143 m_ui->bnToggle->setText(i18nc("In bundle manager; press button to deactivate the resource library"
144 "(remove resources from the resource library from the available resources)", "Deactivate"));
145 connect(m_ui->bnToggle, SIGNAL(clicked(bool)), SLOT(toggleBundle()));
146
147 m_ui->bnNew->setIcon(KisIconUtils::loadIcon("document-new"));
148 m_ui->bnNew->setText(i18nc("In bundle manager; press button to create a new bundle", "Create Bundle"));
149 connect(m_ui->bnNew, SIGNAL(clicked(bool)), SLOT(createBundle()));
150
151 m_ui->bnEdit->setIcon(KisIconUtils::loadIcon("document-new"));
152 m_ui->bnEdit->setText(i18nc("In bundle manager; press button to edit existing bundle", "Edit Bundle"));
153 connect(m_ui->bnEdit, SIGNAL(clicked(bool)), SLOT(editBundle()));
154
156
158 m_proxyModel->setSourceModel(KisStorageModel::instance());
164
165 m_ui->listView->setModel(m_proxyModel);
166 m_ui->listView->setItemDelegate(new ItemDelegate(this, m_proxyModel));
167 QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(m_ui->listView);
168 if (scroller) {
169 connect(scroller, &QScroller::stateChanged, this, [&](QScroller::State state) {
171 });
172 }
173
174 QItemSelectionModel* selectionModel = m_ui->listView->selectionModel();
175 connect(selectionModel, &QItemSelectionModel::currentChanged, this, &DlgBundleManager::currentCellSelectedChanged);
176
177 connect(KisStorageModel::instance(), &KisStorageModel::modelAboutToBeReset, this, &DlgBundleManager::slotModelAboutToBeReset);
178 connect(KisStorageModel::instance(), &KisStorageModel::modelReset, this, &DlgBundleManager::slotModelReset);
179 connect(KisStorageModel::instance(), &KisStorageModel::rowsRemoved, this, &DlgBundleManager::slotRowsRemoved);
180 connect(KisStorageModel::instance(), &KisStorageModel::rowsInserted, this, &DlgBundleManager::slotRowsInserted);
181
182 updateToggleButton(m_proxyModel->data(m_ui->listView->currentIndex(), Qt::UserRole + KisStorageModel::Active).toBool());
183}
184
186{
188 if (mw) {
189 QString warning;
190 if (!mw->checkActiveBundlesAvailable()) {
191 warning = i18n("You don't have any resource bundles enabled.");
192 }
193
194 if (!mw->checkPaintOpAvailable()) {
195 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.");
196 QMessageBox::critical(this, i18nc("@title:window", "Krita"), warning);
197 return;
198 }
199
200 if (!mw->checkActiveBundlesAvailable()) {
201 QMessageBox::warning(this, i18nc("@title:window", "Krita"), warning + i18n("\nOnly your local resources are available."));
202 }
203 }
204 KoDialog::done(res);
205}
206
208{
209 KoFileDialog dlg(this, KoFileDialog::OpenFiles, i18n("Choose the resource library to import"));
210 dlg.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
212 {"application/x-krita-bundle", "image/x-adobe-brushlibrary", "application/x-photoshop-style-library"});
213 dlg.setCaption(i18n("Select the bundle"));
214
215 Q_FOREACH(const QString &filename, dlg.filenames()) {
216 if (!filename.isEmpty()) {
217 KisCursorOverrideLock cursorLock(Qt::BusyCursor);
218
219 // 0. Validate bundle
220 {
222 KIS_ASSERT(!storage.isNull());
223
224 if (!storage->valid()) {
225 cursorLock.unlock();
226 qWarning() << "Attempted to import an invalid bundle!" << filename;
227 QMessageBox::warning(this,
228 i18nc("@title:window", "Krita"),
229 i18n("Could not load bundle %1.", filename));
230 continue;
231 }
232 }
233
234 // 1. Copy the bundle to the resource folder
235 QFileInfo oldFileInfo(filename);
236
237 QString newDir = KoResourcePaths::getAppDataLocation();
238 QString newName = oldFileInfo.fileName();
239 const QString newLocation = QStringLiteral("%1/%2").arg(newDir, newName);
240
241 QFileInfo newFileInfo(newLocation);
242 if (newFileInfo.exists()) {
243 cursorLock.unlock();
244 if (QMessageBox::warning(
245 this,
246 i18nc("@title:window", "Warning"),
247 i18n("There is already a bundle with this name installed. Do you want to overwrite it?"),
248 QMessageBox::Ok | QMessageBox::Cancel)
249 == QMessageBox::Cancel) {
250 continue;
251 } else {
252 QFile::remove(newLocation);
253 }
254 cursorLock.lock();
255 }
256 QFile::copy(filename, newLocation);
257
258 // 2. Add the bundle as a storage/update database
260 KIS_ASSERT(!storage.isNull());
261 if (!KisResourceLocator::instance()->addStorage(newLocation, storage)) {
262 qWarning() << "Could not add bundle to the storages" << newLocation;
263 }
264 }
265 }
266}
267
269{
270 DlgCreateBundle* dlg = new DlgCreateBundle(0, this);
271 dlg->exec();
272}
273
275{
276 KoFileDialog dlg(this, KoFileDialog::OpenFiles, i18n("Choose the bundle to edit"));
277 dlg.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
279 {"application/x-krita-bundle", "image/x-adobe-brushlibrary", "application/x-photoshop-style-library"});
280 dlg.setCaption(i18n("Select the bundle"));
281
282
283 Q_FOREACH(const QString &filename, dlg.filenames()) {
284 if (!filename.isEmpty()) {
285
286 {
288 KIS_ASSERT(!storage.isNull());
289
290 if (!storage->valid()) {
291 qApp->restoreOverrideCursor();
292 qWarning() << "Attempted to edit an invalid bundle!" << filename;
293 QMessageBox::warning(this,
294 i18nc("@title:window", "Krita"),
295 i18n("Could not load bundle %1.", filename));
296 qApp->setOverrideCursor(Qt::BusyCursor);
297 continue;
298 }
299 }
300
302 if (storage.isNull()){continue;}
303 if (storage->valid()) {
304 if (!KisResourceLocator::instance()->hasStorage(filename)) {
305 if (!KisResourceLocator::instance()->addStorage(filename, storage)) {
306 qWarning() << "Could not add bundle to the storages" << filename;
307 }
308 }
309
310 KoResourceBundle *bundle = new KoResourceBundle(filename);
311 bool importedBundle = bundle->load();
312
313 KoResourceBundleSP bundleSP(bundle);
314 DlgCreateBundle* dlgBC = new DlgCreateBundle(bundleSP, this);
315 int response = dlgBC->exec();
316 }
317 }
318 }
319}
320
322{
323 QModelIndex idx = m_ui->listView->currentIndex();
325
326 if (!idx.isValid()) {
327 ENTER_FUNCTION() << "Index is invalid\n";
328 return;
329 }
330
331 bool active = m_proxyModel->data(idx, Qt::UserRole + KisStorageModel::Active).toBool();
332 idx = m_proxyModel->index(idx.row(), 0);
333 m_proxyModel->setData(idx, QVariant(!active), Qt::CheckStateRole);
334
335 if (active) {
336 m_ui->bnEdit->setEnabled(false);
337 } else {
338 m_ui->bnEdit->setEnabled(true);
339 }
340
342
344 if (mw) {
345 QString warning;
346 if (!mw->checkActiveBundlesAvailable()) {
347 warning = i18n("You don't have any resource bundles enabled.");
348 }
349
350 if (!mw->checkPaintOpAvailable()) {
351 button(KoDialog::Close)->setEnabled(false);
352
353 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.");
354 QMessageBox::critical(this, i18nc("@title:window", "Krita"), warning);
355 return;
356 }
357
358 if (!mw->checkActiveBundlesAvailable()) {
359 QMessageBox::warning(this, i18nc("@title:window", "Krita"), warning + i18n("\nOnly your local resources are available."));
360 }
361 }
362 button(KoDialog::Close)->setEnabled(true);
363}
364
366{
368 lastIndex = QPersistentModelIndex(m_proxyModel->mapToSource(m_ui->listView->currentIndex()));
369 ENTER_FUNCTION() << ppVar(lastIndex) << ppVar(lastIndex.isValid());
370}
371
372void DlgBundleManager::slotRowsRemoved(const QModelIndex &parent, int start, int end)
373{
374 m_proxyModel->removeRows(start, end - start + 1, parent);
375}
376
377void DlgBundleManager::slotRowsInserted(const QModelIndex &parent, int start, int end)
378{
379 m_proxyModel->insertRows(start, end - start + 1, parent);
380}
381
383{
385 ENTER_FUNCTION() << ppVar(lastIndex) << ppVar(lastIndex.isValid());
386 if (lastIndex.isValid()) {
387 ENTER_FUNCTION() << "last index valid!";
388 m_ui->listView->setCurrentIndex(m_proxyModel->mapToSource(lastIndex));
389 }
390 lastIndex = QModelIndex();
391}
392
393void DlgBundleManager::currentCellSelectedChanged(QModelIndex current, QModelIndex previous)
394{
395 Q_UNUSED(previous);
397 if (!current.isValid()) {
398 ENTER_FUNCTION() << "Index is invalid\n";
399 return;
400 }
401 bool active = m_proxyModel->data(current, Qt::UserRole + KisStorageModel::Active).toBool();
402 updateToggleButton(active);
404}
405
407{
408 if (active) {
409 m_ui->bnToggle->setIcon(KisIconUtils::loadIcon("edit-delete"));
410 m_ui->bnToggle->setText(i18nc("In bundle manager; press button to deactivate the bundle "
411 "(remove resources from the bundle from the available resources)","Deactivate"));
412 m_ui->bnEdit->setEnabled(true);
413 } else {
414 m_ui->bnToggle->setIcon(QIcon());
415 m_ui->bnToggle->setText(i18nc("In bundle manager; press button to activate the bundle "
416 "(add resources from the bundle to the available resources)","Activate"));
417 m_ui->bnEdit->setEnabled(false);
418 }
419}
420
422{
425
426 m_ui->detailsPanel->hide();
427 m_ui->lblDescription->hide();
428
429 m_ui->BundleSelectedGroupBox->setTitle(storage->name());
430
431 if (storage->type() == KisResourceStorage::StorageType::Bundle) {
432
433 m_ui->detailsPanel->show();
434 m_ui->lblDescription->show();
435
436 m_ui->lblAuthor->setText(storage->metaData(KisResourceStorage::s_meta_author).toString());
437 QString date = storage->metaData(KisResourceStorage::s_meta_creation_date).toString();
438 m_ui->lblCreated->setText(date);
439 QString date2 = storage->metaData(KisResourceStorage::s_meta_dc_date).toString();
440 m_ui->lblUpdated->setText(date2);
441
442 m_ui->lblDescription->setPlainText(storage->metaData(KisResourceStorage::s_meta_description).toString());
443 m_ui->lblEmail->setText(storage->metaData(KisResourceStorage::s_meta_email).toString());
444 m_ui->lblLicense->setText(storage->metaData(KisResourceStorage::s_meta_license).toString());
445 m_ui->lblWebsite->setText(storage->metaData(KisResourceStorage::s_meta_website).toString());
446
447
448 }
449
450 QImage thumbnail = KisStorageModel::instance()->data(m_proxyModel->mapToSource(idx), Qt::UserRole + KisStorageModel::Thumbnail).value<QImage>();
451 m_ui->lblPreview->setPixmap(QPixmap::fromImage(thumbnail));
452 m_ui->lblType->setText(KisResourceStorage::storageTypeToString(storage->type()));
453}
454
455
QList< QString > QStringList
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
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:483
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:591
void setMainWidget(QWidget *widget)
Definition KoDialog.cpp:354
virtual void setCaption(const QString &caption)
Definition KoDialog.cpp:498
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)