2 SPDX-FileCopyrightText: 2025 Felix Ernst <felixernst@kde.org>
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 #include "diskspaceusagemenu.h"
9 #include "dolphinpackageinstaller.h"
12 #include <KIO/ApplicationLauncherJob>
13 #include <KLocalizedString>
16 #include <QDesktopServices>
17 #include <QHBoxLayout>
19 #include <QPushButton>
21 #include <QStorageInfo>
22 #include <QWidgetAction>
24 DiskSpaceUsageMenu::DiskSpaceUsageMenu(QWidget
*parent
)
27 connect(this, &QMenu::aboutToShow
, this, &DiskSpaceUsageMenu::updateMenu
);
30 void DiskSpaceUsageMenu::slotInstallFilelightButtonClicked()
33 QDesktopServices::openUrl(QUrl("https://apps.kde.org/filelight"));
35 auto packageInstaller
= new DolphinPackageInstaller(
36 FILELIGHT_PACKAGE_NAME
,
37 QUrl("appstream://org.kde.filelight.desktop"),
39 return KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"));
42 connect(packageInstaller
, &KJob::result
, this, [this](KJob
*job
) {
43 Q_EMIT
showInstallationProgress(QString(), 100); // Hides the progress information in the status bar.
45 Q_EMIT
showMessage(job
->errorString(), KMessageWidget::Error
);
47 Q_EMIT
showMessage(xi18nc("@info", "<application>Filelight</application> installed successfully."), KMessageWidget::Positive
);
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
);
62 packageInstaller
->start();
66 void DiskSpaceUsageMenu::updateMenu()
70 // Creates a menu with tools that help to find out more about free
71 // disk space for the given url.
73 const KService::Ptr filelight
= KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"));
74 const KService::Ptr kdiskfree
= KService::serviceByDesktopName(QStringLiteral("org.kde.kdf"));
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();
81 addAction(m_installFilelightWidgetAction
);
86 QAction
*filelightFolderAction
= addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - current folder"));
88 connect(filelightFolderAction
, &QAction::triggered
, this, [this, filelight
](bool) {
89 auto *job
= new KIO::ApplicationLauncherJob(filelight
);
90 job
->setUrls({m_url
});
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"));
98 connect(filelightDiskAction
, &QAction::triggered
, this, [this, filelight
](bool) {
99 const QStorageInfo
info(m_url
.toLocalFile());
101 if (info
.isValid() && info
.isReady()) {
102 auto *job
= new KIO::ApplicationLauncherJob(filelight
);
103 job
->setUrls({QUrl::fromLocalFile(info
.rootPath())});
109 QAction
*filelightAllAction
= addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - all devices"));
111 connect(filelightAllAction
, &QAction::triggered
, this, [this, filelight
](bool) {
112 const QStorageInfo
info(m_url
.toLocalFile());
114 if (info
.isValid() && info
.isReady()) {
115 auto *job
= new KIO::ApplicationLauncherJob(filelight
);
122 QAction
*kdiskfreeAction
= addAction(QIcon::fromTheme(QStringLiteral("kdf")), i18n("KDiskFree"));
124 connect(kdiskfreeAction
, &QAction::triggered
, this, [kdiskfree
] {
125 auto *job
= new KIO::ApplicationLauncherJob(kdiskfree
);
131 void DiskSpaceUsageMenu::showEvent(QShowEvent
*event
)
133 if (!event
->spontaneous() && !actions().isEmpty()) {
134 auto widgetAction
= qobject_cast
<QWidgetAction
*>(*actions().constBegin());
136 widgetAction
->defaultWidget()->setFocus();
139 QMenu::showEvent(event
);
142 void DiskSpaceUsageMenu::initialiseInstallFilelightWidgetAction()
144 Q_ASSERT(!m_installFilelightWidgetAction
);
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
);
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
);
161 vLayout
->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT
);
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>"),
167 installFilelightBody
->setAlignment(Qt::AlignCenter
);
168 installFilelightBody
->setTextInteractionFlags(Qt::TextSelectableByMouse
| Qt::TextSelectableByKeyboard
| Qt::LinksAccessibleByKeyboard
);
169 vLayout
->addWidget(installFilelightBody
);
171 vLayout
->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT
);
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
);
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
);
187 m_installFilelightWidgetAction
= new QWidgetAction
{this};
188 m_installFilelightWidgetAction
->setDefaultWidget(containerWidget
); // transfers ownership of containerWidget