2018-02-27 18:06:05 +01:00
|
|
|
/*
|
|
|
|
* Strawberry Music Player
|
|
|
|
* This file was part of Clementine.
|
|
|
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
2021-03-20 21:14:47 +01:00
|
|
|
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
2018-02-27 18:06:05 +01:00
|
|
|
*
|
|
|
|
* Strawberry 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.
|
|
|
|
*
|
|
|
|
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
2018-08-09 18:39:44 +02:00
|
|
|
*
|
2018-02-27 18:06:05 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <functional>
|
|
|
|
#include <memory>
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <QtGlobal>
|
|
|
|
#include <QWidget>
|
|
|
|
#include <QDialog>
|
2018-02-27 18:06:05 +01:00
|
|
|
#include <QtConcurrentRun>
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <QFuture>
|
2021-01-30 21:53:53 +01:00
|
|
|
#include <QFutureWatcher>
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <QMap>
|
|
|
|
#include <QSize>
|
|
|
|
#include <QByteArray>
|
|
|
|
#include <QVariant>
|
|
|
|
#include <QString>
|
|
|
|
#include <QStringList>
|
|
|
|
#include <QComboBox>
|
|
|
|
#include <QGroupBox>
|
|
|
|
#include <QLineEdit>
|
|
|
|
#include <QPushButton>
|
|
|
|
#include <QRadioButton>
|
|
|
|
#include <QScrollBar>
|
|
|
|
#include <QListWidget>
|
|
|
|
#include <QListWidgetItem>
|
|
|
|
#include <QTableWidgetItem>
|
|
|
|
#include <QStackedWidget>
|
|
|
|
#include <QTableWidget>
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
#include "core/iconloader.h"
|
|
|
|
#include "core/musicstorage.h"
|
|
|
|
#include "widgets/freespacebar.h"
|
2018-02-27 18:06:05 +01:00
|
|
|
#include "connecteddevice.h"
|
|
|
|
#include "devicelister.h"
|
|
|
|
#include "devicemanager.h"
|
2018-05-01 00:41:33 +02:00
|
|
|
#include "deviceproperties.h"
|
2018-12-20 18:11:22 +01:00
|
|
|
#ifdef HAVE_GSTREAMER
|
|
|
|
# include "transcoder/transcoder.h"
|
|
|
|
#endif
|
2018-05-01 00:41:33 +02:00
|
|
|
#include "ui_deviceproperties.h"
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
DeviceProperties::DeviceProperties(QWidget *parent)
|
|
|
|
: QDialog(parent),
|
|
|
|
ui_(new Ui_DeviceProperties),
|
|
|
|
manager_(nullptr),
|
|
|
|
updating_formats_(false) {
|
2021-01-26 16:48:04 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
ui_->setupUi(this);
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::connect(ui_->open_device, &QPushButton::clicked, this, &DeviceProperties::OpenDevice);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
// Maximum height of the icon widget
|
2021-01-26 16:48:04 +01:00
|
|
|
ui_->icon->setMaximumHeight(ui_->icon->iconSize().height() + ui_->icon->horizontalScrollBar()->sizeHint().height() + ui_->icon->spacing() * 2 + 5);
|
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DeviceProperties::~DeviceProperties() { delete ui_; }
|
|
|
|
|
|
|
|
void DeviceProperties::SetDeviceManager(DeviceManager *manager) {
|
|
|
|
|
|
|
|
manager_ = manager;
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::connect(manager_, &DeviceManager::dataChanged, this, &DeviceProperties::ModelChanged);
|
|
|
|
QObject::connect(manager_, &DeviceManager::rowsInserted, this, &DeviceProperties::ModelChanged);
|
|
|
|
QObject::connect(manager_, &DeviceManager::rowsRemoved, this, &DeviceProperties::ModelChanged);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-01-21 17:44:37 +01:00
|
|
|
void DeviceProperties::ShowDevice(QModelIndex idx) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (ui_->icon->count() == 0) {
|
|
|
|
// Only load the icons the first time the dialog is shown
|
|
|
|
QStringList icon_names = QStringList()
|
|
|
|
<< "device"
|
|
|
|
<< "device-usb-drive"
|
|
|
|
<< "device-usb-flash"
|
2019-10-20 00:17:28 +02:00
|
|
|
<< "media-optical"
|
2018-02-27 18:06:05 +01:00
|
|
|
<< "device-ipod"
|
|
|
|
<< "device-ipod-nano"
|
|
|
|
<< "device-phone";
|
|
|
|
|
|
|
|
|
|
|
|
for (const QString &icon_name : icon_names) {
|
|
|
|
QListWidgetItem *item = new QListWidgetItem(IconLoader::Load(icon_name), QString(), ui_->icon);
|
|
|
|
item->setData(Qt::UserRole, icon_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_GSTREAMER
|
|
|
|
// Load the transcode formats the first time the dialog is shown
|
|
|
|
for (const TranscoderPreset &preset : Transcoder::GetAllPresets()) {
|
|
|
|
ui_->transcode_format->addItem(preset.name_, preset.type_);
|
|
|
|
}
|
|
|
|
ui_->transcode_format->model()->sort(0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-01-21 17:44:37 +01:00
|
|
|
index_ = idx;
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
// Basic information
|
|
|
|
ui_->name->setText(index_.data(DeviceManager::Role_FriendlyName).toString());
|
|
|
|
|
|
|
|
// Find the right icon
|
|
|
|
QString icon_name = index_.data(DeviceManager::Role_IconName).toString();
|
|
|
|
for (int i = 0; i < ui_->icon->count(); ++i) {
|
|
|
|
if (ui_->icon->item(i)->data(Qt::UserRole).toString() == icon_name) {
|
|
|
|
ui_->icon->setCurrentRow(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateHardwareInfo();
|
|
|
|
UpdateFormats();
|
|
|
|
|
|
|
|
show();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
void DeviceProperties::AddHardwareInfo(const int row, const QString &key, const QString &value) {
|
2018-02-27 18:06:05 +01:00
|
|
|
ui_->hardware_info->setItem(row, 0, new QTableWidgetItem(key));
|
|
|
|
ui_->hardware_info->setItem(row, 1, new QTableWidgetItem(value));
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceProperties::ModelChanged() {
|
|
|
|
|
|
|
|
if (!isVisible()) return;
|
|
|
|
|
|
|
|
if (!index_.isValid())
|
|
|
|
reject(); // Device went away
|
|
|
|
else {
|
|
|
|
UpdateHardwareInfo();
|
|
|
|
UpdateFormats();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceProperties::UpdateHardwareInfo() {
|
|
|
|
|
|
|
|
// Hardware information
|
|
|
|
QString id = index_.data(DeviceManager::Role_UniqueId).toString();
|
2019-01-21 17:44:37 +01:00
|
|
|
if (DeviceLister *lister = manager_->GetLister(index_)) {
|
2018-02-27 18:06:05 +01:00
|
|
|
QVariantMap info = lister->DeviceHardwareInfo(id);
|
|
|
|
|
|
|
|
// Remove empty items
|
2021-03-21 04:47:11 +01:00
|
|
|
QStringList keys = info.keys();
|
|
|
|
for (const QString &key : keys) {
|
2018-02-27 18:06:05 +01:00
|
|
|
if (info[key].isNull() || info[key].toString().isEmpty())
|
|
|
|
info.remove(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
ui_->hardware_info_stack->setCurrentWidget(ui_->hardware_info_page);
|
|
|
|
ui_->hardware_info->clear();
|
2021-03-21 18:53:02 +01:00
|
|
|
ui_->hardware_info->setRowCount(2 + static_cast<int>(info.count()));
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
int row = 0;
|
|
|
|
AddHardwareInfo(row++, tr("Model"), lister->DeviceModel(id));
|
|
|
|
AddHardwareInfo(row++, tr("Manufacturer"), lister->DeviceManufacturer(id));
|
2021-03-21 04:47:11 +01:00
|
|
|
keys = info.keys();
|
|
|
|
for (const QString &key : keys) {
|
2018-02-27 18:06:05 +01:00
|
|
|
AddHardwareInfo(row++, tr(key.toLatin1()), info[key].toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
ui_->hardware_info->sortItems(0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ui_->hardware_info_stack->setCurrentWidget(ui_->hardware_info_not_connected_page);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Size
|
|
|
|
quint64 total = index_.data(DeviceManager::Role_Capacity).toLongLong();
|
|
|
|
|
|
|
|
QVariant free_var = index_.data(DeviceManager::Role_FreeSpace);
|
|
|
|
if (free_var.isValid()) {
|
|
|
|
quint64 free = free_var.toLongLong();
|
|
|
|
|
|
|
|
ui_->free_space_bar->set_total_bytes(total);
|
|
|
|
ui_->free_space_bar->set_free_bytes(free);
|
|
|
|
ui_->free_space_bar->show();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ui_->free_space_bar->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceProperties::UpdateFormats() {
|
|
|
|
|
2019-01-21 17:44:37 +01:00
|
|
|
DeviceLister *lister = manager_->GetLister(index_);
|
|
|
|
std::shared_ptr<ConnectedDevice> device = manager_->GetConnectedDevice(index_);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
// Transcode mode
|
2018-05-01 00:41:33 +02:00
|
|
|
MusicStorage::TranscodeMode mode = MusicStorage::TranscodeMode(index_.data(DeviceManager::Role_TranscodeMode).toInt());
|
2018-02-27 18:06:05 +01:00
|
|
|
switch (mode) {
|
|
|
|
case MusicStorage::Transcode_Always:
|
|
|
|
ui_->transcode_all->setChecked(true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MusicStorage::Transcode_Never:
|
|
|
|
ui_->transcode_off->setChecked(true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MusicStorage::Transcode_Unsupported:
|
|
|
|
default:
|
|
|
|
ui_->transcode_unsupported->setChecked(true);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there's no lister then the device is physically disconnected
|
|
|
|
if (!lister) {
|
|
|
|
ui_->formats_stack->setCurrentWidget(ui_->formats_page_not_connected);
|
|
|
|
ui_->open_device->setEnabled(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there's a lister but no device then the user just needs to open the
|
|
|
|
// device. This will cause a rescan so we don't do it automatically.
|
|
|
|
if (!device) {
|
|
|
|
ui_->formats_stack->setCurrentWidget(ui_->formats_page_not_connected);
|
|
|
|
ui_->open_device->setEnabled(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!updating_formats_) {
|
2021-01-26 16:48:04 +01:00
|
|
|
// Get the device's supported formats list. This takes a long time and it blocks, so do it in the background.
|
2018-02-27 18:06:05 +01:00
|
|
|
supported_formats_.clear();
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
|
|
QFuture<bool> future = QtConcurrent::run(&ConnectedDevice::GetSupportedFiletypes, device, &supported_formats_);
|
|
|
|
#else
|
2018-02-27 18:06:05 +01:00
|
|
|
QFuture<bool> future = QtConcurrent::run(std::bind(&ConnectedDevice::GetSupportedFiletypes, device, &supported_formats_));
|
2021-01-26 16:48:04 +01:00
|
|
|
#endif
|
2021-01-30 21:53:53 +01:00
|
|
|
QFutureWatcher<bool> *watcher = new QFutureWatcher<bool>();
|
|
|
|
watcher->setFuture(future);
|
|
|
|
QObject::connect(watcher, &QFutureWatcher<bool>::finished, this, &DeviceProperties::UpdateFormatsFinished);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
ui_->formats_stack->setCurrentWidget(ui_->formats_page_loading);
|
|
|
|
updating_formats_ = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceProperties::accept() {
|
|
|
|
|
|
|
|
QDialog::accept();
|
|
|
|
|
|
|
|
// Transcode mode
|
|
|
|
MusicStorage::TranscodeMode mode = MusicStorage::Transcode_Unsupported;
|
|
|
|
if (ui_->transcode_all->isChecked())
|
|
|
|
mode = MusicStorage::Transcode_Always;
|
|
|
|
else if (ui_->transcode_off->isChecked())
|
|
|
|
mode = MusicStorage::Transcode_Never;
|
|
|
|
else if (ui_->transcode_unsupported->isChecked())
|
|
|
|
mode = MusicStorage::Transcode_Unsupported;
|
|
|
|
|
|
|
|
// Transcode format
|
|
|
|
Song::FileType format = Song::FileType(ui_->transcode_format->itemData(ui_->transcode_format->currentIndex()).toInt());
|
|
|
|
|
|
|
|
// By default no icon is selected and thus no current item is selected
|
|
|
|
QString icon_name;
|
|
|
|
if (ui_->icon->currentItem() != nullptr) {
|
|
|
|
icon_name = ui_->icon->currentItem()->data(Qt::UserRole).toString();
|
|
|
|
}
|
|
|
|
|
2019-01-21 17:44:37 +01:00
|
|
|
manager_->SetDeviceOptions(index_, ui_->name->text(), icon_name, mode, format);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-01-21 17:44:37 +01:00
|
|
|
void DeviceProperties::OpenDevice() { manager_->Connect(index_); }
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2021-01-30 21:53:53 +01:00
|
|
|
void DeviceProperties::UpdateFormatsFinished() {
|
|
|
|
|
|
|
|
QFutureWatcher<bool> *watcher = static_cast<QFutureWatcher<bool>*>(sender());
|
|
|
|
bool result = watcher->result();
|
|
|
|
watcher->deleteLater();
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
updating_formats_ = false;
|
|
|
|
|
2021-01-30 21:53:53 +01:00
|
|
|
if (!result) {
|
2018-02-27 18:06:05 +01:00
|
|
|
supported_formats_.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hide widgets if there are no supported types
|
|
|
|
ui_->supported_formats_container->setVisible(!supported_formats_.isEmpty());
|
|
|
|
ui_->transcode_unsupported->setEnabled(!supported_formats_.isEmpty());
|
|
|
|
|
|
|
|
if (ui_->transcode_unsupported->isChecked() && supported_formats_.isEmpty()) {
|
|
|
|
ui_->transcode_off->setChecked(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Populate supported types list
|
|
|
|
ui_->supported_formats->clear();
|
|
|
|
for (Song::FileType type : supported_formats_) {
|
|
|
|
QListWidgetItem *item = new QListWidgetItem(Song::TextForFiletype(type));
|
|
|
|
ui_->supported_formats->addItem(item);
|
|
|
|
}
|
|
|
|
ui_->supported_formats->sortItems();
|
|
|
|
|
|
|
|
#ifdef HAVE_GSTREAMER
|
|
|
|
// Set the format combobox item
|
|
|
|
TranscoderPreset preset = Transcoder::PresetForFileType(Song::FileType(index_.data(DeviceManager::Role_TranscodeFormat).toInt()));
|
2018-09-08 12:38:02 +02:00
|
|
|
if (preset.type_ == Song::FileType_Unknown) {
|
2018-05-01 00:41:33 +02:00
|
|
|
// The user hasn't chosen a format for this device yet,
|
|
|
|
// so work our way down a list of some preferred formats, picking the first one that is supported
|
2018-02-27 18:06:05 +01:00
|
|
|
preset = Transcoder::PresetForFileType(Transcoder::PickBestFormat(supported_formats_));
|
|
|
|
}
|
|
|
|
ui_->transcode_format->setCurrentIndex(ui_->transcode_format->findText(preset.name_));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ui_->formats_stack->setCurrentWidget(ui_->formats_page);
|
|
|
|
|
|
|
|
}
|