2010-06-26 00:01:47 +02:00
|
|
|
/* 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/>.
|
|
|
|
*/
|
|
|
|
|
2010-07-04 22:52:45 +02:00
|
|
|
#include "config.h"
|
2010-06-26 14:41:18 +02:00
|
|
|
#include "connecteddevice.h"
|
2010-07-04 01:00:07 +02:00
|
|
|
#include "devicedatabasebackend.h"
|
2010-06-26 14:41:18 +02:00
|
|
|
#include "devicemanager.h"
|
2010-06-26 00:36:21 +02:00
|
|
|
#include "devicekitlister.h"
|
2010-07-04 17:56:08 +02:00
|
|
|
#include "core/taskmanager.h"
|
2010-07-03 23:05:55 +02:00
|
|
|
#include "core/utilities.h"
|
|
|
|
#include "ui/iconloader.h"
|
2010-06-26 00:01:47 +02:00
|
|
|
|
2010-07-03 23:05:55 +02:00
|
|
|
#include <QIcon>
|
2010-07-04 13:34:25 +02:00
|
|
|
#include <QPainter>
|
2010-07-03 23:05:55 +02:00
|
|
|
|
2010-07-04 13:34:25 +02:00
|
|
|
const int DeviceManager::kDeviceIconSize = 32;
|
|
|
|
const int DeviceManager::kDeviceIconOverlaySize = 16;
|
2010-07-03 23:05:55 +02:00
|
|
|
|
|
|
|
DeviceManager::DeviceInfo::DeviceInfo()
|
|
|
|
: database_id_(-1),
|
2010-07-04 17:56:08 +02:00
|
|
|
lister_(NULL),
|
|
|
|
task_percentage_(-1)
|
2010-07-03 23:05:55 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2010-07-04 01:00:07 +02:00
|
|
|
DeviceDatabaseBackend::Device DeviceManager::DeviceInfo::SaveToDb() const {
|
|
|
|
DeviceDatabaseBackend::Device ret;
|
|
|
|
ret.friendly_name_ = friendly_name_;
|
|
|
|
ret.size_ = size_;
|
|
|
|
ret.unique_id_ = unique_id_;
|
|
|
|
ret.id_ = database_id_;
|
|
|
|
|
|
|
|
if (lister_)
|
2010-07-04 17:01:24 +02:00
|
|
|
ret.icon_name_ = lister_->DeviceIcon(unique_id_);
|
2010-07-04 01:00:07 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceManager::DeviceInfo::InitFromDb(const DeviceDatabaseBackend::Device &dev) {
|
|
|
|
database_id_ = dev.id_;
|
|
|
|
friendly_name_ = dev.friendly_name_;
|
|
|
|
unique_id_ = dev.unique_id_;
|
|
|
|
size_ = dev.size_;
|
|
|
|
LoadIcon(dev.icon_name_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceManager::DeviceInfo::LoadIcon(const QString &filename) {
|
|
|
|
// Try to load the icon with that exact name first
|
2010-07-04 13:34:25 +02:00
|
|
|
icon_name_ = filename;
|
|
|
|
icon_ = IconLoader::Load(icon_name_);
|
2010-07-04 01:00:07 +02:00
|
|
|
|
|
|
|
// If that failed than try to guess if it's a phone or ipod. Fall back on
|
|
|
|
// a usb memory stick icon.
|
|
|
|
if (icon_.isNull()) {
|
|
|
|
if (filename.contains("phone"))
|
2010-07-04 13:34:25 +02:00
|
|
|
icon_name_ = "phone";
|
2010-07-04 01:00:07 +02:00
|
|
|
else if (filename.contains("ipod") || filename.contains("apple"))
|
2010-07-04 13:34:25 +02:00
|
|
|
icon_name_ = "multimedia-player-ipod-standard-monochrome";
|
2010-07-04 01:00:07 +02:00
|
|
|
else
|
2010-07-04 13:34:25 +02:00
|
|
|
icon_name_ = "drive-removable-media-usb-pendrive";
|
|
|
|
icon_ = IconLoader::Load(icon_name_);
|
2010-07-04 01:00:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-07-03 23:05:55 +02:00
|
|
|
DeviceManager::DeviceManager(BackgroundThread<Database>* database,
|
|
|
|
TaskManager* task_manager, QObject *parent)
|
|
|
|
: QAbstractListModel(parent),
|
|
|
|
database_(database),
|
2010-07-04 13:34:25 +02:00
|
|
|
task_manager_(task_manager),
|
|
|
|
not_connected_overlay_(IconLoader::Load("edit-delete"))
|
2010-06-26 00:01:47 +02:00
|
|
|
{
|
2010-07-04 17:56:08 +02:00
|
|
|
connect(task_manager_, SIGNAL(TasksChanged()), SLOT(TasksChanged()));
|
|
|
|
|
2010-07-04 01:00:07 +02:00
|
|
|
// Create the backend in the database thread
|
|
|
|
backend_ = database_->CreateInThread<DeviceDatabaseBackend>();
|
|
|
|
backend_->Init(database_->Worker());
|
|
|
|
|
|
|
|
DeviceDatabaseBackend::DeviceList devices = backend_->GetAllDevices();
|
|
|
|
foreach (const DeviceDatabaseBackend::Device& device, devices) {
|
|
|
|
DeviceInfo info;
|
|
|
|
info.InitFromDb(device);
|
|
|
|
devices_ << info;
|
|
|
|
}
|
|
|
|
|
2010-06-26 14:57:00 +02:00
|
|
|
#ifdef Q_WS_X11
|
2010-06-26 14:41:18 +02:00
|
|
|
AddLister(new DeviceKitLister);
|
2010-06-26 14:57:00 +02:00
|
|
|
#endif
|
2010-06-26 14:41:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
DeviceManager::~DeviceManager() {
|
|
|
|
qDeleteAll(listers_);
|
2010-07-04 01:00:07 +02:00
|
|
|
backend_->deleteLater();
|
2010-06-26 14:41:18 +02:00
|
|
|
}
|
|
|
|
|
2010-07-03 23:05:55 +02:00
|
|
|
int DeviceManager::rowCount(const QModelIndex&) const {
|
|
|
|
return devices_.count();
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant DeviceManager::data(const QModelIndex& index, int role) const {
|
|
|
|
if (!index.isValid() || index.column() != 0)
|
|
|
|
return QVariant();
|
|
|
|
|
|
|
|
const DeviceInfo& info = devices_[index.row()];
|
|
|
|
|
|
|
|
switch (role) {
|
2010-07-04 13:34:25 +02:00
|
|
|
case Qt::DisplayRole: {
|
|
|
|
QString text = info.friendly_name_.isEmpty() ? info.unique_id_ : info.friendly_name_;
|
|
|
|
if (info.size_)
|
|
|
|
text = text + QString(" (%1)").arg(Utilities::PrettySize(info.size_));
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Qt::DecorationRole: {
|
|
|
|
QPixmap pixmap = info.icon_.pixmap(kDeviceIconSize);
|
|
|
|
|
|
|
|
if (!info.lister_) {
|
|
|
|
// Disconnected but remembered
|
|
|
|
QPainter p(&pixmap);
|
|
|
|
p.drawPixmap(kDeviceIconSize - kDeviceIconOverlaySize,
|
|
|
|
kDeviceIconSize - kDeviceIconOverlaySize,
|
|
|
|
not_connected_overlay_.pixmap(kDeviceIconOverlaySize));
|
|
|
|
}
|
|
|
|
|
|
|
|
return pixmap;
|
|
|
|
}
|
|
|
|
|
2010-07-04 17:01:24 +02:00
|
|
|
case Role_FriendlyName:
|
|
|
|
return info.friendly_name_;
|
2010-07-04 13:34:25 +02:00
|
|
|
|
|
|
|
case Role_UniqueId:
|
|
|
|
return info.unique_id_;
|
|
|
|
|
2010-07-04 17:01:24 +02:00
|
|
|
case Role_IconName:
|
|
|
|
return info.icon_name_;
|
|
|
|
|
|
|
|
case Role_Capacity:
|
|
|
|
return info.size_;
|
|
|
|
|
2010-07-04 13:34:25 +02:00
|
|
|
case Role_State:
|
|
|
|
if (info.device_)
|
|
|
|
return State_Connected;
|
|
|
|
if (info.lister_)
|
|
|
|
return State_NotConnected;
|
|
|
|
return State_Remembered;
|
|
|
|
|
2010-07-04 17:56:08 +02:00
|
|
|
case Role_UpdatingPercentage:
|
|
|
|
if (info.task_percentage_ == -1)
|
|
|
|
return QVariant();
|
|
|
|
return info.task_percentage_;
|
|
|
|
|
2010-07-04 13:34:25 +02:00
|
|
|
default:
|
|
|
|
return QVariant();
|
2010-07-03 23:05:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-26 14:41:18 +02:00
|
|
|
void DeviceManager::AddLister(DeviceLister *lister) {
|
2010-06-26 00:36:21 +02:00
|
|
|
listers_ << lister;
|
2010-07-03 23:05:55 +02:00
|
|
|
connect(lister, SIGNAL(DeviceAdded(QString)), SLOT(PhysicalDeviceAdded(QString)));
|
|
|
|
connect(lister, SIGNAL(DeviceRemoved(QString)), SLOT(PhysicalDeviceRemoved(QString)));
|
|
|
|
connect(lister, SIGNAL(DeviceChanged(QString)), SLOT(PhysicalDeviceChanged(QString)));
|
2010-06-26 00:01:47 +02:00
|
|
|
|
2010-06-26 00:36:21 +02:00
|
|
|
lister->Start();
|
2010-06-26 00:01:47 +02:00
|
|
|
}
|
|
|
|
|
2010-07-03 23:05:55 +02:00
|
|
|
int DeviceManager::FindDeviceById(const QString &id) const {
|
|
|
|
for (int i=0 ; i<devices_.count() ; ++i) {
|
|
|
|
if (devices_[i].unique_id_ == id)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceManager::PhysicalDeviceAdded(const QString &id) {
|
2010-06-26 14:41:18 +02:00
|
|
|
DeviceLister* lister = qobject_cast<DeviceLister*>(sender());
|
2010-06-26 00:01:47 +02:00
|
|
|
|
|
|
|
qDebug() << "Device added:" << id;
|
2010-06-26 14:41:18 +02:00
|
|
|
|
2010-07-03 23:05:55 +02:00
|
|
|
// Do we have this device already?
|
|
|
|
int i = FindDeviceById(id);
|
|
|
|
if (i == -1) {
|
|
|
|
DeviceInfo info;
|
|
|
|
info.lister_ = lister;
|
|
|
|
info.unique_id_ = id;
|
2010-07-04 17:01:24 +02:00
|
|
|
info.friendly_name_ = lister->MakeFriendlyName(id);
|
|
|
|
info.size_ = lister->DeviceCapacity(id);
|
|
|
|
info.LoadIcon(lister->DeviceIcon(id));
|
2010-07-03 23:05:55 +02:00
|
|
|
|
|
|
|
beginInsertRows(QModelIndex(), devices_.count(), devices_.count());
|
|
|
|
devices_ << info;
|
|
|
|
endInsertRows();
|
|
|
|
} else {
|
|
|
|
DeviceInfo& info = devices_[i];
|
|
|
|
|
2010-07-04 01:00:07 +02:00
|
|
|
info.lister_ = lister;
|
2010-07-04 13:43:17 +02:00
|
|
|
emit dataChanged(index(i, 0), index(i, 0));
|
2010-07-03 23:05:55 +02:00
|
|
|
}
|
2010-06-26 00:01:47 +02:00
|
|
|
}
|
|
|
|
|
2010-07-03 23:05:55 +02:00
|
|
|
void DeviceManager::PhysicalDeviceRemoved(const QString &id) {
|
2010-06-26 00:01:47 +02:00
|
|
|
qDebug() << "Device removed:" << id;
|
2010-07-03 23:05:55 +02:00
|
|
|
|
|
|
|
int i = FindDeviceById(id);
|
|
|
|
if (i == -1) {
|
|
|
|
// Shouldn't happen
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
DeviceInfo& info = devices_[i];
|
|
|
|
|
|
|
|
if (info.database_id_ != -1) {
|
|
|
|
// Keep the structure around, but just "disconnect" it
|
|
|
|
info.lister_ = NULL;
|
|
|
|
info.device_.reset();
|
|
|
|
|
|
|
|
emit dataChanged(index(i, 0), index(i, 0));
|
2010-07-04 02:33:34 +02:00
|
|
|
emit DeviceDisconnected(i);
|
2010-07-03 23:05:55 +02:00
|
|
|
} else {
|
|
|
|
// Remove the item from the model
|
|
|
|
beginRemoveRows(QModelIndex(), i, i);
|
|
|
|
devices_.removeAt(i);
|
2010-07-04 17:01:24 +02:00
|
|
|
|
|
|
|
foreach (const QModelIndex& idx, persistentIndexList()) {
|
|
|
|
if (idx.row() == i)
|
|
|
|
changePersistentIndex(idx, QModelIndex());
|
|
|
|
else if (idx.row() > i)
|
|
|
|
changePersistentIndex(idx, index(idx.row()-1, idx.column()));
|
|
|
|
}
|
|
|
|
|
2010-07-03 23:05:55 +02:00
|
|
|
endRemoveRows();
|
2010-06-26 14:41:18 +02:00
|
|
|
}
|
2010-06-26 00:01:47 +02:00
|
|
|
}
|
|
|
|
|
2010-07-03 23:05:55 +02:00
|
|
|
void DeviceManager::PhysicalDeviceChanged(const QString &id) {
|
2010-06-26 14:41:18 +02:00
|
|
|
DeviceLister* lister = qobject_cast<DeviceLister*>(sender());
|
2010-07-04 13:46:42 +02:00
|
|
|
Q_UNUSED(lister);
|
2010-06-26 00:01:47 +02:00
|
|
|
|
2010-07-03 23:05:55 +02:00
|
|
|
int i = FindDeviceById(id);
|
|
|
|
if (i == -1) {
|
|
|
|
// Shouldn't happen
|
|
|
|
return;
|
2010-06-26 00:01:47 +02:00
|
|
|
}
|
2010-07-03 23:05:55 +02:00
|
|
|
|
|
|
|
// TODO
|
2010-06-26 00:01:47 +02:00
|
|
|
}
|
2010-07-04 01:00:07 +02:00
|
|
|
|
|
|
|
boost::shared_ptr<ConnectedDevice> DeviceManager::Connect(int row) {
|
|
|
|
DeviceInfo& info = devices_[row];
|
|
|
|
if (info.device_) // Already connected
|
|
|
|
return info.device_;
|
|
|
|
|
|
|
|
if (!info.lister_) // Not physically connected
|
|
|
|
return boost::shared_ptr<ConnectedDevice>();
|
|
|
|
|
|
|
|
bool first_time = (info.database_id_ == -1);
|
|
|
|
if (first_time) {
|
|
|
|
// We haven't stored this device in the database before
|
|
|
|
info.database_id_ = backend_->AddDevice(info.SaveToDb());
|
|
|
|
}
|
|
|
|
|
2010-07-04 17:56:08 +02:00
|
|
|
info.device_ = info.lister_->Connect(
|
|
|
|
info.unique_id_, this, info.database_id_, first_time);
|
|
|
|
connect(info.device_.get(), SIGNAL(TaskStarted(int)), SLOT(DeviceTaskStarted(int)));
|
2010-07-04 22:52:45 +02:00
|
|
|
connect(info.device_.get(), SIGNAL(Error(QString)), SIGNAL(Error(QString)));
|
2010-07-04 17:56:08 +02:00
|
|
|
|
2010-07-04 01:00:07 +02:00
|
|
|
return info.device_;
|
|
|
|
}
|
|
|
|
|
|
|
|
boost::shared_ptr<ConnectedDevice> DeviceManager::GetConnectedDevice(int row) const {
|
|
|
|
return devices_[row].device_;
|
|
|
|
}
|
2010-07-04 02:33:34 +02:00
|
|
|
|
2010-07-04 14:56:49 +02:00
|
|
|
int DeviceManager::GetDatabaseId(int row) const {
|
|
|
|
return devices_[row].database_id_;
|
|
|
|
}
|
|
|
|
|
2010-07-04 17:01:24 +02:00
|
|
|
DeviceLister* DeviceManager::GetLister(int row) const {
|
|
|
|
return devices_[row].lister_;
|
|
|
|
}
|
|
|
|
|
2010-07-04 02:33:34 +02:00
|
|
|
void DeviceManager::Disconnect(int row) {
|
|
|
|
DeviceInfo& info = devices_[row];
|
|
|
|
if (!info.device_) // Already disconnected
|
|
|
|
return;
|
|
|
|
|
|
|
|
info.device_.reset();
|
|
|
|
emit DeviceDisconnected(row);
|
|
|
|
}
|
2010-07-04 14:56:49 +02:00
|
|
|
|
|
|
|
void DeviceManager::Forget(int row) {
|
|
|
|
DeviceInfo& info = devices_[row];
|
|
|
|
if (info.database_id_ == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (info.device_)
|
|
|
|
Disconnect(row);
|
|
|
|
|
|
|
|
backend_->RemoveDevice(info.database_id_);
|
|
|
|
info.database_id_ = -1;
|
|
|
|
|
|
|
|
if (!info.lister_) {
|
|
|
|
// It's not attached any more so remove it from the list
|
|
|
|
beginRemoveRows(QModelIndex(), row, row);
|
|
|
|
devices_.removeAt(row);
|
2010-07-04 17:01:24 +02:00
|
|
|
|
|
|
|
foreach (const QModelIndex& idx, persistentIndexList()) {
|
|
|
|
if (idx.row() == row)
|
|
|
|
changePersistentIndex(idx, QModelIndex());
|
|
|
|
else if (idx.row() > row)
|
|
|
|
changePersistentIndex(idx, index(idx.row()-1, idx.column()));
|
|
|
|
}
|
|
|
|
|
2010-07-04 14:56:49 +02:00
|
|
|
endRemoveRows();
|
|
|
|
} else {
|
2010-07-04 17:18:37 +02:00
|
|
|
// It's still attached, set the name and icon back to what they were
|
|
|
|
// originally
|
|
|
|
info.friendly_name_ = info.lister_->MakeFriendlyName(info.unique_id_);
|
|
|
|
info.icon_name_ = info.lister_->DeviceIcon(info.unique_id_);
|
|
|
|
info.LoadIcon(info.icon_name_);
|
|
|
|
|
2010-07-04 14:56:49 +02:00
|
|
|
dataChanged(index(row, 0), index(row, 0));
|
|
|
|
}
|
|
|
|
}
|
2010-07-04 17:18:37 +02:00
|
|
|
|
|
|
|
void DeviceManager::SetDeviceIdentity(int row, const QString &friendly_name,
|
|
|
|
const QString &icon_name) {
|
|
|
|
DeviceInfo& info = devices_[row];
|
|
|
|
info.friendly_name_ = friendly_name;
|
|
|
|
info.icon_name_ = icon_name;
|
|
|
|
info.LoadIcon(info.icon_name_);
|
|
|
|
|
|
|
|
emit dataChanged(index(row, 0), index(row, 0));
|
|
|
|
|
|
|
|
if (info.database_id_ != -1)
|
|
|
|
backend_->SetDeviceIdentity(info.database_id_, friendly_name, icon_name);
|
|
|
|
}
|
2010-07-04 17:56:08 +02:00
|
|
|
|
|
|
|
void DeviceManager::DeviceTaskStarted(int id) {
|
|
|
|
ConnectedDevice* device = qobject_cast<ConnectedDevice*>(sender());
|
|
|
|
if (!device)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (int i=0 ; i<devices_.count() ; ++i) {
|
|
|
|
DeviceInfo& info = devices_[i];
|
|
|
|
if (info.device_.get() == device) {
|
|
|
|
active_tasks_[id] = index(i);
|
|
|
|
info.task_percentage_ = 0;
|
|
|
|
emit dataChanged(index(i), index(i));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceManager::TasksChanged() {
|
|
|
|
QList<TaskManager::Task> tasks = task_manager_->GetTasks();
|
|
|
|
QList<QPersistentModelIndex> finished_tasks = active_tasks_.values();
|
|
|
|
|
|
|
|
foreach (const TaskManager::Task& task, tasks) {
|
|
|
|
if (!active_tasks_.contains(task.id))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
QPersistentModelIndex index = active_tasks_[task.id];
|
|
|
|
if (!index.isValid())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
DeviceInfo& info = devices_[index.row()];
|
|
|
|
if (task.progress_max)
|
|
|
|
info.task_percentage_ = float(task.progress) / task.progress_max * 100;
|
|
|
|
else
|
|
|
|
info.task_percentage_ = 0;
|
|
|
|
emit dataChanged(index, index);
|
|
|
|
finished_tasks.removeAll(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (const QPersistentModelIndex& index, finished_tasks) {
|
|
|
|
if (!index.isValid())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
DeviceInfo& info = devices_[index.row()];
|
|
|
|
info.task_percentage_ = -1;
|
|
|
|
emit dataChanged(index, index);
|
|
|
|
}
|
|
|
|
}
|