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}
35 , m_hasSpaceInfo
{false}
38 hide(); // Only become visible when we have space info to show. @see StatusBarSpaceInfo::setShown().
40 m_capacityBar
= new KCapacityBar(KCapacityBar::DrawTextInline
, this);
41 m_textInfoButton
= new QToolButton(this);
42 m_textInfoButton
->setAutoRaise(true);
43 m_textInfoButton
->setPopupMode(QToolButton::InstantPopup
);
44 m_buttonMenu
= new QMenu(this);
45 m_textInfoButton
->setMenu(m_buttonMenu
);
46 connect(m_buttonMenu
, &QMenu::aboutToShow
, this, &StatusBarSpaceInfo::updateMenu
);
48 auto layout
= new QHBoxLayout(this);
49 // 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.
50 layout
->setContentsMargins(2, -1, 0, -1); // "-1" makes it so the fixed height won't be ignored.
51 layout
->addWidget(m_capacityBar
);
52 layout
->addWidget(m_textInfoButton
);
55 StatusBarSpaceInfo::~StatusBarSpaceInfo()
59 void StatusBarSpaceInfo::setShown(bool shown
)
67 // We only show() this widget in slotValueChanged() when it m_hasSpaceInfo.
68 if (m_observer
.isNull()) {
69 m_observer
.reset(new SpaceInfoObserver(m_url
, this));
70 connect(m_observer
.data(), &SpaceInfoObserver::valuesChanged
, this, &StatusBarSpaceInfo::slotValuesChanged
);
78 void StatusBarSpaceInfo::setUrl(const QUrl
&url
)
82 m_hasSpaceInfo
= false;
84 m_observer
.reset(new SpaceInfoObserver(m_url
, this));
85 connect(m_observer
.data(), &SpaceInfoObserver::valuesChanged
, this, &StatusBarSpaceInfo::slotValuesChanged
);
90 QUrl
StatusBarSpaceInfo::url() const
95 void StatusBarSpaceInfo::update()
102 void StatusBarSpaceInfo::showEvent(QShowEvent
*event
)
104 if (m_shown
&& m_observer
.isNull()) {
105 m_observer
.reset(new SpaceInfoObserver(m_url
, this));
106 connect(m_observer
.data(), &SpaceInfoObserver::valuesChanged
, this, &StatusBarSpaceInfo::slotValuesChanged
);
108 QWidget::showEvent(event
);
111 void StatusBarSpaceInfo::hideEvent(QHideEvent
*event
)
113 if (m_hasSpaceInfo
) {
115 m_hasSpaceInfo
= false;
117 QWidget::hideEvent(event
);
120 QSize
StatusBarSpaceInfo::minimumSizeHint() const
125 void StatusBarSpaceInfo::updateMenu()
127 m_buttonMenu
->clear();
129 // Creates a menu with tools that help to find out more about free
130 // disk space for the given url.
132 const KService::Ptr filelight
= KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"));
133 const KService::Ptr kdiskfree
= KService::serviceByDesktopName(QStringLiteral("org.kde.kdf"));
135 if (!filelight
&& !kdiskfree
) {
136 // 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.
137 if (!m_installFilelightWidgetAction
) {
138 initialiseInstallFilelightWidgetAction();
140 m_buttonMenu
->addAction(m_installFilelightWidgetAction
);
145 QAction
*filelightFolderAction
= m_buttonMenu
->addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - current folder"));
147 m_buttonMenu
->connect(filelightFolderAction
, &QAction::triggered
, m_buttonMenu
, [this, filelight
](bool) {
148 auto *job
= new KIO::ApplicationLauncherJob(filelight
);
149 job
->setUrls({m_url
});
153 // For remote URLs like FTP analyzing the device makes no sense
154 if (m_url
.isLocalFile()) {
155 QAction
*filelightDiskAction
=
156 m_buttonMenu
->addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - current device"));
158 m_buttonMenu
->connect(filelightDiskAction
, &QAction::triggered
, m_buttonMenu
, [this, filelight
](bool) {
159 const QStorageInfo
info(m_url
.toLocalFile());
161 if (info
.isValid() && info
.isReady()) {
162 auto *job
= new KIO::ApplicationLauncherJob(filelight
);
163 job
->setUrls({QUrl::fromLocalFile(info
.rootPath())});
169 QAction
*filelightAllAction
= m_buttonMenu
->addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - all devices"));
171 m_buttonMenu
->connect(filelightAllAction
, &QAction::triggered
, m_buttonMenu
, [this, filelight
](bool) {
172 const QStorageInfo
info(m_url
.toLocalFile());
174 if (info
.isValid() && info
.isReady()) {
175 auto *job
= new KIO::ApplicationLauncherJob(filelight
);
182 QAction
*kdiskfreeAction
= m_buttonMenu
->addAction(QIcon::fromTheme(QStringLiteral("kdf")), i18n("KDiskFree"));
184 connect(kdiskfreeAction
, &QAction::triggered
, this, [kdiskfree
] {
185 auto *job
= new KIO::ApplicationLauncherJob(kdiskfree
);
191 void StatusBarSpaceInfo::slotInstallFilelightButtonClicked()
194 QDesktopServices::openUrl(QUrl("https://apps.kde.org/filelight"));
196 auto packageInstaller
= new DolphinPackageInstaller(
197 FILELIGHT_PACKAGE_NAME
,
198 QUrl("appstream://org.kde.filelight.desktop"),
200 return KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"));
203 connect(packageInstaller
, &KJob::result
, this, [this](KJob
*job
) {
204 Q_EMIT
showInstallationProgress(QString(), 100); // Hides the progress information in the status bar.
206 Q_EMIT
showMessage(job
->errorString(), KMessageWidget::Error
);
208 Q_EMIT
showMessage(xi18nc("@info", "<application>Filelight</application> installed successfully."), KMessageWidget::Positive
);
209 if (m_textInfoButton
->menu()->isVisible()) {
210 m_textInfoButton
->menu()->hide();
212 m_textInfoButton
->menu()->show();
216 const auto installationTaskText
{i18nc("@info:status", "Installing Filelight…")};
217 Q_EMIT
showInstallationProgress(installationTaskText
, -1);
218 connect(packageInstaller
, &KJob::percentChanged
, this, [this, installationTaskText
](KJob
* /* job */, long unsigned int percent
) {
219 if (percent
< 100) { // Ignore some weird reported values.
220 Q_EMIT
showInstallationProgress(installationTaskText
, percent
);
223 packageInstaller
->start();
227 void StatusBarSpaceInfo::slotValuesChanged()
229 Q_ASSERT(m_observer
);
230 const quint64 size
= m_observer
->size();
232 if (!m_shown
|| size
== 0) {
237 m_hasSpaceInfo
= true;
239 const quint64 available
= m_observer
->available();
240 const quint64 used
= size
- available
;
241 const int percentUsed
= qRound(100.0 * qreal(used
) / qreal(size
));
243 m_textInfoButton
->setText(i18nc("@info:status Free disk space", "%1 free", KIO::convertSize(available
)));
244 setToolTip(i18nc("tooltip:status Free disk space", "%1 free out of %2 (%3% used)", KIO::convertSize(available
), KIO::convertSize(size
), percentUsed
));
245 m_textInfoButton
->setToolTip(i18nc("@info:tooltip for the free disk space button",
246 "%1 free out of %2 (%3% used)\nPress to manage disk space usage.",
247 KIO::convertSize(available
),
248 KIO::convertSize(size
),
250 setUpdatesEnabled(false);
251 m_capacityBar
->setValue(percentUsed
);
252 setUpdatesEnabled(true);
261 void StatusBarSpaceInfo::initialiseInstallFilelightWidgetAction()
263 Q_ASSERT(!m_installFilelightWidgetAction
);
265 auto containerWidget
= new QWidget
{this};
266 containerWidget
->setContentsMargins(Dolphin::VERTICAL_SPACER_HEIGHT
,
267 Dolphin::VERTICAL_SPACER_HEIGHT
,
268 Dolphin::VERTICAL_SPACER_HEIGHT
, // Using the same value for every spacing in this containerWidget looks nice.
269 Dolphin::VERTICAL_SPACER_HEIGHT
);
270 auto vLayout
= new QVBoxLayout(containerWidget
);
272 auto installFilelightTitle
= new QLabel(i18nc("@title", "Free Up Disk Space"), containerWidget
);
273 installFilelightTitle
->setAlignment(Qt::AlignCenter
);
274 installFilelightTitle
->setTextInteractionFlags(Qt::TextSelectableByMouse
| Qt::TextSelectableByKeyboard
| Qt::LinksAccessibleByKeyboard
);
275 QFont titleFont
{installFilelightTitle
->font()};
276 titleFont
.setPointSize(titleFont
.pointSize() + 2);
277 installFilelightTitle
->setFont(titleFont
);
278 vLayout
->addWidget(installFilelightTitle
);
280 vLayout
->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT
);
282 auto installFilelightBody
=
283 // i18n: The new line ("<nl/>") tag is only there to format this text visually pleasing, i.e. to avoid having one very long line.
284 new QLabel(xi18nc("@title", "<para>Install additional software to view disk usage statistics<nl/>and identify big files and folders.</para>"),
286 installFilelightBody
->setAlignment(Qt::AlignCenter
);
287 installFilelightBody
->setTextInteractionFlags(Qt::TextSelectableByMouse
| Qt::TextSelectableByKeyboard
| Qt::LinksAccessibleByKeyboard
);
288 vLayout
->addWidget(installFilelightBody
);
290 vLayout
->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT
);
292 auto installFilelightButton
=
293 new QPushButton(QIcon::fromTheme(QStringLiteral("filelight")), i18nc("@action:button", "Install Filelight…"), containerWidget
);
294 installFilelightButton
->setMinimumWidth(std::max(installFilelightButton
->sizeHint().width(), installFilelightTitle
->sizeHint().width()));
295 auto buttonLayout
= new QHBoxLayout
; // The parent is automatically set on addLayout() below.
296 buttonLayout
->addWidget(installFilelightButton
, 0, Qt::AlignHCenter
);
297 vLayout
->addLayout(buttonLayout
);
299 // Make sure one Tab press focuses the button after the UI opened.
300 m_buttonMenu
->setFocusProxy(installFilelightButton
);
301 containerWidget
->setFocusPolicy(Qt::TabFocus
);
302 containerWidget
->setFocusProxy(installFilelightButton
);
303 installFilelightButton
->setAccessibleDescription(installFilelightBody
->text());
304 connect(installFilelightButton
, &QAbstractButton::clicked
, this, &StatusBarSpaceInfo::slotInstallFilelightButtonClicked
);
306 m_installFilelightWidgetAction
= new QWidgetAction
{this};
307 m_installFilelightWidgetAction
->setDefaultWidget(containerWidget
); // transfers ownership of containerWidget
310 #include "moc_statusbarspaceinfo.cpp"