2 * SPDX-FileCopyrightText: 2006 Peter Penz (peter.penz@gmx.at) and Patrice Tremblay
4 * SPDX-License-Identifier: GPL-2.0-or-later
7 #include "statusbarspaceinfo.h"
9 #include "config-dolphin.h"
10 #include "dolphinpackageinstaller.h"
12 #include "spaceinfoobserver.h"
14 #include <KCapacityBar>
15 #include <KIO/ApplicationLauncherJob>
17 #include <KLocalizedString>
20 #include <QDesktopServices>
21 #include <QHBoxLayout>
24 #include <QMouseEvent>
25 #include <QPushButton>
26 #include <QStorageInfo>
27 #include <QToolButton>
28 #include <QVBoxLayout>
29 #include <QWidgetAction>
31 StatusBarSpaceInfo::StatusBarSpaceInfo(QWidget
*parent
)
34 , m_installFilelightWidgetAction
{nullptr}
36 m_capacityBar
= new KCapacityBar(KCapacityBar::DrawTextInline
, this);
37 m_textInfoButton
= new QToolButton(this);
38 m_textInfoButton
->setAutoRaise(true);
39 m_textInfoButton
->setPopupMode(QToolButton::InstantPopup
);
40 m_buttonMenu
= new QMenu(this);
41 m_textInfoButton
->setMenu(m_buttonMenu
);
42 connect(m_buttonMenu
, &QMenu::aboutToShow
, this, &StatusBarSpaceInfo::updateMenu
);
44 auto layout
= new QHBoxLayout(this);
45 // We reduce the outside margin of the flat button so it visually has the same margin as the status bar text label on the other end of the bar.
46 layout
->setContentsMargins(2, -1, 0, -1); // "-1" makes it so the fixed height won't be ignored.
47 layout
->addWidget(m_capacityBar
);
48 layout
->addWidget(m_textInfoButton
);
51 StatusBarSpaceInfo::~StatusBarSpaceInfo()
55 void StatusBarSpaceInfo::setShown(bool shown
)
64 void StatusBarSpaceInfo::setUrl(const QUrl
&url
)
70 m_observer
.reset(new SpaceInfoObserver(m_url
, this));
71 connect(m_observer
.data(), &SpaceInfoObserver::valuesChanged
, this, &StatusBarSpaceInfo::slotValuesChanged
);
76 QUrl
StatusBarSpaceInfo::url() const
81 void StatusBarSpaceInfo::update()
88 void StatusBarSpaceInfo::showEvent(QShowEvent
*event
)
92 QWidget::showEvent(event
);
95 if (m_observer
.isNull()) {
96 m_observer
.reset(new SpaceInfoObserver(m_url
, this));
97 connect(m_observer
.data(), &SpaceInfoObserver::valuesChanged
, this, &StatusBarSpaceInfo::slotValuesChanged
);
102 void StatusBarSpaceInfo::hideEvent(QHideEvent
*event
)
108 QWidget::hideEvent(event
);
111 QSize
StatusBarSpaceInfo::minimumSizeHint() const
116 void StatusBarSpaceInfo::updateMenu()
118 m_buttonMenu
->clear();
120 // Creates a menu with tools that help to find out more about free
121 // disk space for the given url.
123 const KService::Ptr filelight
= KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"));
124 const KService::Ptr kdiskfree
= KService::serviceByDesktopName(QStringLiteral("org.kde.kdf"));
126 if (!filelight
&& !kdiskfree
) {
127 // 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.
128 if (!m_installFilelightWidgetAction
) {
129 initialiseInstallFilelightWidgetAction();
131 m_buttonMenu
->addAction(m_installFilelightWidgetAction
);
136 QAction
*filelightFolderAction
= m_buttonMenu
->addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - current folder"));
138 m_buttonMenu
->connect(filelightFolderAction
, &QAction::triggered
, m_buttonMenu
, [this, filelight
](bool) {
139 auto *job
= new KIO::ApplicationLauncherJob(filelight
);
140 job
->setUrls({m_url
});
144 // For remote URLs like FTP analyzing the device makes no sense
145 if (m_url
.isLocalFile()) {
146 QAction
*filelightDiskAction
=
147 m_buttonMenu
->addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - current device"));
149 m_buttonMenu
->connect(filelightDiskAction
, &QAction::triggered
, m_buttonMenu
, [this, filelight
](bool) {
150 const QStorageInfo
info(m_url
.toLocalFile());
152 if (info
.isValid() && info
.isReady()) {
153 auto *job
= new KIO::ApplicationLauncherJob(filelight
);
154 job
->setUrls({QUrl::fromLocalFile(info
.rootPath())});
160 QAction
*filelightAllAction
= m_buttonMenu
->addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - all devices"));
162 m_buttonMenu
->connect(filelightAllAction
, &QAction::triggered
, m_buttonMenu
, [this, filelight
](bool) {
163 const QStorageInfo
info(m_url
.toLocalFile());
165 if (info
.isValid() && info
.isReady()) {
166 auto *job
= new KIO::ApplicationLauncherJob(filelight
);
173 QAction
*kdiskfreeAction
= m_buttonMenu
->addAction(QIcon::fromTheme(QStringLiteral("kdf")), i18n("KDiskFree"));
175 connect(kdiskfreeAction
, &QAction::triggered
, this, [kdiskfree
] {
176 auto *job
= new KIO::ApplicationLauncherJob(kdiskfree
);
182 void StatusBarSpaceInfo::slotInstallFilelightButtonClicked()
185 QDesktopServices::openUrl(QUrl("https://apps.kde.org/filelight"));
187 auto packageInstaller
= new DolphinPackageInstaller(
188 FILELIGHT_PACKAGE_NAME
,
189 QUrl("appstream://org.kde.filelight.desktop"),
191 return KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"));
194 connect(packageInstaller
, &KJob::result
, this, [this](KJob
*job
) {
195 Q_EMIT
showInstallationProgress(QString(), 100); // Hides the progress information in the status bar.
197 Q_EMIT
showMessage(job
->errorString(), KMessageWidget::Error
);
199 Q_EMIT
showMessage(xi18nc("@info", "<application>Filelight</application> installed successfully."), KMessageWidget::Positive
);
200 if (m_textInfoButton
->menu()->isVisible()) {
201 m_textInfoButton
->menu()->hide();
203 m_textInfoButton
->menu()->show();
207 const auto installationTaskText
{i18nc("@info:status", "Installing Filelight…")};
208 Q_EMIT
showInstallationProgress(installationTaskText
, -1);
209 connect(packageInstaller
, &KJob::percentChanged
, this, [this, installationTaskText
](KJob
* /* job */, long unsigned int percent
) {
210 if (percent
< 100) { // Ignore some weird reported values.
211 Q_EMIT
showInstallationProgress(installationTaskText
, percent
);
214 packageInstaller
->start();
218 void StatusBarSpaceInfo::slotValuesChanged()
220 Q_ASSERT(m_observer
);
221 const quint64 size
= m_observer
->size();
223 if (!m_shown
|| size
== 0) {
230 const quint64 available
= m_observer
->available();
231 const quint64 used
= size
- available
;
232 const int percentUsed
= qRound(100.0 * qreal(used
) / qreal(size
));
234 m_textInfoButton
->setText(i18nc("@info:status Free disk space", "%1 free", KIO::convertSize(available
)));
235 setToolTip(i18nc("tooltip:status Free disk space", "%1 free out of %2 (%3% used)", KIO::convertSize(available
), KIO::convertSize(size
), percentUsed
));
236 m_textInfoButton
->setToolTip(i18nc("@info:tooltip for the free disk space button",
237 "%1 free out of %2 (%3% used)\nPress to manage disk space usage.",
238 KIO::convertSize(available
),
239 KIO::convertSize(size
),
241 setUpdatesEnabled(false);
242 m_capacityBar
->setValue(percentUsed
);
243 setUpdatesEnabled(true);
252 void StatusBarSpaceInfo::initialiseInstallFilelightWidgetAction()
254 Q_ASSERT(!m_installFilelightWidgetAction
);
256 auto containerWidget
= new QWidget
{this};
257 containerWidget
->setContentsMargins(Dolphin::VERTICAL_SPACER_HEIGHT
,
258 Dolphin::VERTICAL_SPACER_HEIGHT
,
259 Dolphin::VERTICAL_SPACER_HEIGHT
, // Using the same value for every spacing in this containerWidget looks nice.
260 Dolphin::VERTICAL_SPACER_HEIGHT
);
261 auto vLayout
= new QVBoxLayout(containerWidget
);
263 auto installFilelightTitle
= new QLabel(i18nc("@title", "Free Up Disk Space"), containerWidget
);
264 installFilelightTitle
->setAlignment(Qt::AlignCenter
);
265 installFilelightTitle
->setTextInteractionFlags(Qt::TextSelectableByMouse
| Qt::TextSelectableByKeyboard
| Qt::LinksAccessibleByKeyboard
);
266 QFont titleFont
{installFilelightTitle
->font()};
267 titleFont
.setPointSize(titleFont
.pointSize() + 2);
268 installFilelightTitle
->setFont(titleFont
);
269 vLayout
->addWidget(installFilelightTitle
);
271 vLayout
->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT
);
273 auto installFilelightBody
=
274 // i18n: The new line ("<nl/>") tag is only there to format this text visually pleasing, i.e. to avoid having one very long line.
275 new QLabel(xi18nc("@title", "<para>Install additional software to view disk usage statistics<nl/>and identify big files and folders.</para>"),
277 installFilelightBody
->setAlignment(Qt::AlignCenter
);
278 installFilelightBody
->setTextInteractionFlags(Qt::TextSelectableByMouse
| Qt::TextSelectableByKeyboard
| Qt::LinksAccessibleByKeyboard
);
279 vLayout
->addWidget(installFilelightBody
);
281 vLayout
->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT
);
283 auto installFilelightButton
=
284 new QPushButton(QIcon::fromTheme(QStringLiteral("filelight")), i18nc("@action:button", "Install Filelight…"), containerWidget
);
285 installFilelightButton
->setMinimumWidth(std::max(installFilelightButton
->sizeHint().width(), installFilelightTitle
->sizeHint().width()));
286 auto buttonLayout
= new QHBoxLayout
; // The parent is automatically set on addLayout() below.
287 buttonLayout
->addWidget(installFilelightButton
, 0, Qt::AlignHCenter
);
288 vLayout
->addLayout(buttonLayout
);
290 // Make sure one Tab press focuses the button after the UI opened.
291 m_buttonMenu
->setFocusProxy(installFilelightButton
);
292 containerWidget
->setFocusPolicy(Qt::TabFocus
);
293 containerWidget
->setFocusProxy(installFilelightButton
);
294 installFilelightButton
->setAccessibleDescription(installFilelightBody
->text());
295 connect(installFilelightButton
, &QAbstractButton::clicked
, this, &StatusBarSpaceInfo::slotInstallFilelightButtonClicked
);
297 m_installFilelightWidgetAction
= new QWidgetAction
{this};
298 m_installFilelightWidgetAction
->setDefaultWidget(containerWidget
); // transfers ownership of containerWidget
301 #include "moc_statusbarspaceinfo.cpp"