]> cloud.milkyroute.net Git - dolphin.git/blob - src/statusbar/statusbarspaceinfo.cpp
Rewrite search integration
[dolphin.git] / src / statusbar / statusbarspaceinfo.cpp
1 /*
2 * SPDX-FileCopyrightText: 2006 Peter Penz (peter.penz@gmx.at) and Patrice Tremblay
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "statusbarspaceinfo.h"
8
9 #include "config-dolphin.h"
10 #include "dolphinpackageinstaller.h"
11 #include "global.h"
12 #include "spaceinfoobserver.h"
13
14 #include <KCapacityBar>
15 #include <KIO/ApplicationLauncherJob>
16 #include <KIO/Global>
17 #include <KLocalizedString>
18 #include <KService>
19
20 #include <QDesktopServices>
21 #include <QHBoxLayout>
22 #include <QLabel>
23 #include <QMenu>
24 #include <QMouseEvent>
25 #include <QPushButton>
26 #include <QStorageInfo>
27 #include <QToolButton>
28 #include <QVBoxLayout>
29 #include <QWidgetAction>
30
31 StatusBarSpaceInfo::StatusBarSpaceInfo(QWidget *parent)
32 : QWidget(parent)
33 , m_observer(nullptr)
34 , m_installFilelightWidgetAction{nullptr}
35 , m_hasSpaceInfo{false}
36 , m_shown{false}
37 {
38 hide(); // Only become visible when we have space info to show. @see StatusBarSpaceInfo::setShown().
39
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);
47
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);
53 }
54
55 StatusBarSpaceInfo::~StatusBarSpaceInfo()
56 {
57 }
58
59 void StatusBarSpaceInfo::setShown(bool shown)
60 {
61 m_shown = shown;
62 if (!m_shown) {
63 hide();
64 return;
65 }
66
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);
71 }
72
73 if (m_hasSpaceInfo) {
74 slotValuesChanged();
75 }
76 }
77
78 void StatusBarSpaceInfo::setUrl(const QUrl &url)
79 {
80 if (m_url != url) {
81 m_url = url;
82 m_hasSpaceInfo = false;
83 if (m_observer) {
84 m_observer.reset(new SpaceInfoObserver(m_url, this));
85 connect(m_observer.data(), &SpaceInfoObserver::valuesChanged, this, &StatusBarSpaceInfo::slotValuesChanged);
86 }
87 }
88 }
89
90 QUrl StatusBarSpaceInfo::url() const
91 {
92 return m_url;
93 }
94
95 void StatusBarSpaceInfo::update()
96 {
97 if (m_observer) {
98 m_observer->update();
99 }
100 }
101
102 void StatusBarSpaceInfo::showEvent(QShowEvent *event)
103 {
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);
107 }
108 QWidget::showEvent(event);
109 }
110
111 void StatusBarSpaceInfo::hideEvent(QHideEvent *event)
112 {
113 if (m_hasSpaceInfo) {
114 m_observer.reset();
115 m_hasSpaceInfo = false;
116 }
117 QWidget::hideEvent(event);
118 }
119
120 QSize StatusBarSpaceInfo::minimumSizeHint() const
121 {
122 return QSize();
123 }
124
125 void StatusBarSpaceInfo::updateMenu()
126 {
127 m_buttonMenu->clear();
128
129 // Creates a menu with tools that help to find out more about free
130 // disk space for the given url.
131
132 const KService::Ptr filelight = KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"));
133 const KService::Ptr kdiskfree = KService::serviceByDesktopName(QStringLiteral("org.kde.kdf"));
134
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();
139 }
140 m_buttonMenu->addAction(m_installFilelightWidgetAction);
141 return;
142 }
143
144 if (filelight) {
145 QAction *filelightFolderAction = m_buttonMenu->addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - current folder"));
146
147 m_buttonMenu->connect(filelightFolderAction, &QAction::triggered, m_buttonMenu, [this, filelight](bool) {
148 auto *job = new KIO::ApplicationLauncherJob(filelight);
149 job->setUrls({m_url});
150 job->start();
151 });
152
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"));
157
158 m_buttonMenu->connect(filelightDiskAction, &QAction::triggered, m_buttonMenu, [this, filelight](bool) {
159 const QStorageInfo info(m_url.toLocalFile());
160
161 if (info.isValid() && info.isReady()) {
162 auto *job = new KIO::ApplicationLauncherJob(filelight);
163 job->setUrls({QUrl::fromLocalFile(info.rootPath())});
164 job->start();
165 }
166 });
167 }
168
169 QAction *filelightAllAction = m_buttonMenu->addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - all devices"));
170
171 m_buttonMenu->connect(filelightAllAction, &QAction::triggered, m_buttonMenu, [this, filelight](bool) {
172 const QStorageInfo info(m_url.toLocalFile());
173
174 if (info.isValid() && info.isReady()) {
175 auto *job = new KIO::ApplicationLauncherJob(filelight);
176 job->start();
177 }
178 });
179 }
180
181 if (kdiskfree) {
182 QAction *kdiskfreeAction = m_buttonMenu->addAction(QIcon::fromTheme(QStringLiteral("kdf")), i18n("KDiskFree"));
183
184 connect(kdiskfreeAction, &QAction::triggered, this, [kdiskfree] {
185 auto *job = new KIO::ApplicationLauncherJob(kdiskfree);
186 job->start();
187 });
188 }
189 }
190
191 void StatusBarSpaceInfo::slotInstallFilelightButtonClicked()
192 {
193 #ifdef Q_OS_WIN
194 QDesktopServices::openUrl(QUrl("https://apps.kde.org/filelight"));
195 #else
196 auto packageInstaller = new DolphinPackageInstaller(
197 FILELIGHT_PACKAGE_NAME,
198 QUrl("appstream://org.kde.filelight.desktop"),
199 []() {
200 return KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"));
201 },
202 this);
203 connect(packageInstaller, &KJob::result, this, [this](KJob *job) {
204 Q_EMIT showInstallationProgress(QString(), 100); // Hides the progress information in the status bar.
205 if (job->error()) {
206 Q_EMIT showMessage(job->errorString(), KMessageWidget::Error);
207 } else {
208 Q_EMIT showMessage(xi18nc("@info", "<application>Filelight</application> installed successfully."), KMessageWidget::Positive);
209 if (m_textInfoButton->menu()->isVisible()) {
210 m_textInfoButton->menu()->hide();
211 updateMenu();
212 m_textInfoButton->menu()->show();
213 }
214 }
215 });
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);
221 }
222 });
223 packageInstaller->start();
224 #endif
225 }
226
227 void StatusBarSpaceInfo::slotValuesChanged()
228 {
229 Q_ASSERT(m_observer);
230 const quint64 size = m_observer->size();
231
232 if (!m_shown || size == 0) {
233 hide();
234 return;
235 }
236
237 m_hasSpaceInfo = true;
238
239 const quint64 available = m_observer->available();
240 const quint64 used = size - available;
241 const int percentUsed = qRound(100.0 * qreal(used) / qreal(size));
242
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),
249 percentUsed));
250 setUpdatesEnabled(false);
251 m_capacityBar->setValue(percentUsed);
252 setUpdatesEnabled(true);
253
254 if (!isVisible()) {
255 show();
256 } else {
257 update();
258 }
259 }
260
261 void StatusBarSpaceInfo::initialiseInstallFilelightWidgetAction()
262 {
263 Q_ASSERT(!m_installFilelightWidgetAction);
264
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);
271
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);
279
280 vLayout->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT);
281
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>"),
285 containerWidget);
286 installFilelightBody->setAlignment(Qt::AlignCenter);
287 installFilelightBody->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard);
288 vLayout->addWidget(installFilelightBody);
289
290 vLayout->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT);
291
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);
298
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);
305
306 m_installFilelightWidgetAction = new QWidgetAction{this};
307 m_installFilelightWidgetAction->setDefaultWidget(containerWidget); // transfers ownership of containerWidget
308 }
309
310 #include "moc_statusbarspaceinfo.cpp"