From de197075a70905701f18b04d3249fd684de7f79d Mon Sep 17 00:00:00 2001 From: Frank Reininghaus Date: Thu, 22 May 2014 18:42:17 +0200 Subject: [PATCH] Keep the "free space" information updated in all visible views The old code would watch the free space on a mount point, i.e., determine the free space again, in 10-second intervals, only until the view became invisible once (even if it was invisible only for a very short moment, i.e., while splitting the view). This commit ensures that the mount point is watched again as soon as the corresponding view becomes visible again. Moreover, the object that watches the free space for a mount point is shared among all views that show URLs that belong to this mount point. To achieve this, there is a central cache which can be used to obtain an existing MountPointObserver for a certain path. If necessary, a new MountPointObserver is created and added to the cache. The MountPointObserver is removed from the cache and destroyed only if no views use it any more, and no new users appear until the next update (which happens every 10 seconds). This prevents that the free space is measured repeatedly when changing the current directory on the same mount point. Many thanks to Emmanuel Pescosta for the initial ideas to factor out the "free space" code and to establish a central storage for the "observer" objects, and for providing many good suggestions how to improve the code! BUG: 327708 REVIEW: 118208 FIXED-IN: 4.14.0 --- dolphin/src/CMakeLists.txt | 3 + dolphin/src/statusbar/mountpointobserver.cpp | 48 ++++++++ dolphin/src/statusbar/mountpointobserver.h | 109 ++++++++++++++++++ .../src/statusbar/mountpointobservercache.cpp | 99 ++++++++++++++++ .../src/statusbar/mountpointobservercache.h | 58 ++++++++++ dolphin/src/statusbar/spaceinfoobserver.cpp | 89 ++++++++++++++ dolphin/src/statusbar/spaceinfoobserver.h | 51 ++++++++ dolphin/src/statusbar/statusbarspaceinfo.cpp | 57 +++------ dolphin/src/statusbar/statusbarspaceinfo.h | 10 +- 9 files changed, 480 insertions(+), 44 deletions(-) create mode 100644 dolphin/src/statusbar/mountpointobserver.cpp create mode 100644 dolphin/src/statusbar/mountpointobserver.h create mode 100644 dolphin/src/statusbar/mountpointobservercache.cpp create mode 100644 dolphin/src/statusbar/mountpointobservercache.h create mode 100644 dolphin/src/statusbar/spaceinfoobserver.cpp create mode 100644 dolphin/src/statusbar/spaceinfoobserver.h diff --git a/dolphin/src/CMakeLists.txt b/dolphin/src/CMakeLists.txt index 3f584792d1..0a72721a98 100644 --- a/dolphin/src/CMakeLists.txt +++ b/dolphin/src/CMakeLists.txt @@ -211,6 +211,9 @@ set(dolphin_SRCS settings/viewmodes/viewmodesettings.cpp settings/viewmodes/viewsettingstab.cpp statusbar/dolphinstatusbar.cpp + statusbar/mountpointobserver.cpp + statusbar/mountpointobservercache.cpp + statusbar/spaceinfoobserver.cpp statusbar/statusbarspaceinfo.cpp views/zoomlevelinfo.cpp ) diff --git a/dolphin/src/statusbar/mountpointobserver.cpp b/dolphin/src/statusbar/mountpointobserver.cpp new file mode 100644 index 0000000000..4a23be6b62 --- /dev/null +++ b/dolphin/src/statusbar/mountpointobserver.cpp @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2014 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "mountpointobserver.h" +#include "mountpointobservercache.h" + +MountPointObserver::MountPointObserver(const QString& mountPoint, QObject* parent) : + QObject(parent), + m_mountPoint(mountPoint), + m_referenceCount(0), + m_spaceInfo(KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint)) +{ +} + +MountPointObserver* MountPointObserver::observerForPath(const QString& path) +{ + MountPointObserver* observer = MountPointObserverCache::instance()->observerForPath(path); + return observer; +} + +void MountPointObserver::update() +{ + if (m_referenceCount == 0) { + delete this; + } else { + const KDiskFreeSpaceInfo spaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(m_mountPoint); + if (spaceInfo.size() != m_spaceInfo.size() || spaceInfo.available() != m_spaceInfo.available()) { + m_spaceInfo = spaceInfo; + emit spaceInfoChanged(); + } + } +} diff --git a/dolphin/src/statusbar/mountpointobserver.h b/dolphin/src/statusbar/mountpointobserver.h new file mode 100644 index 0000000000..ac5f8ecc37 --- /dev/null +++ b/dolphin/src/statusbar/mountpointobserver.h @@ -0,0 +1,109 @@ +/*************************************************************************** + * Copyright (C) 2014 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef MOUNTPOINTOBSERVER_H +#define MOUNTPOINTOBSERVER_H + +#include + +#include + +/** + * A MountPointObserver can be used to determine the free space on a mount + * point. It will then check the free space periodically, and emit the signal + * spaceInfoChanged() if the return value of spaceInfo() has changed. + * + * Since multiple users which watch paths on the same mount point can share + * a MountPointObserver, it is not possible to create a MountPointObserver + * manually. Instead, the function observerForPath(QString&) should be called, + * which returns either an existing or a newly created MountPointObserver for + * the mount point where the path is mounted. observerForPath(QString&) looks + * for a suitable MountPointObserver in an internal cache and creates new + * MountPointObservers and adds them to the cache if necessary. + * + * Reference counting is used to keep track of the number of users, and to + * decide when the object can be deleted. A user of this class should call + * the method ref() when it starts using it, and deref() when it does not need + * the MountPointObserver any more. + * + * The object will not be deleted immediately if the reference count reaches + * zero. The object will only be destroyed when the next periodic update of + * the free space information happens, and the reference count is still zero. + * This approach makes it possible to re-use the object if a new user requests + * the free space for the same mount point before the next update. + */ +class MountPointObserver : public QObject +{ + Q_OBJECT + + explicit MountPointObserver(const QString& mountPoint, QObject* parent = 0); + virtual ~MountPointObserver() {} + +public: + /** + * Obtains information about the available space on the observed mount point. + */ + KDiskFreeSpaceInfo spaceInfo() const { return m_spaceInfo; } + + /** + * Call this function to indicate that the caller intends to continue using this object. An + * internal reference count is increased then. When the observer is not needed any more, + * deref() should be called, which decreases the reference count again. + */ + void ref() { ++m_referenceCount; } + + /** + * This function can be used to indicate that the caller does not need this MountPointObserver + * any more. Internally, a reference count is decreased. If the reference count is zero while + * update() is called, the object deletes itself. + */ + void deref() + { + --m_referenceCount; + Q_ASSERT(m_referenceCount >= 0); + } + + /** + * Returns a MountPointObserver for the given \a path. If the caller intends to continue using + * the returned object, it must call its ref() method. + */ + static MountPointObserver* observerForPath(const QString& path); + +signals: + /** + * This signal is emitted if the information that spaceInfo() will return has changed. + */ + void spaceInfoChanged(); + +public slots: + /** + * If this slot is invoked, MountPointObserver checks if the available space on the observed + * mount point has changed, and emits spaceInfoChanged() if that is the case. + */ + void update(); + +private: + const QString m_mountPoint; + int m_referenceCount; + KDiskFreeSpaceInfo m_spaceInfo; + + friend class MountPointObserverCache; +}; + +#endif diff --git a/dolphin/src/statusbar/mountpointobservercache.cpp b/dolphin/src/statusbar/mountpointobservercache.cpp new file mode 100644 index 0000000000..aff0c8e2f0 --- /dev/null +++ b/dolphin/src/statusbar/mountpointobservercache.cpp @@ -0,0 +1,99 @@ +/*************************************************************************** + * Copyright (C) 2014 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "mountpointobservercache.h" + +#include "mountpointobserver.h" + +#include +#include + +#include + +class MountPointObserverCacheSingleton +{ +public: + MountPointObserverCache instance; +}; +K_GLOBAL_STATIC(MountPointObserverCacheSingleton, s_MountPointObserverCache) + + +MountPointObserverCache::MountPointObserverCache() : + m_observerForMountPoint(), + m_mountPointForObserver(), + m_updateTimer(0) +{ + m_updateTimer = new QTimer(this); +} + +MountPointObserverCache::~MountPointObserverCache() +{ +} + +MountPointObserverCache* MountPointObserverCache::instance() +{ + return &s_MountPointObserverCache->instance; +} + +MountPointObserver* MountPointObserverCache::observerForPath(const QString& path) +{ + // Try to share the observer with other paths that have the same mount point. + QString mountPointPath; + KMountPoint::Ptr mountPoint = KMountPoint::currentMountPoints().findByPath(path); + if (mountPoint) { + mountPointPath = mountPoint->mountPoint(); + } else { + // Even if determining the mount point failed, KDiskFreeSpaceInfo might still + // be able to retrieve information about the path. + mountPointPath = path; + } + + MountPointObserver* observer = m_observerForMountPoint.value(mountPointPath); + if (!observer) { + observer = new MountPointObserver(mountPointPath, this); + m_observerForMountPoint.insert(mountPointPath, observer); + m_mountPointForObserver.insert(observer, mountPointPath); + Q_ASSERT(m_observerForMountPoint.count() == m_mountPointForObserver.count()); + + connect(observer, SIGNAL(destroyed(QObject*)), this, SLOT(slotObserverDestroyed(QObject*))); + + if (!m_updateTimer->isActive()) { + m_updateTimer->start(10000); + } + + connect(m_updateTimer, SIGNAL(timeout()), observer, SLOT(update())); + } + + return observer; +} + +void MountPointObserverCache::slotObserverDestroyed(QObject* observer) +{ + Q_ASSERT(m_mountPointForObserver.contains(observer)); + const QString& path = m_mountPointForObserver.value(observer); + Q_ASSERT(m_observerForMountPoint.contains(path)); + m_observerForMountPoint.remove(path); + m_mountPointForObserver.remove(observer); + + Q_ASSERT(m_observerForMountPoint.count() == m_mountPointForObserver.count()); + + if (m_mountPointForObserver.isEmpty()) { + m_updateTimer->stop(); + } +} diff --git a/dolphin/src/statusbar/mountpointobservercache.h b/dolphin/src/statusbar/mountpointobservercache.h new file mode 100644 index 0000000000..425000645b --- /dev/null +++ b/dolphin/src/statusbar/mountpointobservercache.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2014 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef MOUNTPOINTOBSERVERCACHE_H +#define MOUNTPOINTOBSERVERCACHE_H + +#include +#include + +class MountPointObserver; +class QTimer; + +class MountPointObserverCache : public QObject +{ + Q_OBJECT + + MountPointObserverCache(); + virtual ~MountPointObserverCache(); + +public: + static MountPointObserverCache* instance(); + + /** + * Returns a MountPointObserver for the given \a path. A new observer is created if necessary. + */ + MountPointObserver* observerForPath(const QString& path); + +private slots: + /** + * Removes the given \a observer from the cache. + */ + void slotObserverDestroyed(QObject* observer); + +private: + QHash m_observerForMountPoint; + QHash m_mountPointForObserver; + QTimer* m_updateTimer; + + friend class MountPointObserverCacheSingleton; +}; + +#endif diff --git a/dolphin/src/statusbar/spaceinfoobserver.cpp b/dolphin/src/statusbar/spaceinfoobserver.cpp new file mode 100644 index 0000000000..9125a9308a --- /dev/null +++ b/dolphin/src/statusbar/spaceinfoobserver.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright (C) 2014 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "spaceinfoobserver.h" + +#include "mountpointobserver.h" + +#include + +SpaceInfoObserver::SpaceInfoObserver(const KUrl& url, QObject* parent) : + QObject(parent), + m_mountPointObserver(0) +{ + if (url.isLocalFile()) { + m_mountPointObserver = MountPointObserver::observerForPath(url.toLocalFile()); + m_mountPointObserver->ref(); + connect(m_mountPointObserver, SIGNAL(spaceInfoChanged()), this, SIGNAL(valuesChanged())); + } +} + +SpaceInfoObserver::~SpaceInfoObserver() +{ + if (m_mountPointObserver) { + m_mountPointObserver->deref(); + m_mountPointObserver = 0; + } +} + +quint64 SpaceInfoObserver::size() const +{ + if (m_mountPointObserver && m_mountPointObserver->spaceInfo().isValid()) { + return m_mountPointObserver->spaceInfo().size(); + } else { + return 0; + } +} + +quint64 SpaceInfoObserver::available() const +{ + if (m_mountPointObserver && m_mountPointObserver->spaceInfo().isValid()) { + return m_mountPointObserver->spaceInfo().available(); + } else { + return 0; + } +} + +void SpaceInfoObserver::setUrl(const KUrl& url) +{ + if (url.isLocalFile()) { + MountPointObserver* newObserver = MountPointObserver::observerForPath(url.toLocalFile()); + if (newObserver != m_mountPointObserver) { + if (m_mountPointObserver) { + disconnect(m_mountPointObserver, SIGNAL(spaceInfoChanged()), this, SIGNAL(valuesChanged())); + m_mountPointObserver->deref(); + m_mountPointObserver = 0; + } + + m_mountPointObserver = newObserver; + m_mountPointObserver->ref(); + connect(m_mountPointObserver, SIGNAL(spaceInfoChanged()), this, SIGNAL(valuesChanged())); + + emit valuesChanged(); + } + } else { + if (m_mountPointObserver) { + disconnect(m_mountPointObserver, SIGNAL(spaceInfoChanged()), this, SIGNAL(valuesChanged())); + m_mountPointObserver->deref(); + m_mountPointObserver = 0; + + emit valuesChanged(); + } + } +} diff --git a/dolphin/src/statusbar/spaceinfoobserver.h b/dolphin/src/statusbar/spaceinfoobserver.h new file mode 100644 index 0000000000..d2fb6ebf31 --- /dev/null +++ b/dolphin/src/statusbar/spaceinfoobserver.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2014 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef SPACEINFOOBSERVER_H +#define SPACEINFOOBSERVER_H + +#include + +class KUrl; +class MountPointObserver; + +class SpaceInfoObserver : public QObject +{ + Q_OBJECT + +public: + explicit SpaceInfoObserver(const KUrl& url, QObject* parent = 0); + virtual ~SpaceInfoObserver(); + + quint64 size() const; + quint64 available() const; + + void setUrl(const KUrl& url); + +signals: + /** + * This signal is emitted if the information that size() and/or available() will return has changed. + */ + void valuesChanged(); + +private: + MountPointObserver* m_mountPointObserver; +}; + +#endif diff --git a/dolphin/src/statusbar/statusbarspaceinfo.cpp b/dolphin/src/statusbar/statusbarspaceinfo.cpp index 61b28334a2..3692947b1c 100644 --- a/dolphin/src/statusbar/statusbarspaceinfo.cpp +++ b/dolphin/src/statusbar/statusbarspaceinfo.cpp @@ -20,22 +20,17 @@ #include "statusbarspaceinfo.h" -#include +#include "spaceinfoobserver.h" + #include #include -#include #include StatusBarSpaceInfo::StatusBarSpaceInfo(QWidget* parent) : KCapacityBar(KCapacityBar::DrawTextInline, parent), - m_kBSize(0), - m_timer(0) + m_observer(0) { - // Use a timer to update the space information. Polling is useful - // here, as files can be deleted/added outside the scope of Dolphin. - m_timer = new QTimer(this); - connect(m_timer, SIGNAL(timeout()), this, SLOT(calculateSpaceInfo())); } StatusBarSpaceInfo::~StatusBarSpaceInfo() @@ -46,8 +41,8 @@ void StatusBarSpaceInfo::setUrl(const KUrl& url) { if (m_url != url) { m_url = url; - if (isVisible()) { - calculateSpaceInfo(); + if (m_observer) { + m_observer->setUrl(url); } } } @@ -60,47 +55,33 @@ KUrl StatusBarSpaceInfo::url() const void StatusBarSpaceInfo::showEvent(QShowEvent* event) { KCapacityBar::showEvent(event); - if (!event->spontaneous()) { - calculateSpaceInfo(); - m_timer->start(10000); - } + m_observer.reset(new SpaceInfoObserver(m_url, this)); + slotValuesChanged(); + connect(m_observer.data(), SIGNAL(valuesChanged()), this, SLOT(slotValuesChanged())); } void StatusBarSpaceInfo::hideEvent(QHideEvent* event) { - m_timer->stop(); + m_observer.reset(); KCapacityBar::hideEvent(event); } -void StatusBarSpaceInfo::calculateSpaceInfo() +void StatusBarSpaceInfo::slotValuesChanged() { - // KDiskFreeSpace is for local paths only - if (!m_url.isLocalFile()) { + Q_ASSERT(m_observer); + const quint64 size = m_observer->size(); + if (size == 0) { setText(i18nc("@info:status", "Unknown size")); setValue(0); update(); - return; - } - - KDiskFreeSpaceInfo job = KDiskFreeSpaceInfo::freeSpaceInfo(m_url.toLocalFile()); - if (!job.isValid()) { - setText(i18nc("@info:status", "Unknown size")); - setValue(0); - update(); - return; - } - - KIO::filesize_t kBSize = job.size() / 1024; - KIO::filesize_t kBUsed = job.used() / 1024; - - const bool valuesChanged = (kBUsed != static_cast(value())) || (kBSize != m_kBSize); - if (valuesChanged) { - setText(i18nc("@info:status Free disk space", "%1 free", - KIO::convertSize(job.available()))); + } else { + 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))); setUpdatesEnabled(false); - m_kBSize = kBSize; - setValue(kBSize > 0 ? (kBUsed * 100) / kBSize : 0); + setValue(percentUsed); setUpdatesEnabled(true); update(); } diff --git a/dolphin/src/statusbar/statusbarspaceinfo.h b/dolphin/src/statusbar/statusbarspaceinfo.h index 1849462a9a..1065d9f455 100644 --- a/dolphin/src/statusbar/statusbarspaceinfo.h +++ b/dolphin/src/statusbar/statusbarspaceinfo.h @@ -31,6 +31,8 @@ class QHideEvent; class QShowEvent; +class SpaceInfoObserver; + /** * @short Shows the available space for the volume represented * by the given URL as part of the status bar. @@ -51,15 +53,11 @@ protected: void hideEvent(QHideEvent* event); private slots: - /** - * Calculates the space information for the current set URL. - */ - void calculateSpaceInfo(); + void slotValuesChanged(); private: - quint64 m_kBSize; + QScopedPointer m_observer; KUrl m_url; - QTimer* m_timer; }; #endif