X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/576a97d8eb9601f4ad74def6528cc90fffe97b1c..0aadb8cf849fcab8ee8e8108a19e1ff4830054b1:/src/statusbar/statusbarspaceinfo.cpp diff --git a/src/statusbar/statusbarspaceinfo.cpp b/src/statusbar/statusbarspaceinfo.cpp index bb7f85842..c25d028d6 100644 --- a/src/statusbar/statusbarspaceinfo.cpp +++ b/src/statusbar/statusbarspaceinfo.cpp @@ -6,18 +6,50 @@ #include "statusbarspaceinfo.h" +#include "config-dolphin.h" +#include "dolphinpackageinstaller.h" +#include "global.h" #include "spaceinfoobserver.h" +#include +#include +#include #include -#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include -StatusBarSpaceInfo::StatusBarSpaceInfo(QWidget* parent) : - KCapacityBar(KCapacityBar::DrawTextInline, parent), - m_observer(nullptr) +StatusBarSpaceInfo::StatusBarSpaceInfo(QWidget *parent) + : QWidget(parent) + , m_observer(nullptr) + , m_installFilelightWidgetAction{nullptr} + , m_hasSpaceInfo{false} + , m_shown{false} { - setCursor(Qt::PointingHandCursor); + hide(); // Only become visible when we have space info to show. @see StatusBarSpaceInfo::setShown(). + + m_capacityBar = new KCapacityBar(KCapacityBar::DrawTextInline, this); + m_textInfoButton = new QToolButton(this); + m_textInfoButton->setAutoRaise(true); + m_textInfoButton->setPopupMode(QToolButton::InstantPopup); + m_buttonMenu = new QMenu(this); + m_textInfoButton->setMenu(m_buttonMenu); + connect(m_buttonMenu, &QMenu::aboutToShow, this, &StatusBarSpaceInfo::updateMenu); + + auto layout = new QHBoxLayout(this); + // 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. + layout->setContentsMargins(2, -1, 0, -1); // "-1" makes it so the fixed height won't be ignored. + layout->addWidget(m_capacityBar); + layout->addWidget(m_textInfoButton); } StatusBarSpaceInfo::~StatusBarSpaceInfo() @@ -29,15 +61,25 @@ void StatusBarSpaceInfo::setShown(bool shown) m_shown = shown; if (!m_shown) { hide(); - m_ready = false; + return; + } + + // We only show() this widget in slotValueChanged() when it m_hasSpaceInfo. + if (m_observer.isNull()) { + m_observer.reset(new SpaceInfoObserver(m_url, this)); + connect(m_observer.data(), &SpaceInfoObserver::valuesChanged, this, &StatusBarSpaceInfo::slotValuesChanged); + } + + if (m_hasSpaceInfo) { + slotValuesChanged(); } } -void StatusBarSpaceInfo::setUrl(const QUrl& url) +void StatusBarSpaceInfo::setUrl(const QUrl &url) { if (m_url != url) { m_url = url; - m_ready = false; + m_hasSpaceInfo = false; if (m_observer) { m_observer.reset(new SpaceInfoObserver(m_url, this)); connect(m_observer.data(), &SpaceInfoObserver::valuesChanged, this, &StatusBarSpaceInfo::slotValuesChanged); @@ -57,44 +99,129 @@ void StatusBarSpaceInfo::update() } } -void StatusBarSpaceInfo::showEvent(QShowEvent* event) +void StatusBarSpaceInfo::showEvent(QShowEvent *event) { - if (m_shown) { - if (m_ready) { - KCapacityBar::showEvent(event); - } - - if (m_observer.isNull()) { - m_observer.reset(new SpaceInfoObserver(m_url, this)); - connect(m_observer.data(), &SpaceInfoObserver::valuesChanged, this, &StatusBarSpaceInfo::slotValuesChanged); - } + if (m_shown && m_observer.isNull()) { + m_observer.reset(new SpaceInfoObserver(m_url, this)); + connect(m_observer.data(), &SpaceInfoObserver::valuesChanged, this, &StatusBarSpaceInfo::slotValuesChanged); } + QWidget::showEvent(event); } -void StatusBarSpaceInfo::hideEvent(QHideEvent* event) +void StatusBarSpaceInfo::hideEvent(QHideEvent *event) { - if (m_ready) { + if (m_hasSpaceInfo) { m_observer.reset(); - m_ready = false; + m_hasSpaceInfo = false; } - KCapacityBar::hideEvent(event); + QWidget::hideEvent(event); } -void StatusBarSpaceInfo::mousePressEvent(QMouseEvent* event) +QSize StatusBarSpaceInfo::minimumSizeHint() const { - if (event->button() == Qt::LeftButton) { - // Creates a menu with tools that help to find out more about free - // disk space for the given url. - - // Note that this object must live long enough in case the user opens - // the "Configure..." dialog - KMoreToolsMenuFactory menuFactory(QStringLiteral("dolphin/statusbar-diskspace-menu")); - menuFactory.setParentWidget(this); - auto menu = menuFactory.createMenuFromGroupingNames( - { "disk-usage", "more:", "disk-partitions" }, m_url); - - menu->exec(QCursor::pos()); + return QSize(); +} + +void StatusBarSpaceInfo::updateMenu() +{ + m_buttonMenu->clear(); + + // Creates a menu with tools that help to find out more about free + // disk space for the given url. + + const KService::Ptr filelight = KService::serviceByDesktopName(QStringLiteral("org.kde.filelight")); + const KService::Ptr kdiskfree = KService::serviceByDesktopName(QStringLiteral("org.kde.kdf")); + + if (!filelight && !kdiskfree) { + // 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. + if (!m_installFilelightWidgetAction) { + initialiseInstallFilelightWidgetAction(); + } + m_buttonMenu->addAction(m_installFilelightWidgetAction); + return; } + + if (filelight) { + QAction *filelightFolderAction = m_buttonMenu->addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - current folder")); + + m_buttonMenu->connect(filelightFolderAction, &QAction::triggered, m_buttonMenu, [this, filelight](bool) { + auto *job = new KIO::ApplicationLauncherJob(filelight); + job->setUrls({m_url}); + job->start(); + }); + + // For remote URLs like FTP analyzing the device makes no sense + if (m_url.isLocalFile()) { + QAction *filelightDiskAction = + m_buttonMenu->addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - current device")); + + m_buttonMenu->connect(filelightDiskAction, &QAction::triggered, m_buttonMenu, [this, filelight](bool) { + const QStorageInfo info(m_url.toLocalFile()); + + if (info.isValid() && info.isReady()) { + auto *job = new KIO::ApplicationLauncherJob(filelight); + job->setUrls({QUrl::fromLocalFile(info.rootPath())}); + job->start(); + } + }); + } + + QAction *filelightAllAction = m_buttonMenu->addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Disk Usage Statistics - all devices")); + + m_buttonMenu->connect(filelightAllAction, &QAction::triggered, m_buttonMenu, [this, filelight](bool) { + const QStorageInfo info(m_url.toLocalFile()); + + if (info.isValid() && info.isReady()) { + auto *job = new KIO::ApplicationLauncherJob(filelight); + job->start(); + } + }); + } + + if (kdiskfree) { + QAction *kdiskfreeAction = m_buttonMenu->addAction(QIcon::fromTheme(QStringLiteral("kdf")), i18n("KDiskFree")); + + connect(kdiskfreeAction, &QAction::triggered, this, [kdiskfree] { + auto *job = new KIO::ApplicationLauncherJob(kdiskfree); + job->start(); + }); + } +} + +void StatusBarSpaceInfo::slotInstallFilelightButtonClicked() +{ +#ifdef Q_OS_WIN + QDesktopServices::openUrl(QUrl("https://apps.kde.org/filelight")); +#else + auto packageInstaller = new DolphinPackageInstaller( + FILELIGHT_PACKAGE_NAME, + QUrl("appstream://org.kde.filelight.desktop"), + []() { + return KService::serviceByDesktopName(QStringLiteral("org.kde.filelight")); + }, + this); + connect(packageInstaller, &KJob::result, this, [this](KJob *job) { + Q_EMIT showInstallationProgress(QString(), 100); // Hides the progress information in the status bar. + if (job->error()) { + Q_EMIT showMessage(job->errorString(), KMessageWidget::Error); + } else { + Q_EMIT showMessage(xi18nc("@info", "Filelight installed successfully."), KMessageWidget::Positive); + if (m_textInfoButton->menu()->isVisible()) { + m_textInfoButton->menu()->hide(); + updateMenu(); + m_textInfoButton->menu()->show(); + } + } + }); + const auto installationTaskText{i18nc("@info:status", "Installing Filelight…")}; + Q_EMIT showInstallationProgress(installationTaskText, -1); + connect(packageInstaller, &KJob::percentChanged, this, [this, installationTaskText](KJob * /* job */, long unsigned int percent) { + if (percent < 100) { // Ignore some weird reported values. + Q_EMIT showInstallationProgress(installationTaskText, percent); + } + }); + packageInstaller->start(); +#endif } void StatusBarSpaceInfo::slotValuesChanged() @@ -107,15 +234,21 @@ void StatusBarSpaceInfo::slotValuesChanged() return; } - m_ready = true; + m_hasSpaceInfo = true; const quint64 available = m_observer->available(); const quint64 used = size - available; const int percentUsed = qRound(100.0 * qreal(used) / qreal(size)); - setText(i18nc("@info:status Free disk space", "%1 free", KIO::convertSize(available))); + m_textInfoButton->setText(i18nc("@info:status Free disk space", "%1 free", KIO::convertSize(available))); + setToolTip(i18nc("tooltip:status Free disk space", "%1 free out of %2 (%3% used)", KIO::convertSize(available), KIO::convertSize(size), percentUsed)); + m_textInfoButton->setToolTip(i18nc("@info:tooltip for the free disk space button", + "%1 free out of %2 (%3% used)\nPress to manage disk space usage.", + KIO::convertSize(available), + KIO::convertSize(size), + percentUsed)); setUpdatesEnabled(false); - setValue(percentUsed); + m_capacityBar->setValue(percentUsed); setUpdatesEnabled(true); if (!isVisible()) { @@ -125,3 +258,53 @@ void StatusBarSpaceInfo::slotValuesChanged() } } +void StatusBarSpaceInfo::initialiseInstallFilelightWidgetAction() +{ + Q_ASSERT(!m_installFilelightWidgetAction); + + auto containerWidget = new QWidget{this}; + containerWidget->setContentsMargins(Dolphin::VERTICAL_SPACER_HEIGHT, + Dolphin::VERTICAL_SPACER_HEIGHT, + Dolphin::VERTICAL_SPACER_HEIGHT, // Using the same value for every spacing in this containerWidget looks nice. + Dolphin::VERTICAL_SPACER_HEIGHT); + auto vLayout = new QVBoxLayout(containerWidget); + + auto installFilelightTitle = new QLabel(i18nc("@title", "Free Up Disk Space"), containerWidget); + installFilelightTitle->setAlignment(Qt::AlignCenter); + installFilelightTitle->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard); + QFont titleFont{installFilelightTitle->font()}; + titleFont.setPointSize(titleFont.pointSize() + 2); + installFilelightTitle->setFont(titleFont); + vLayout->addWidget(installFilelightTitle); + + vLayout->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT); + + auto installFilelightBody = + // i18n: The new line ("") tag is only there to format this text visually pleasing, i.e. to avoid having one very long line. + new QLabel(xi18nc("@title", "Install additional software to view disk usage statisticsand identify big files and folders."), + containerWidget); + installFilelightBody->setAlignment(Qt::AlignCenter); + installFilelightBody->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard); + vLayout->addWidget(installFilelightBody); + + vLayout->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT); + + auto installFilelightButton = + new QPushButton(QIcon::fromTheme(QStringLiteral("filelight")), i18nc("@action:button", "Install Filelight…"), containerWidget); + installFilelightButton->setMinimumWidth(std::max(installFilelightButton->sizeHint().width(), installFilelightTitle->sizeHint().width())); + auto buttonLayout = new QHBoxLayout; // The parent is automatically set on addLayout() below. + buttonLayout->addWidget(installFilelightButton, 0, Qt::AlignHCenter); + vLayout->addLayout(buttonLayout); + + // Make sure one Tab press focuses the button after the UI opened. + m_buttonMenu->setFocusProxy(installFilelightButton); + containerWidget->setFocusPolicy(Qt::TabFocus); + containerWidget->setFocusProxy(installFilelightButton); + installFilelightButton->setAccessibleDescription(installFilelightBody->text()); + connect(installFilelightButton, &QAbstractButton::clicked, this, &StatusBarSpaceInfo::slotInstallFilelightButtonClicked); + + m_installFilelightWidgetAction = new QWidgetAction{this}; + m_installFilelightWidgetAction->setDefaultWidget(containerWidget); // transfers ownership of containerWidget +} + +#include "moc_statusbarspaceinfo.cpp"