]> cloud.milkyroute.net Git - dolphin.git/blob - src/statusbar/diskspaceusagemenu.cpp
Clazy fix
[dolphin.git] / src / statusbar / diskspaceusagemenu.cpp
1 /*
2 SPDX-FileCopyrightText: 2025 Felix Ernst <felixernst@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6
7 #include "diskspaceusagemenu.h"
8
9 #include "dolphinpackageinstaller.h"
10 #include "global.h"
11
12 #include <KIO/ApplicationLauncherJob>
13 #include <KLocalizedString>
14 #include <KService>
15
16 #include <QDesktopServices>
17 #include <QHBoxLayout>
18 #include <QLabel>
19 #include <QPushButton>
20 #include <QShowEvent>
21 #include <QStorageInfo>
22 #include <QWidgetAction>
23
24 DiskSpaceUsageMenu::DiskSpaceUsageMenu(QWidget *parent)
25 : QMenu{parent}
26 {
27 connect(this, &QMenu::aboutToShow, this, &DiskSpaceUsageMenu::updateMenu);
28 }
29
30 void DiskSpaceUsageMenu::slotInstallFilelightButtonClicked()
31 {
32 #ifdef Q_OS_WIN
33 QDesktopServices::openUrl(QUrl("https://apps.kde.org/filelight"));
34 #else
35 auto packageInstaller = new DolphinPackageInstaller(
36 FILELIGHT_PACKAGE_NAME,
37 QUrl("appstream://org.kde.filelight.desktop"),
38 []() {
39 return KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"));
40 },
41 this);
42 connect(packageInstaller, &KJob::result, this, [this](KJob *job) {
43 Q_EMIT showInstallationProgress(QString(), 100); // Hides the progress information in the status bar.
44 if (job->error()) {
45 Q_EMIT showMessage(job->errorString(), KMessageWidget::Error);
46 } else {
47 Q_EMIT showMessage(xi18nc("@info", "<application>Filelight</application> installed successfully."), KMessageWidget::Positive);
48 if (isVisible()) {
49 hide();
50 updateMenu();
51 show();
52 }
53 }
54 });
55 const auto installationTaskText{i18nc("@info:status", "Installing Filelight…")};
56 Q_EMIT showInstallationProgress(installationTaskText, -1);
57 connect(packageInstaller, &KJob::percentChanged, this, [this, installationTaskText](KJob * /* job */, long unsigned int percent) {
58 if (percent < 100) { // Ignore some weird reported values.
59 Q_EMIT showInstallationProgress(installationTaskText, percent);
60 }
61 });
62 packageInstaller->start();
63 #endif
64 }
65
66 void DiskSpaceUsageMenu::updateMenu()
67 {
68 clear();
69
70 // Creates a menu with tools that help to find out more about free
71 // disk space for the given url.
72
73 const KService::Ptr filelight = KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"));
74 const KService::Ptr kdiskfree = KService::serviceByDesktopName(QStringLiteral("org.kde.kdf"));
75
76 if (!filelight && !kdiskfree) {
77 // Show an UI to install a tool to free up disk space because this is what a user pressing on a "free space" button would want.
78 if (!m_installFilelightWidgetAction) {
79 initialiseInstallFilelightWidgetAction();
80 }
81 addAction(m_installFilelightWidgetAction);
82 return;
83 }
84
85 if (filelight) {
86 QAction *filelightFolderAction = addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - current folder"));
87
88 connect(filelightFolderAction, &QAction::triggered, this, [this, filelight](bool) {
89 auto *job = new KIO::ApplicationLauncherJob(filelight);
90 job->setUrls({m_url});
91 job->start();
92 });
93
94 // For remote URLs like FTP analyzing the device makes no sense
95 if (m_url.isLocalFile()) {
96 QAction *filelightDiskAction = addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - current device"));
97
98 connect(filelightDiskAction, &QAction::triggered, this, [this, filelight](bool) {
99 const QStorageInfo info(m_url.toLocalFile());
100
101 if (info.isValid() && info.isReady()) {
102 auto *job = new KIO::ApplicationLauncherJob(filelight);
103 job->setUrls({QUrl::fromLocalFile(info.rootPath())});
104 job->start();
105 }
106 });
107 }
108
109 QAction *filelightAllAction = addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - all devices"));
110
111 connect(filelightAllAction, &QAction::triggered, this, [this, filelight](bool) {
112 const QStorageInfo info(m_url.toLocalFile());
113
114 if (info.isValid() && info.isReady()) {
115 auto *job = new KIO::ApplicationLauncherJob(filelight);
116 job->start();
117 }
118 });
119 }
120
121 if (kdiskfree) {
122 QAction *kdiskfreeAction = addAction(QIcon::fromTheme(QStringLiteral("kdf")), i18n("KDiskFree"));
123
124 connect(kdiskfreeAction, &QAction::triggered, this, [kdiskfree] {
125 auto *job = new KIO::ApplicationLauncherJob(kdiskfree);
126 job->start();
127 });
128 }
129 }
130
131 void DiskSpaceUsageMenu::showEvent(QShowEvent *event)
132 {
133 if (!event->spontaneous() && !actions().isEmpty()) {
134 auto widgetAction = qobject_cast<QWidgetAction *>(*actions().constBegin());
135 if (widgetAction) {
136 widgetAction->defaultWidget()->setFocus();
137 }
138 }
139 QMenu::showEvent(event);
140 }
141
142 void DiskSpaceUsageMenu::initialiseInstallFilelightWidgetAction()
143 {
144 Q_ASSERT(!m_installFilelightWidgetAction);
145
146 auto containerWidget = new QWidget{this};
147 containerWidget->setContentsMargins(Dolphin::VERTICAL_SPACER_HEIGHT,
148 Dolphin::VERTICAL_SPACER_HEIGHT,
149 Dolphin::VERTICAL_SPACER_HEIGHT, // Using the same value for every spacing in this containerWidget looks nice.
150 Dolphin::VERTICAL_SPACER_HEIGHT);
151 auto vLayout = new QVBoxLayout(containerWidget);
152
153 auto installFilelightTitle = new QLabel(i18nc("@title", "Free Up Disk Space"), containerWidget);
154 installFilelightTitle->setAlignment(Qt::AlignCenter);
155 installFilelightTitle->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard);
156 QFont titleFont{installFilelightTitle->font()};
157 titleFont.setPointSize(titleFont.pointSize() + 2);
158 installFilelightTitle->setFont(titleFont);
159 vLayout->addWidget(installFilelightTitle);
160
161 vLayout->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT);
162
163 auto installFilelightBody =
164 // i18n: The new line ("<nl/>") tag is only there to format this text visually pleasing, i.e. to avoid having one very long line.
165 new QLabel(xi18nc("@title", "<para>Install additional software to view disk usage statistics<nl/>and identify big files and folders.</para>"),
166 containerWidget);
167 installFilelightBody->setAlignment(Qt::AlignCenter);
168 installFilelightBody->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard);
169 vLayout->addWidget(installFilelightBody);
170
171 vLayout->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT);
172
173 auto installFilelightButton =
174 new QPushButton(QIcon::fromTheme(QStringLiteral("filelight")), i18nc("@action:button", "Install Filelight…"), containerWidget);
175 installFilelightButton->setMinimumWidth(std::max(installFilelightButton->sizeHint().width(), installFilelightTitle->sizeHint().width()));
176 auto buttonLayout = new QHBoxLayout; // The parent is automatically set on addLayout() below.
177 buttonLayout->addWidget(installFilelightButton, 0, Qt::AlignHCenter);
178 vLayout->addLayout(buttonLayout);
179
180 // Make sure one Tab press focuses the button after the UI opened.
181 setFocusProxy(installFilelightButton);
182 containerWidget->setFocusPolicy(Qt::TabFocus);
183 containerWidget->setFocusProxy(installFilelightButton);
184 installFilelightButton->setAccessibleDescription(installFilelightBody->text());
185 connect(installFilelightButton, &QAbstractButton::clicked, this, &DiskSpaceUsageMenu::slotInstallFilelightButtonClicked);
186
187 m_installFilelightWidgetAction = new QWidgetAction{this};
188 m_installFilelightWidgetAction->setDefaultWidget(containerWidget); // transfers ownership of containerWidget
189 }