Start of work on a GIO/GVFS device backend.

This commit is contained in:
David Sansome 2010-07-17 14:22:07 +00:00
parent 40a2302311
commit 0dcdf36535
43 changed files with 380 additions and 46 deletions

View File

@ -54,6 +54,7 @@ else(WIN32)
pkg_check_modules(LIBXML libxml-2.0)
pkg_check_modules(GOBJECT gobject-2.0)
pkg_check_modules(LIBGPOD libgpod-1.0)
pkg_check_modules(GIO gio-2.0)
endif(WIN32)
find_library(LASTFM_LIBRARIES lastfm)

View File

@ -470,6 +470,15 @@ if(LIBGPOD_FOUND)
list(APPEND HEADERS devices/gpoddevice.h devices/gpodloader.h)
endif(LIBGPOD_FOUND)
# GIO device backend
if(GIO_FOUND)
set(HAVE_GIO ON)
include_directories(${GIO_INCLUDE_DIRS})
list(APPEND SOURCES devices/giolister.cpp)
list(APPEND HEADERS devices/giolister.h)
endif(GIO_FOUND)
# Mac specific startup stuff
if(APPLE)
list(APPEND SOURCES core/mac_startup.mm)

View File

@ -40,5 +40,6 @@
#cmakedefine HAVE_STATIC_SQLITE
#cmakedefine HAVE_LIBGPOD
#cmakedefine HAVE_GIO
#endif // CONFIG_H_IN

View File

@ -23,8 +23,9 @@
#include <QtDebug>
ConnectedDevice::ConnectedDevice(DeviceLister* lister, const QString& unique_id,
DeviceManager* manager, int database_id)
ConnectedDevice::ConnectedDevice(const QUrl&, DeviceLister* lister,
const QString& unique_id, DeviceManager* manager,
int database_id, bool)
: QObject(manager),
lister_(lister),
unique_id_(unique_id),

View File

@ -18,6 +18,8 @@
#define CONNECTEDDEVICE_H
#include <QObject>
#include <QStringList>
#include <QUrl>
class Database;
class DeviceLister;
@ -29,8 +31,9 @@ class ConnectedDevice : public QObject {
Q_OBJECT
public:
ConnectedDevice(DeviceLister* lister, const QString& unique_id,
DeviceManager* manager, int database_id);
ConnectedDevice(const QUrl& url, DeviceLister* lister,
const QString& unique_id, DeviceManager* manager,
int database_id, bool first_time);
~ConnectedDevice();
DeviceLister* lister() const { return lister_; }

View File

@ -236,23 +236,15 @@ QString DeviceKitLister::FindUniqueIdByPath(const QDBusObjectPath &path) const {
return QString();
}
boost::shared_ptr<ConnectedDevice> DeviceKitLister::Connect(
const QString &unique_id, DeviceManager* manager, int database_id,
bool first_time) {
QUrl DeviceKitLister::MakeDeviceUrl(const QString& id) {
QString mount_point = LockAndGetDeviceInfo(
unique_id, &DeviceData::device_mount_paths)[0];
boost::shared_ptr<ConnectedDevice> ret;
id, &DeviceData::device_mount_paths)[0];
#ifdef HAVE_LIBGPOD
if (QFile::exists(mount_point + "/iTunes_Control")) {
ret.reset(new GPodDevice(
mount_point, this, unique_id, manager, database_id, first_time));
return ret;
return QUrl("ipod://" + mount_point);
}
#endif
ret.reset(new FilesystemDevice(
mount_point, this, unique_id, manager, database_id, first_time));
return ret;
return QUrl::fromLocalFile(mount_point);
}

View File

@ -45,9 +45,7 @@ public:
QString MakeFriendlyName(const QString &id);
boost::shared_ptr<ConnectedDevice> Connect(
const QString &unique_id, DeviceManager* manager, int database_id,
bool first_time);
QUrl MakeDeviceUrl(const QString &id);
protected:
void Init();

View File

@ -18,6 +18,7 @@
#define DEVICELISTER_H
#include <QAbstractItemModel>
#include <QUrl>
#include <boost/shared_ptr.hpp>
@ -46,11 +47,7 @@ public:
virtual QString MakeFriendlyName(const QString& id) = 0;
// Create a new ConnectedDevice instance for the given device. Must be
// thread-safe.
virtual boost::shared_ptr<ConnectedDevice> Connect(
const QString& unique_id, DeviceManager* manager, int database_id,
bool first_time) = 0;
virtual QUrl MakeDeviceUrl(const QString& id) = 0;
signals:
void DeviceAdded(const QString& id);

View File

@ -15,16 +15,19 @@
*/
#include "config.h"
#include "connecteddevice.h"
#include "devicedatabasebackend.h"
#include "devicemanager.h"
#include "devicekitlister.h"
#include "filesystemdevice.h"
#include "giolister.h"
#include "gpoddevice.h"
#include "core/taskmanager.h"
#include "core/utilities.h"
#include "ui/iconloader.h"
#include <QIcon>
#include <QPainter>
#include <QUrl>
const int DeviceManager::kDeviceIconSize = 32;
const int DeviceManager::kDeviceIconOverlaySize = 16;
@ -99,6 +102,15 @@ DeviceManager::DeviceManager(BackgroundThread<Database>* database,
#ifdef Q_WS_X11
AddLister(new DeviceKitLister);
#endif
#ifdef HAVE_GIO
AddLister(new GioLister);
#endif
AddDeviceClass<FilesystemDevice>();
#ifdef HAVE_LIBGPOD
AddDeviceClass<GPodDevice>();
#endif
}
DeviceManager::~DeviceManager() {
@ -262,8 +274,10 @@ boost::shared_ptr<ConnectedDevice> DeviceManager::Connect(int row) {
if (info.device_) // Already connected
return info.device_;
boost::shared_ptr<ConnectedDevice> ret;
if (!info.lister_) // Not physically connected
return boost::shared_ptr<ConnectedDevice>();
return ret;
bool first_time = (info.database_id_ == -1);
if (first_time) {
@ -271,12 +285,33 @@ boost::shared_ptr<ConnectedDevice> DeviceManager::Connect(int row) {
info.database_id_ = backend_->AddDevice(info.SaveToDb());
}
info.device_ = info.lister_->Connect(
info.unique_id_, this, info.database_id_, first_time);
connect(info.device_.get(), SIGNAL(TaskStarted(int)), SLOT(DeviceTaskStarted(int)));
connect(info.device_.get(), SIGNAL(Error(QString)), SIGNAL(Error(QString)));
// Get the device URL
QUrl url = info.lister_->MakeDeviceUrl(info.unique_id_);
if (url.isEmpty())
return ret;
return info.device_;
// Find a device class for this URL's scheme
if (!device_classes_.contains(url.scheme())) {
emit Error(tr("This type of device is not supported: %1").arg(url.toString()));
return ret;
}
QMetaObject meta_object = device_classes_.value(url.scheme());
QObject* instance = meta_object.newInstance(
Q_ARG(QUrl, url), Q_ARG(DeviceLister*, info.lister_),
Q_ARG(QString, info.unique_id_), Q_ARG(DeviceManager*, this),
Q_ARG(int, info.database_id_), Q_ARG(bool, first_time));
ret.reset(static_cast<ConnectedDevice*>(instance));
if (!ret) {
qWarning() << "Could not create device for" << url.toString();
} else {
info.device_ = ret;
connect(info.device_.get(), SIGNAL(TaskStarted(int)), SLOT(DeviceTaskStarted(int)));
connect(info.device_.get(), SIGNAL(Error(QString)), SIGNAL(Error(QString)));
}
return ret;
}
boost::shared_ptr<ConnectedDevice> DeviceManager::GetConnectedDevice(int row) const {

View File

@ -123,6 +123,7 @@ private:
};
void AddLister(DeviceLister* lister);
template <typename T> void AddDeviceClass();
DeviceDatabaseBackend::Device InfoToDatabaseDevice(const DeviceInfo& info) const;
@ -136,8 +137,20 @@ private:
QList<DeviceLister*> listers_;
QList<DeviceInfo> devices_;
QMultiMap<QString, QMetaObject> device_classes_;
// Map of task ID to device index
QMap<int, QPersistentModelIndex> active_tasks_;
};
template <typename T>
void DeviceManager::AddDeviceClass() {
QStringList schemes = T::url_schemes();
QMetaObject obj = T::staticMetaObject;
foreach (const QString& scheme, schemes) {
device_classes_.insert(scheme, obj);
}
}
#endif // DEVICEMANAGER_H

View File

@ -24,9 +24,10 @@
#include <QtDebug>
FilesystemDevice::FilesystemDevice(
const QString& mount_point, DeviceLister* lister, const QString& unique_id,
DeviceManager* manager, int database_id, bool first_time)
: ConnectedDevice(lister, unique_id, manager, database_id),
const QUrl& url, DeviceLister* lister,
const QString& unique_id, DeviceManager* manager,
int database_id, bool first_time)
: ConnectedDevice(url, lister, unique_id, manager, database_id, first_time),
watcher_(new BackgroundThreadImplementation<LibraryWatcher, LibraryWatcher>(this))
{
// Create the library watcher
@ -57,7 +58,7 @@ FilesystemDevice::FilesystemDevice(
backend_, SLOT(UpdateCompilations()));
connect(watcher, SIGNAL(ScanStarted(int)), SIGNAL(TaskStarted(int)));
InitBackendDirectory(mount_point, first_time);
InitBackendDirectory(url.toLocalFile(), first_time);
model_->Init();
}

View File

@ -27,11 +27,14 @@ class FilesystemDevice : public ConnectedDevice {
Q_OBJECT
public:
FilesystemDevice(const QString& mount_point, DeviceLister* lister,
const QString& unique_id, DeviceManager* manager,
int database_id, bool first_time);
Q_INVOKABLE FilesystemDevice(
const QUrl& url, DeviceLister* lister,
const QString& unique_id, DeviceManager* manager,
int database_id, bool first_time);
~FilesystemDevice();
static QStringList url_schemes() { return QStringList() << "file"; }
private:
BackgroundThread<LibraryWatcher>* watcher_;
};

109
src/devices/giolister.cpp Normal file
View File

@ -0,0 +1,109 @@
/* This file is part of Clementine.
Clementine 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 3 of the License, or
(at your option) any later version.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "giolister.h"
#include <QStringList>
#include <QtDebug>
GioLister::GioLister()
: monitor_(NULL)
{
}
void GioLister::Init() {
monitor_ = g_volume_monitor_get();
// Get things that are already mounted
GList* mounts = g_volume_monitor_get_mounts(monitor_);
for (; mounts ; mounts=mounts->next) {
GMount* mount = static_cast<GMount*>(mounts->data);
MountAdded(mount);
g_object_unref(mount);
}
g_list_free(mounts);
// Connect signals from the monitor
g_signal_connect(monitor_, "mount-added", G_CALLBACK(MountAddedCallback), this);
g_signal_connect(monitor_, "mount-changed", G_CALLBACK(MountChangedCallback), this);
g_signal_connect(monitor_, "mount-removed", G_CALLBACK(MountRemovedCallback), this);
}
GioLister::~GioLister() {
if (monitor_)
g_object_unref(monitor_);
}
QStringList GioLister::DeviceUniqueIDs() {
return QStringList();
}
QString GioLister::DeviceIcon(const QString &id) {
return QString();
}
QString GioLister::DeviceManufacturer(const QString &id) {
return QString();
}
QString GioLister::DeviceModel(const QString &id) {
return QString();
}
quint64 GioLister::DeviceCapacity(const QString &id) {
return 0;
}
quint64 GioLister::DeviceFreeSpace(const QString &id) {
return 0; // TODO
}
QString GioLister::MakeFriendlyName(const QString &id) {
return QString();
}
QVariantMap GioLister::DeviceHardwareInfo(const QString &id) {
return QVariantMap();
}
QUrl GioLister::MakeDeviceUrl(const QString &id) {
return QUrl();
}
void GioLister::MountAddedCallback(GVolumeMonitor*, GMount* m, gpointer d) {
static_cast<GioLister*>(d)->MountAdded(m);
}
void GioLister::MountChangedCallback(GVolumeMonitor*, GMount* m, gpointer d) {
static_cast<GioLister*>(d)->MountChanged(m);
}
void GioLister::MountRemovedCallback(GVolumeMonitor*, GMount* m, gpointer d) {
static_cast<GioLister*>(d)->MountRemoved(m);
}
void GioLister::MountAdded(GMount *mount) {
qDebug() << "mount added" << g_mount_get_name(mount);
}
void GioLister::MountChanged(GMount *mount) {
qDebug() << "mount changed" << g_mount_get_name(mount);
}
void GioLister::MountRemoved(GMount *mount) {
qDebug() << "mount removed" << g_mount_get_name(mount);
}

59
src/devices/giolister.h Normal file
View File

@ -0,0 +1,59 @@
/* This file is part of Clementine.
Clementine 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 3 of the License, or
(at your option) any later version.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GIOLISTER_H
#define GIOLISTER_H
#include "devicelister.h"
#include <gio/gio.h>
class GioLister : public DeviceLister {
Q_OBJECT
public:
GioLister();
~GioLister();
QStringList DeviceUniqueIDs();
QString DeviceIcon(const QString& id);
QString DeviceManufacturer(const QString& id);
QString DeviceModel(const QString& id);
quint64 DeviceCapacity(const QString& id);
quint64 DeviceFreeSpace(const QString& id);
QVariantMap DeviceHardwareInfo(const QString& id);
QString MakeFriendlyName(const QString &id);
QUrl MakeDeviceUrl(const QString &id);
protected:
void Init();
private:
void MountAdded(GMount* mount);
void MountChanged(GMount* mount);
void MountRemoved(GMount* mount);
static void MountAddedCallback(GVolumeMonitor*, GMount*, gpointer);
static void MountChangedCallback(GVolumeMonitor*, GMount*, gpointer);
static void MountRemovedCallback(GVolumeMonitor*, GMount*, gpointer);
private:
GVolumeMonitor* monitor_;
};
#endif // GIOLISTER_H

View File

@ -24,13 +24,14 @@
#include <gpod/itdb.h>
GPodDevice::GPodDevice(
const QString& mount_point, DeviceLister* lister, const QString& unique_id,
DeviceManager* manager, int database_id, bool first_time)
: ConnectedDevice(lister, unique_id, manager, database_id),
const QUrl& url, DeviceLister* lister,
const QString& unique_id, DeviceManager* manager,
int database_id, bool first_time)
: ConnectedDevice(url, lister, unique_id, manager, database_id, first_time),
loader_thread_(new QThread(this)),
loader_(new GPodLoader(mount_point, manager->task_manager(), backend_))
loader_(new GPodLoader(url.toLocalFile(), manager->task_manager(), backend_))
{
InitBackendDirectory(mount_point, first_time);
InitBackendDirectory(url.toLocalFile(), first_time);
model_->Init();
loader_->moveToThread(loader_thread_);

View File

@ -25,11 +25,14 @@ class GPodDevice : public ConnectedDevice {
Q_OBJECT
public:
GPodDevice(const QString& mount_point, DeviceLister* lister,
const QString& unique_id, DeviceManager* manager,
int database_id, bool first_time);
Q_INVOKABLE GPodDevice(
const QUrl& url, DeviceLister* lister,
const QString& unique_id, DeviceManager* manager,
int database_id, bool first_time);
~GPodDevice();
static QStringList url_schemes() { return QStringList() << "ipod"; }
private:
QThread* loader_thread_;
GPodLoader* loader_;

View File

@ -1530,6 +1530,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr ""
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "العنوان"

View File

@ -1534,6 +1534,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Tento proud je pouze pro předplatitele"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Titulek"

View File

@ -1537,6 +1537,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Denne stream er kun for betalende abonnenter"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Titel"

View File

@ -1538,6 +1538,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Nur für zahlende Kunden"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Titel"

View File

@ -1546,6 +1546,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Η ροή (stream) αυτή είναι μόνο για συνδρομητές"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Τίτλος"

View File

@ -1535,6 +1535,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "This stream is for paid subscribers only"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Title"

View File

@ -1532,6 +1532,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "This stream is for paid subscribers only"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Title"

View File

@ -1544,6 +1544,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Este flujo es solo para Suscriptores de Paga"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Título"

View File

@ -1532,6 +1532,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr ""
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Kappale"

View File

@ -1540,6 +1540,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Ce flux n'est accessible qu'aux abonnés ayant payé"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Titre"

View File

@ -1532,6 +1532,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Esta stream é só para asinantes"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Título"

View File

@ -1545,6 +1545,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Questo flusso è riservato ai soli abbonati"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Titolo"

View File

@ -1532,6 +1532,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr ""
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Аталуы"

View File

@ -1534,6 +1534,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Denne tjenesten er kun for betalende kunder"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Tittel"

View File

@ -1530,6 +1530,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr ""
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Títol"

View File

@ -1532,6 +1532,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Strumień wyłacznie dla płatnych subskrybentów"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Nazwa"

View File

@ -1542,6 +1542,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Esta emissão é apenas para assinantes"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Título"

View File

@ -1543,6 +1543,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Este canal é apenas para assinantes"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Tí­tulo"

View File

@ -1531,6 +1531,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr ""
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Titlu"

View File

@ -1537,6 +1537,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Этот поток только для платных подписчиков"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Название"

View File

@ -1541,6 +1541,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Tento stream je len pre platiacich odoberateľov"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Názov"

View File

@ -1537,6 +1537,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Den här strömmen är endast för betalande abonnenter"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Titel"

View File

@ -1532,6 +1532,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr ""
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Başlık"

View File

@ -1520,6 +1520,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr ""
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr ""

View File

@ -1541,6 +1541,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr "Цей потік лише для платних передплатників"
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "Назва"

View File

@ -1530,6 +1530,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr ""
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr "标题"

View File

@ -1530,6 +1530,10 @@ msgstr ""
msgid "This stream is for paid subscribers only"
msgstr ""
#, qt-format
msgid "This type of device is not supported: %1"
msgstr ""
msgid "Title"
msgstr ""