1 /*
2     SPDX-FileCopyrightText: 2016 Elvis Angelaccio <elvis.angelaccio@kde.org>
3     SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
4 
5     SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #include "compressfileitemaction.h"
9 
10 #include <QMenu>
11 #include <QMimeDatabase>
12 
13 #include <KFileItem>
14 #include <KIO/ApplicationLauncherJob>
15 #include <KLocalizedString>
16 #include <KPluginFactory>
17 
18 #include <algorithm>
19 
20 #include "pluginmanager.h"
21 #include "addtoarchive.h"
22 
23 K_PLUGIN_CLASS_WITH_JSON(CompressFileItemAction, "compressfileitemaction.json")
24 
25 using namespace Kerfuffle;
26 
CompressFileItemAction(QObject * parent,const QVariantList &)27 CompressFileItemAction::CompressFileItemAction(QObject* parent, const QVariantList&)
28     : KAbstractFileItemActionPlugin(parent)
29     , m_pluginManager(new PluginManager(this))
30 {}
31 
actions(const KFileItemListProperties & fileItemInfos,QWidget * parentWidget)32 QList<QAction*> CompressFileItemAction::actions(const KFileItemListProperties& fileItemInfos, QWidget* parentWidget)
33 {
34     // #268163: don't offer compression on already compressed archives, unless the user selected 2 or more of them.
35     if (fileItemInfos.items().count() == 1 && m_pluginManager->supportedMimeTypes().contains(fileItemInfos.mimeType())) {
36         return {};
37     }
38 
39     // KFileItemListProperties::isLocal() doesn't check target URL (e.g. files on the desktop)
40     const auto urlList = fileItemInfos.urlList();
41     const bool hasLocalUrl = std::any_of(urlList.begin(), urlList.end(), [](const QUrl &url) {
42         return url.isLocalFile();
43     });
44 
45     if (!hasLocalUrl) {
46         return {};
47     }
48 
49     QList<QAction*> actions;
50     const QIcon icon = QIcon::fromTheme(QStringLiteral("archive-insert"));
51 
52     QMenu *compressMenu = new QMenu(parentWidget);
53 
54     compressMenu->addAction(createAction(icon,
55                                          i18nc("@action:inmenu Part of Compress submenu in Dolphin context menu", "Here (as TAR.GZ)"),
56                                          parentWidget,
57                                          urlList,
58                                          QStringLiteral("tar.gz")));
59 
60     const QMimeType zipMime = QMimeDatabase().mimeTypeForName(QStringLiteral("application/zip"));
61     // Don't offer zip compression if no zip plugin is available.
62     if (!m_pluginManager->preferredWritePluginsFor(zipMime).isEmpty()) {
63         compressMenu->addAction(createAction(icon,
64                                              i18nc("@action:inmenu Part of Compress submenu in Dolphin context menu", "Here (as ZIP)"),
65                                              parentWidget,
66                                              urlList,
67                                              QStringLiteral("zip")));
68     }
69 
70     compressMenu->addAction(createAction(icon,
71                                          i18nc("@action:inmenu Part of Compress submenu in Dolphin context menu", "Compress to..."),
72                                          parentWidget,
73                                          urlList,
74                                          QString()));
75 
76     QAction *compressMenuAction = new QAction(i18nc("@action:inmenu Compress submenu in Dolphin context menu", "Compress"), parentWidget);
77     compressMenuAction->setMenu(compressMenu);
78     compressMenuAction->setEnabled(fileItemInfos.isLocal() && fileItemInfos.supportsWriting() && !m_pluginManager->availableWritePlugins().isEmpty());
79     compressMenuAction->setIcon(icon);
80 
81     actions << compressMenuAction;
82     return actions;
83 }
84 
createAction(const QIcon & icon,const QString & name,QWidget * parent,const QList<QUrl> & urls,const QString & fileExtension)85 QAction *CompressFileItemAction::createAction(const QIcon& icon, const QString& name, QWidget *parent, const QList<QUrl>& urls, const QString& fileExtension)
86 {
87     QAction *action = new QAction(icon, name, parent);
88 
89     connect(action, &QAction::triggered, this, [fileExtension, urls, name, parent, this]() {
90         auto *addToArchiveJob = new AddToArchive(parent);
91         addToArchiveJob->setChangeToFirstPath(true);
92         for (const QUrl &url : urls) {
93             addToArchiveJob->addInput(url);
94         }
95         if (!fileExtension.isEmpty()) {
96             addToArchiveJob->setAutoFilenameSuffix(fileExtension);
97         } else {
98             if (!addToArchiveJob->showAddDialog()) {
99                 delete addToArchiveJob;
100                 return;
101             }
102         }
103         addToArchiveJob->start();
104         connect(addToArchiveJob, &KJob::finished, this, [this, addToArchiveJob]() {
105             if (addToArchiveJob->error() != 0) {
106                 Q_EMIT error(addToArchiveJob->errorString());
107             }
108         });
109     });
110 
111     return action;
112 }
113 
114 #include "compressfileitemaction.moc"
115