1 /*
2  *  Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 #include "dlg_bundle_manager.h"
20 #include "ui_wdgdlgbundlemanager.h"
21 
22 #include "resourcemanager.h"
23 #include "dlg_create_bundle.h"
24 
25 #include <QListWidget>
26 #include <QTreeWidget>
27 #include <QListWidgetItem>
28 #include <QPainter>
29 #include <QPixmap>
30 #include <QMessageBox>
31 
32 #include <kis_icon.h>
33 #include "kis_action.h"
34 #include <KisResourceServerProvider.h>
35 #include <kconfiggroup.h>
36 #include <ksharedconfig.h>
37 
38 #define ICON_SIZE 48
39 
DlgBundleManager(ResourceManager * resourceManager,KisActionManager * actionMgr,QWidget * parent)40 DlgBundleManager::DlgBundleManager(ResourceManager *resourceManager, KisActionManager* actionMgr, QWidget *parent)
41     : KoDialog(parent)
42     , m_page(new QWidget())
43     , m_ui(new Ui::WdgDlgBundleManager)
44     , m_currentBundle(0)
45     , m_resourceManager(resourceManager)
46 {
47     setCaption(i18n("Manage Resource Bundles"));
48     m_ui->setupUi(m_page);
49     setMainWidget(m_page);
50     resize(m_page->sizeHint());
51     setButtons(Ok | Cancel);
52     setDefaultButton(Ok);
53 
54     m_ui->listActive->setIconSize(QSize(ICON_SIZE, ICON_SIZE));
55     m_ui->listActive->setSelectionMode(QAbstractItemView::SingleSelection);
56     connect(m_ui->listActive, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*,QListWidgetItem*)));
57     connect(m_ui->listActive, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*)));
58 
59     m_ui->listInactive->setIconSize(QSize(ICON_SIZE, ICON_SIZE));
60     m_ui->listInactive->setSelectionMode(QAbstractItemView::SingleSelection);
61     connect(m_ui->listInactive, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*,QListWidgetItem*)));
62     connect(m_ui->listInactive, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(itemSelected(QListWidgetItem*)));
63 
64     m_ui->bnAdd->setIcon(KisIconUtils::loadIcon("arrow-right"));
65     connect(m_ui->bnAdd, SIGNAL(clicked()), SLOT(addSelected()));
66 
67     m_ui->bnRemove->setIcon(KisIconUtils::loadIcon("arrow-left"));
68     connect(m_ui->bnRemove, SIGNAL(clicked()), SLOT(removeSelected()));
69 
70     m_ui->listBundleContents->setHeaderLabel(i18n("Resource"));
71     m_ui->listBundleContents->setSelectionMode(QAbstractItemView::NoSelection);
72 
73     m_actionManager = actionMgr;
74 
75     refreshListData();
76 
77     connect(m_ui->bnEditBundle, SIGNAL(clicked()), SLOT(editBundle()));
78 
79     connect(m_ui->bnImportBrushes, SIGNAL(clicked()), SLOT(slotImportResource()));
80     connect(m_ui->bnImportGradients, SIGNAL(clicked()), SLOT(slotImportResource()));
81     connect(m_ui->bnImportPalettes, SIGNAL(clicked()), SLOT(slotImportResource()));
82     connect(m_ui->bnImportPatterns, SIGNAL(clicked()), SLOT(slotImportResource()));
83     connect(m_ui->bnImportPresets, SIGNAL(clicked()), SLOT(slotImportResource()));
84     connect(m_ui->bnImportWorkspaces, SIGNAL(clicked()), SLOT(slotImportResource()));
85     connect(m_ui->bnImportBundles, SIGNAL(clicked()), SLOT(slotImportResource()));
86 
87 
88     connect(m_ui->createBundleButton, SIGNAL(clicked()), SLOT(slotCreateBundle()));
89     connect(m_ui->deleteBackupFilesButton, SIGNAL(clicked()), SLOT(slotDeleteBackupFiles()));
90     connect(m_ui->openResourceFolderButton, SIGNAL(clicked()), SLOT(slotOpenResourceFolder()));
91 
92 }
93 
94 
refreshListData()95 void DlgBundleManager::refreshListData()
96 {
97     KoResourceServer<KisResourceBundle> *bundleServer = KisResourceBundleServerProvider::instance()->resourceBundleServer();
98 
99     m_ui->listInactive->clear();
100     m_ui->listActive->clear();
101 
102     Q_FOREACH (const QString &f, bundleServer->blackListedFiles()) {
103         KisResourceBundle *bundle = new KisResourceBundle(f);
104         bundle->load();
105         if (bundle->valid()) {
106             bundle->setInstalled(false);
107             m_blacklistedBundles[f] = bundle;
108         }
109     }
110     fillListWidget(m_blacklistedBundles.values(), m_ui->listInactive);
111 
112     Q_FOREACH (KisResourceBundle *bundle, bundleServer->resources()) {
113         if (bundle->valid()) {
114             m_activeBundles[bundle->filename()] = bundle;
115         }
116     }
117     fillListWidget(m_activeBundles.values(), m_ui->listActive);
118 }
119 
accept()120 void DlgBundleManager::accept()
121 {
122     KoResourceServer<KisResourceBundle> *bundleServer = KisResourceBundleServerProvider::instance()->resourceBundleServer();
123 
124     for (int i = 0; i < m_ui->listActive->count(); ++i) {
125         QListWidgetItem *item = m_ui->listActive->item(i);
126         QByteArray ba = item->data(Qt::UserRole).toByteArray();
127         QString name = item->text();
128         KisResourceBundle *bundle = bundleServer->resourceByMD5(ba);
129         QMessageBox bundleFeedback;
130         bundleFeedback.setIcon(QMessageBox::Warning);
131         QString feedback = "bundlefeedback";
132 
133         if (!bundle) {
134             // Get it from the blacklisted bundles
135             Q_FOREACH (KisResourceBundle *b2, m_blacklistedBundles.values()) {
136                 if (b2->md5() == ba) {
137                     bundle = b2;
138                     break;
139                 }
140             }
141         }
142 
143         if (bundle) {
144             bool isKrita3Bundle = false;
145             if (bundle->filename().endsWith("Krita_3_Default_Resources.bundle")) {
146                 isKrita3Bundle = true;
147                 KConfigGroup group = KSharedConfig::openConfig()->group("BundleHack");
148                 group.writeEntry("HideKrita3Bundle", false);
149             }
150             else {
151                 if (!bundle->isInstalled()) {
152                     bundle->install();
153                     //this removes the bundle from the blacklist and add it to the server without saving or putting it in front//
154                     if (!bundleServer->addResource(bundle, false, false)){
155 
156                         feedback = i18n("Couldn't add bundle \"%1\" to resource server", name);
157                         bundleFeedback.setText(feedback);
158                         bundleFeedback.exec();
159                     }
160                     if (!isKrita3Bundle) {
161                         if (!bundleServer->removeFromBlacklist(bundle)) {
162                             feedback = i18n("Couldn't remove bundle \"%1\" from blacklist", name);
163                             bundleFeedback.setText(feedback);
164                             bundleFeedback.exec();
165                         }
166                     }
167                 }
168                 else {
169                     if (!isKrita3Bundle) {
170                         bundleServer->removeFromBlacklist(bundle);
171                     }
172                     //let's assume that bundles that exist and are installed have to be removed from the blacklist, and if they were already this returns false, so that's not a problem.
173                 }
174             }
175         }
176         else{
177             QString feedback = i18n("Bundle \"%1\" doesn't exist!", name);
178             bundleFeedback.setText(feedback);
179             bundleFeedback.exec();
180 
181         }
182     }
183 
184     for (int i = 0; i < m_ui->listInactive->count(); ++i) {
185         QListWidgetItem *item = m_ui->listInactive->item(i);
186         QByteArray ba = item->data(Qt::UserRole).toByteArray();
187         KisResourceBundle *bundle = bundleServer->resourceByMD5(ba);
188         bool isKrits3Bundle = false;
189         if (bundle) {
190             if (bundle->filename().contains("Krita_3_Default_Resources.bundle")) {
191                 isKrits3Bundle = true;
192                 KConfigGroup group = KSharedConfig::openConfig()->group("BundleHack");
193                 group.writeEntry("HideKrita3Bundle", true);
194             }
195             if (bundle->isInstalled()) {
196                 bundle->uninstall();
197                 if (!isKrits3Bundle) {
198                     bundleServer->removeResourceAndBlacklist(bundle);
199                 }
200             }
201         }
202     }
203 
204 
205     KoDialog::accept();
206 }
207 
addSelected()208 void DlgBundleManager::addSelected()
209 {
210     Q_FOREACH (QListWidgetItem *item, m_ui->listActive->selectedItems()) {
211         m_ui->listInactive->addItem(m_ui->listActive->takeItem(m_ui->listActive->row(item)));
212     }
213 
214 }
215 
removeSelected()216 void DlgBundleManager::removeSelected()
217 {
218     Q_FOREACH (QListWidgetItem *item, m_ui->listInactive->selectedItems()) {
219         m_ui->listActive->addItem(m_ui->listInactive->takeItem(m_ui->listInactive->row(item)));
220     }
221 }
222 
itemSelected(QListWidgetItem * current,QListWidgetItem *)223 void DlgBundleManager::itemSelected(QListWidgetItem *current, QListWidgetItem *)
224 {
225     if (!current) {
226         m_ui->lblName->clear();
227         m_ui->lblAuthor->clear();
228         m_ui->lblEmail->clear();
229         m_ui->lblLicense->clear();
230         m_ui->lblWebsite->clear();
231         m_ui->lblDescription->clear();
232         m_ui->lblCreated->clear();
233         m_ui->lblUpdated->clear();
234         m_ui->lblPreview->setPixmap(QPixmap::fromImage(QImage()));
235         m_ui->listBundleContents->clear();
236         m_ui->bnEditBundle->setEnabled(false);
237         m_currentBundle = 0;
238     }
239     else {
240 
241         QByteArray ba = current->data(Qt::UserRole).toByteArray();
242         KoResourceServer<KisResourceBundle> *bundleServer = KisResourceBundleServerProvider::instance()->resourceBundleServer();
243         KisResourceBundle *bundle = bundleServer->resourceByMD5(ba);
244 
245         if (!bundle) {
246             // Get it from the blacklisted bundles
247             Q_FOREACH (KisResourceBundle *b2, m_blacklistedBundles.values()) {
248                 if (b2->md5() == ba) {
249                     bundle = b2;
250                     break;
251                 }
252             }
253         }
254 
255 
256         if (bundle) {
257             QFontMetrics metrics(this->font());
258 
259             m_currentBundle = bundle;
260             m_ui->bnEditBundle->setEnabled(true);
261 
262             m_ui->lblName->setText(bundle->name());
263             m_ui->lblAuthor->setText(metrics.elidedText(bundle->getMeta("author"), Qt::ElideRight, m_ui->lblAuthor->width()));
264             m_ui->lblAuthor->setToolTip(bundle->getMeta("author"));
265             m_ui->lblEmail->setText(metrics.elidedText(bundle->getMeta("email"), Qt::ElideRight, m_ui->lblEmail->width()));
266             m_ui->lblEmail->setToolTip(bundle->getMeta("email"));
267             m_ui->lblLicense->setText(metrics.elidedText(bundle->getMeta("license"), Qt::ElideRight, m_ui->lblLicense->width()));
268             m_ui->lblLicense->setToolTip(bundle->getMeta("license"));
269             m_ui->lblWebsite->setText(metrics.elidedText(bundle->getMeta("website"), Qt::ElideRight, m_ui->lblWebsite->width()));
270             m_ui->lblWebsite->setToolTip(bundle->getMeta("website"));
271             m_ui->lblDescription->setPlainText(bundle->getMeta("description"));
272             if (QDateTime::fromString(bundle->getMeta("created"), Qt::ISODate).isValid()) {
273                 m_ui->lblCreated->setText(QDateTime::fromString(bundle->getMeta("created"), Qt::ISODate).toLocalTime().toString(Qt::DefaultLocaleShortDate));
274             } else {
275                 m_ui->lblCreated->setText(QDate::fromString(bundle->getMeta("created"), "dd/MM/yyyy").toString(Qt::DefaultLocaleShortDate));
276             }
277             if (QDateTime::fromString(bundle->getMeta("updated"), Qt::ISODate).isValid()) {
278                 m_ui->lblUpdated->setText(QDateTime::fromString(bundle->getMeta("updated"), Qt::ISODate).toLocalTime().toString(Qt::DefaultLocaleShortDate));
279             } else {
280                 m_ui->lblUpdated->setText(QDate::fromString(bundle->getMeta("updated"), "dd/MM/yyyy").toString(Qt::DefaultLocaleShortDate));
281             }
282             QSize iconSize = QSize(128, 128);
283             QImage icon = bundle->image().scaled(iconSize*devicePixelRatioF(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
284             icon.setDevicePixelRatio(devicePixelRatioF());
285             m_ui->lblPreview->setPixmap(QPixmap::fromImage(icon));
286             m_ui->listBundleContents->clear();
287 
288             Q_FOREACH (const QString & resType, bundle->resourceTypes()) {
289 
290                 QTreeWidgetItem *toplevel = new QTreeWidgetItem();
291                 if (resType == "gradients") {
292                     toplevel->setText(0, i18n("Gradients"));
293                 }
294                 else if (resType  == "patterns") {
295                     toplevel->setText(0, i18n("Patterns"));
296                 }
297                 else if (resType  == "brushes") {
298                     toplevel->setText(0, i18n("Brushes"));
299                 }
300                 else if (resType  == "palettes") {
301                     toplevel->setText(0, i18n("Palettes"));
302                 }
303                 else if (resType  == "workspaces") {
304                     toplevel->setText(0, i18n("Workspaces"));
305                 }
306                 else if (resType  == "paintoppresets") {
307                     toplevel->setText(0, i18n("Brush Presets"));
308                 }
309                 else if (resType  == "gamutmasks") {
310                     toplevel->setText(0, i18n("Gamut Masks"));
311                 }
312 #if defined HAVE_SEEXPR
313                 else if (resType  == "seexpr_scripts") {
314                     toplevel->setText(0, i18n("SeExpr Scripts"));
315                 }
316 #endif
317 
318 
319                 m_ui->listBundleContents->addTopLevelItem(toplevel);
320 
321                 Q_FOREACH (const KoResource *res, bundle->resources(resType)) {
322                     if (res) {
323                         QTreeWidgetItem *i = new QTreeWidgetItem();
324                         i->setIcon(0, QIcon(QPixmap::fromImage(res->image())));
325                         i->setText(0, res->name());
326                         toplevel->addChild(i);
327                     }
328                 }
329             }
330         }
331         else {
332             m_currentBundle = 0;
333         }
334     }
335 }
336 
itemSelected(QListWidgetItem * current)337 void DlgBundleManager::itemSelected(QListWidgetItem *current)
338 {
339     itemSelected(current, 0);
340 }
341 
editBundle()342 void DlgBundleManager::editBundle()
343 {
344     if (m_currentBundle) {
345         DlgCreateBundle dlg(m_currentBundle);
346         m_activeBundles.remove(m_currentBundle->filename());
347         m_currentBundle = 0;
348         if (dlg.exec() != QDialog::Accepted) {
349             return;
350         }
351         m_currentBundle = m_resourceManager->saveBundle(dlg);
352         refreshListData();
353     }
354 }
355 
fillListWidget(QList<KisResourceBundle * > bundles,QListWidget * w)356 void DlgBundleManager::fillListWidget(QList<KisResourceBundle *> bundles, QListWidget *w)
357 {
358 	QSize iconSize = QSize(ICON_SIZE, ICON_SIZE);
359     w->setIconSize(iconSize);
360     w->setSelectionMode(QAbstractItemView::MultiSelection);
361 
362     Q_FOREACH (KisResourceBundle *bundle, bundles) {
363         QPixmap pixmap(iconSize*devicePixelRatioF());
364         pixmap.setDevicePixelRatio(devicePixelRatioF());
365         pixmap.fill(Qt::gray);
366         if (!bundle->image().isNull()) {
367             QImage scaled = bundle->image().scaled(iconSize*devicePixelRatioF(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
368             scaled.setDevicePixelRatio(devicePixelRatioF());
369             int x = (ICON_SIZE - scaled.width()/devicePixelRatioF()) / 2;
370             int y = (ICON_SIZE - scaled.height()/devicePixelRatioF()) / 2;
371             QPainter gc(&pixmap);
372             gc.drawImage(x, y, scaled);
373             gc.end();
374         }
375 
376         QListWidgetItem *item = new QListWidgetItem(pixmap, bundle->name());
377         item->setData(Qt::UserRole, bundle->md5());
378         w->addItem(item);
379     }
380 }
381 
382 
slotImportResource()383 void DlgBundleManager::slotImportResource()
384 {
385     if (m_actionManager) {
386         QObject *button = sender();
387         QString buttonName = button->objectName();
388         KisAction *action = 0;
389         if (buttonName == "bnImportBundles") {
390             action = m_actionManager->actionByName("import_bundles");
391         }
392         else if (buttonName == "bnImportBrushes") {
393             action = m_actionManager->actionByName("import_brushes");
394         }
395         else if (buttonName == "bnImportGradients") {
396             action = m_actionManager->actionByName("import_gradients");
397         }
398         else if (buttonName == "bnImportPalettes") {
399             action = m_actionManager->actionByName("import_palettes");
400         }
401         else if (buttonName == "bnImportPatterns") {
402             action = m_actionManager->actionByName("import_patterns");
403         }
404         else if (buttonName == "bnImportPresets") {
405             action = m_actionManager->actionByName("import_presets");
406         }
407         else if (buttonName == "bnImportWorkspaces") {
408             action = m_actionManager->actionByName("import_workspaces");
409         }
410         else {
411             warnUI << "Unhandled bundle manager import button " << buttonName;
412             return;
413         }
414 
415         action->trigger();
416         refreshListData();
417     }
418 }
419 
slotCreateBundle()420 void DlgBundleManager::slotCreateBundle() {
421 
422     if (m_actionManager) {
423         KisAction *action = m_actionManager->actionByName("create_bundle");
424         action->trigger();
425         refreshListData();
426     }
427 }
428 
slotDeleteBackupFiles()429 void DlgBundleManager::slotDeleteBackupFiles() {
430 
431     if (m_actionManager) {
432         KisAction *action = m_actionManager->actionByName("edit_blacklist_cleanup");
433         action->trigger();
434     }
435 }
436 
slotOpenResourceFolder()437 void DlgBundleManager::slotOpenResourceFolder() {
438 
439     if (m_actionManager) {
440         KisAction *action = m_actionManager->actionByName("open_resources_directory");
441         action->trigger();
442     }
443 }
444 
445