diff --git a/kfile/CMakeLists.txt b/kfile/CMakeLists.txt index ceae140..c7c4d3d 100644 --- a/kfile/CMakeLists.txt +++ b/kfile/CMakeLists.txt @@ -20,6 +20,7 @@ set(kfile_LIB_SRCS kfilefiltercombo.cpp kfiletreeview.cpp kfilewidget.cpp + kfileplacesdevicecache.cpp kfileplacesitem.cpp kfileplacesmodel.cpp kfileplacessharedbookmarks.cpp @@ -63,6 +64,7 @@ install( FILES kdirselectdialog.h kdirsortfilterproxymodel.h kfilefiltercombo.h + kfileplacesdevicecache.h kfileplacesmodel.h kfileplacesview.h kfilepreviewgenerator.h diff --git a/kfile/kfileplacesdevicecache.cpp b/kfile/kfileplacesdevicecache.cpp new file mode 100644 index 0000000..40f7242 --- /dev/null +++ b/kfile/kfileplacesdevicecache.cpp @@ -0,0 +1,174 @@ +/* + Copyright (C) 2012 Dan Vrátil + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kfileplacesdevicecache.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +KFilePlacesDeviceCache* KFilePlacesDeviceCache::s_instance = 0; + +class KFilePlacesDeviceCache::Private +{ + public: + Private(KFilePlacesDeviceCache *parent): + queryRunning(false), + q(parent) + { } + + ~Private() + { } + + /* This method runs asynchronously in thread */ + QSet listSolidDevicesAsync() + { + QSet udis; + + kDebug() << "Querying Solid devices..."; + const QList& deviceList = Solid::Device::listFromQuery(solidPredicate); + kDebug() << "Retrieved" << deviceList.count() << "devices"; + + Q_FOREACH (const Solid::Device& device, deviceList) { + if (solidPredicate.matches(device)) { + udis << device.udi(); + } + } + + return udis; + } + + void _k_slotDeviceAdded(const QString &udi) + { + devicesCache << udi; + + Q_EMIT q->deviceAdded(udi); + } + + void _k_slotDeviceRemoved(const QString &udi) + { + if (!devicesCache.contains(udi)) { + return; + } + + devicesCache.remove(udi); + + Q_EMIT q->deviceRemoved(udi); + } + + void _k_listSolidDevicesFinished() + { + Q_FOREACH (const QString& device, futureWatcher->result()) { + _k_slotDeviceAdded(device); + } + + delete futureWatcher; + futureWatcher = 0; + + queryRunning = false; + } + + Solid::Predicate solidPredicate; + + QFutureWatcher< QSet >* futureWatcher; + + /* Static */ + QSet devicesCache; + bool queryRunning; + + KFilePlacesDeviceCache *q; +}; + +KFilePlacesDeviceCache::KFilePlacesDeviceCache(): + QObject(), + d(new Private(this)) +{ + Solid::DeviceNotifier* notifier = Solid::DeviceNotifier::instance(); + connect(notifier, SIGNAL(deviceAdded(QString)), this, SLOT(_k_slotDeviceAdded(QString))); + connect(notifier, SIGNAL(deviceRemoved(QString)), this, SLOT(_k_slotDeviceRemoved(QString))); + + QString predicate("[[[[ StorageVolume.ignored == false AND [ StorageVolume.usage == 'FileSystem' OR StorageVolume.usage == 'Encrypted' ]]" + " OR " + "[ IS StorageAccess AND StorageDrive.driveType == 'Floppy' ]]" + " OR " + "OpticalDisc.availableContent & 'Audio' ]" + " OR " + "StorageAccess.ignored == false ]"); + + if (KProtocolInfo::isKnownProtocol("mtp")) { + predicate.prepend("["); + predicate.append(" OR PortableMediaPlayer.supportedProtocols == 'mtp']"); + } + + d->solidPredicate = Solid::Predicate::fromString(predicate); +} + +KFilePlacesDeviceCache* KFilePlacesDeviceCache::self() +{ + static QMutex mutex; + + mutex.lock(); + if (s_instance == 0) { + s_instance = new KFilePlacesDeviceCache(); + } + mutex.unlock(); + + return s_instance; +} + +KFilePlacesDeviceCache::~KFilePlacesDeviceCache() +{ + Solid::DeviceNotifier* notifier = Solid::DeviceNotifier::instance(); + disconnect(notifier, SIGNAL(deviceAdded(QString))); + disconnect(notifier, SIGNAL(deviceRemoved(QString))); + + delete d; +} + +const Solid::Predicate& KFilePlacesDeviceCache::predicate() const +{ + return d->solidPredicate; +} + +const QSet& KFilePlacesDeviceCache::devices() const +{ + kDebug(); + if (d->devicesCache.isEmpty() && !d->queryRunning) { + d->queryRunning = true; + d->futureWatcher = new QFutureWatcher< QSet >; + connect(d->futureWatcher, SIGNAL(finished()), this, SLOT(_k_listSolidDevicesFinished())); + + QFuture< QSet > future = QtConcurrent::run(d, &Private::listSolidDevicesAsync); + d->futureWatcher->setFuture(future); + } + + return d->devicesCache; +} + + + +#include "kfileplacesdevicecache.moc" diff --git a/kfile/kfileplacesdevicecache.h b/kfile/kfileplacesdevicecache.h new file mode 100644 index 0000000..7293d03 --- /dev/null +++ b/kfile/kfileplacesdevicecache.h @@ -0,0 +1,96 @@ +/* + Copyright (C) 2012 Dan Vrátil + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KFILEPLACESDEVICECACHE_H +#define KFILEPLACESDEVICECACHE_H + +#include + +#include +#include + +#include + +/** + * @short Asynchronous cache for Solid devices + * + * Purpose of this cache is to load Solid devices asynchronously, because + * udisks2 backend can take quite a lot of time to enumerate devices, and + * since Solid does not have async API, the UI thread is blocked for too long. + * + * When libsolid2 with asynchronous API is available, this class can go away. + * + * The cache keeps itself up-to-date and notifies listeners when a + * new devices is added or removed. + */ +class KFILE_EXPORT KFilePlacesDeviceCache : public QObject +{ + Q_OBJECT + +public: + /** + * Returns a global instance of the cache + */ + static KFilePlacesDeviceCache* self(); + + /** + * Returns list of Solid devices. + * + * This method always returns immediatelly. When there are no + * devices in the cache, it returns an empty list and will asynchronously + * query Solid for devices and will notify listeners by emitting + * deviceAdded() signal for each devices loaded. + */ + const QSet& devices() const; + + /** + * Returns Solid::Predicate used to obtain devices from Solid + */ + const Solid::Predicate& predicate() const; + +Q_SIGNALS: + /** + * Emitted whenever a new device is discovered. + * + * @param udi UDI (Universal Device ID) of the newly discovered device + */ + void deviceAdded(const QString& udi); + + /** + * Emitted whenever a device is removed from system + * + * @param udi UDI (Universal Device ID) of the removed device + */ + void deviceRemoved(const QString& udi); + +private: + Q_PRIVATE_SLOT(d, void _k_listSolidDevicesFinished()) + Q_PRIVATE_SLOT(d, void _k_slotDeviceAdded(const QString&)) + Q_PRIVATE_SLOT(d, void _k_slotDeviceRemoved(const QString&)) + + class Private; + Private * const d; + friend class Private; + + explicit KFilePlacesDeviceCache(); + virtual ~KFilePlacesDeviceCache(); + + static KFilePlacesDeviceCache* s_instance; +}; + +#endif // KFILEPLACESDEVICECACHE_H diff --git a/kfile/kfileplacesmodel.cpp b/kfile/kfileplacesmodel.cpp index 0192926..e0b01c6 100644 --- a/kfile/kfileplacesmodel.cpp +++ b/kfile/kfileplacesmodel.cpp @@ -20,6 +20,7 @@ #include "kfileplacesmodel.h" #include "kfileplacesitem_p.h" #include "kfileplacessharedbookmarks_p.h" +#include "kfileplacesdevicecache.h" #ifdef _WIN32_WCE #include "Windows.h" @@ -49,14 +50,12 @@ #include #include -#include #include #include #include #include #include #include -#include class KFilePlacesModel::Private { @@ -74,7 +73,6 @@ public: QSet availableDevices; QMap setupInProgress; - Solid::Predicate predicate; KBookmarkManager *bookmarkManager; KFilePlacesSharedBookmarks * sharedBookmarks; @@ -149,30 +147,12 @@ KFilePlacesModel::KFilePlacesModel(QObject *parent) // create after, so if we have own places, they are added afterwards, in case of equal priorities d->sharedBookmarks = new KFilePlacesSharedBookmarks(d->bookmarkManager); - QString predicate("[[[[ StorageVolume.ignored == false AND [ StorageVolume.usage == 'FileSystem' OR StorageVolume.usage == 'Encrypted' ]]" - " OR " - "[ IS StorageAccess AND StorageDrive.driveType == 'Floppy' ]]" - " OR " - "OpticalDisc.availableContent & 'Audio' ]" - " OR " - "StorageAccess.ignored == false ]"); - - if (KProtocolInfo::isKnownProtocol("mtp")) { - predicate.prepend("["); - predicate.append(" OR PortableMediaPlayer.supportedProtocols == 'mtp']"); - } - - d->predicate = Solid::Predicate::fromString(predicate); - - Q_ASSERT(d->predicate.isValid()); - connect(d->bookmarkManager, SIGNAL(changed(QString,QString)), this, SLOT(_k_reloadBookmarks())); connect(d->bookmarkManager, SIGNAL(bookmarksChanged(QString)), this, SLOT(_k_reloadBookmarks())); - d->_k_reloadBookmarks(); - QTimer::singleShot(0, this, SLOT(_k_initDeviceList())); + d->_k_initDeviceList(); } KFilePlacesModel::~KFilePlacesModel() @@ -313,30 +293,21 @@ QModelIndex KFilePlacesModel::closestItem(const KUrl &url) const void KFilePlacesModel::Private::_k_initDeviceList() { - Solid::DeviceNotifier *notifier = Solid::DeviceNotifier::instance(); - - connect(notifier, SIGNAL(deviceAdded(QString)), + KFilePlacesDeviceCache *cache = KFilePlacesDeviceCache::self(); + connect(cache, SIGNAL(deviceAdded(QString)), q, SLOT(_k_deviceAdded(QString))); - connect(notifier, SIGNAL(deviceRemoved(QString)), + connect(cache, SIGNAL(deviceRemoved(QString)), q, SLOT(_k_deviceRemoved(QString))); - const QList &deviceList = Solid::Device::listFromQuery(predicate); - - foreach(const Solid::Device &device, deviceList) { - availableDevices << device.udi(); - } + availableDevices = cache->devices(); _k_reloadBookmarks(); } void KFilePlacesModel::Private::_k_deviceAdded(const QString &udi) { - Solid::Device d(udi); - - if (predicate.matches(d)) { - availableDevices << udi; - _k_reloadBookmarks(); - } + availableDevices << udi; + _k_reloadBookmarks(); } void KFilePlacesModel::Private::_k_deviceRemoved(const QString &udi)