Initial commit.
This commit is contained in:
119
src/dialogs/about.cpp
Normal file
119
src/dialogs/about.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2013, Jonas Kvinge <jonas@strawbs.net>
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "about.h"
|
||||
#include "ui_about.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QPushButton>
|
||||
|
||||
About::About(QWidget *parent):QDialog(parent) {
|
||||
|
||||
authors_ \
|
||||
<< Person("Jonas Kvinge", "jonas@strawbs.net");
|
||||
|
||||
clementine_authors_
|
||||
<< Person("David Sansome", "me@davidsansome.com")
|
||||
<< Person("John Maguire", "john.maguire@gmail.com")
|
||||
<< Person(QString::fromUtf8("Paweł Bara"), "keirangtp@gmail.com")
|
||||
<< Person("Arnaud Bienner", "arnaud.bienner@gmail.com");
|
||||
|
||||
thanks_to_ \
|
||||
<< Person("Mark Kretschmann", "kretschmann@kde.org")
|
||||
<< Person("Max Howell", "max.howell@methylblue.com")
|
||||
<< Person(QString::fromUtf8("Bartłomiej Burdukiewicz"), "dev.strikeu@gmail.com")
|
||||
<< Person("Jakub Stachowski", "qbast@go2.pl")
|
||||
<< Person("Paul Cifarelli", "paul@cifarelli.net")
|
||||
<< Person("Felipe Rivera", "liebremx@users.sourceforge.net")
|
||||
<< Person("Alexander Peitz")
|
||||
<< Person("Artur Rona", "artur.rona@gmail.com")
|
||||
<< Person("Andreas Muttscheller", "asfa194@gmail.com");
|
||||
|
||||
QString Title = "";
|
||||
|
||||
ui_.setupUi(this);
|
||||
setWindowFlags(this->windowFlags()|Qt::WindowStaysOnTopHint);
|
||||
setWindowTitle(tr("About Strawberry"));
|
||||
|
||||
Title = QString("About Strawberry");
|
||||
|
||||
ui_.title->setText(Title);
|
||||
|
||||
QFont title_font;
|
||||
title_font.setBold(true);
|
||||
title_font.setPointSize(title_font.pointSize() + 4);
|
||||
ui_.title->setFont(title_font);
|
||||
|
||||
|
||||
|
||||
ui_.text->setWordWrap(true);
|
||||
ui_.text->setText(MakeHtml());
|
||||
|
||||
ui_.buttonBox->button(QDialogButtonBox::Close)->setShortcut(QKeySequence::Close);
|
||||
|
||||
}
|
||||
|
||||
QString About::MakeHtml() const {
|
||||
|
||||
QString ret = "";
|
||||
|
||||
ret = tr("<p>Version %1</p>").arg(QCoreApplication::applicationVersion());
|
||||
|
||||
ret += tr("<p>");
|
||||
ret += tr("Strawberry is a fork of Clementine created in 2013, it's written in C++ and Qt5. So far it works on Linux, it is currently untested on Mac OS X and Windows.<br />");
|
||||
ret += tr("The main goal was to create a player for playing local music files that looked a bit more like Amarok 1.4.");
|
||||
ret += tr("</p>");
|
||||
|
||||
//ret += tr("<p><a href=\"%1\">%2</a></p><p><b>%3:</b>").arg(kUrl, kUrl, tr("Authors"));
|
||||
|
||||
ret += QString("<p><b>%1</b>").arg(tr("Authors"));
|
||||
|
||||
for (const Person &person : authors_) {
|
||||
ret += "<br />" + MakeHtml(person);
|
||||
}
|
||||
|
||||
ret += QString("</p><p><b>%3:</b>").arg(tr("Clementine Authors"));
|
||||
|
||||
for (const Person &person : clementine_authors_) {
|
||||
ret += "<br />" + MakeHtml(person);
|
||||
}
|
||||
|
||||
ret += QString("</p><p><b>%3:</b>").arg(tr("Thanks to"));
|
||||
|
||||
for (const Person &person : thanks_to_) {
|
||||
ret += "<br />" + MakeHtml(person);
|
||||
}
|
||||
|
||||
ret += QString("<br />%1</p>").arg(tr("...and all the Amarok and Clementine contributors"));
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
QString About::MakeHtml(const Person &person) const {
|
||||
|
||||
if (person.email.isNull())
|
||||
return person.name;
|
||||
else
|
||||
return QString("%1 <<a href=\"mailto:%2\">%3</a>>").arg(person.name, person.email, person.email);
|
||||
}
|
||||
57
src/dialogs/about.h
Normal file
57
src/dialogs/about.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ABOUT_H
|
||||
#define ABOUT_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "ui_about.h"
|
||||
|
||||
class About : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
About(QWidget *parent = nullptr);
|
||||
|
||||
struct Person {
|
||||
Person(const QString &n, const QString &e = QString()) : name(n), email(e) {}
|
||||
|
||||
bool operator<(const Person& other) const { return name < other.name; }
|
||||
|
||||
QString name;
|
||||
QString email;
|
||||
};
|
||||
|
||||
private:
|
||||
QString MakeHtml() const;
|
||||
QString MakeHtml(const Person& person) const;
|
||||
|
||||
private:
|
||||
Ui::About ui_;
|
||||
|
||||
QList<Person> authors_;
|
||||
QList<Person> clementine_authors_;
|
||||
QList<Person> thanks_to_;
|
||||
};
|
||||
|
||||
#endif // ABOUT_H
|
||||
186
src/dialogs/about.ui
Normal file
186
src/dialogs/about.ui
Normal file
@@ -0,0 +1,186 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>About</class>
|
||||
<widget class="QDialog" name="About">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>650</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>650</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>650</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../../data/data.qrc">
|
||||
<normaloff>:/icons/64x64/strawberry.png</normaloff>:/icons/64x64/strawberry.png</iconset>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">#line {
|
||||
color: lightgrey;
|
||||
}
|
||||
</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_logo">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../../data/data.qrc">:/icons/64x64/strawberry.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>2</width>
|
||||
<height>2</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>1</width>
|
||||
<height>1</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_text">
|
||||
<item>
|
||||
<widget class="QLabel" name="title">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="text">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../data/data.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>About</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>257</x>
|
||||
<y>460</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>About</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>325</x>
|
||||
<y>460</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
71
src/dialogs/console.cpp
Normal file
71
src/dialogs/console.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QFont>
|
||||
#include <QScrollBar>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlRecord>
|
||||
|
||||
#include "console.h"
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/database.h"
|
||||
|
||||
Console::Console(Application *app, QWidget *parent) : QDialog(parent), app_(app) {
|
||||
|
||||
ui_.setupUi(this);
|
||||
connect(ui_.run, SIGNAL(clicked()), SLOT(RunQuery()));
|
||||
|
||||
QFont font("Monospace");
|
||||
font.setStyleHint(QFont::TypeWriter);
|
||||
|
||||
ui_.output->setFont(font);
|
||||
ui_.query->setFont(font);
|
||||
|
||||
}
|
||||
|
||||
void Console::RunQuery() {
|
||||
|
||||
QSqlDatabase db = app_->database()->Connect();
|
||||
QSqlQuery query = db.exec(ui_.query->text());
|
||||
//ui_.query->clear();
|
||||
|
||||
ui_.output->append("<b>> " + query.executedQuery() + "</b>");
|
||||
|
||||
query.next();
|
||||
|
||||
while (query.isValid()) {
|
||||
QSqlRecord record = query.record();
|
||||
QStringList values;
|
||||
for (int i = 0; i < record.count(); ++i) {
|
||||
values.append(record.value(i).toString());
|
||||
}
|
||||
|
||||
ui_.output->append(values.join("|"));
|
||||
|
||||
query.next();
|
||||
}
|
||||
|
||||
ui_.output->verticalScrollBar()->setValue(ui_.output->verticalScrollBar()->maximum());
|
||||
|
||||
}
|
||||
45
src/dialogs/console.h
Normal file
45
src/dialogs/console.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CONSOLE_H
|
||||
#define CONSOLE_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "ui_console.h"
|
||||
|
||||
class Application;
|
||||
|
||||
class Console : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Console(Application *app, QWidget *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void RunQuery();
|
||||
|
||||
private:
|
||||
Ui::Console ui_;
|
||||
Application *app_;
|
||||
};
|
||||
|
||||
#endif // CONSOLE_H
|
||||
47
src/dialogs/console.ui
Normal file
47
src/dialogs/console.ui
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Console</class>
|
||||
<widget class="QDialog" name="Console">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>545</width>
|
||||
<height>347</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Console</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="output"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="query"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="run">
|
||||
<property name="text">
|
||||
<string>Run</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>query</tabstop>
|
||||
<tabstop>run</tabstop>
|
||||
<tabstop>output</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
910
src/dialogs/edittagdialog.cpp
Normal file
910
src/dialogs/edittagdialog.cpp
Normal file
@@ -0,0 +1,910 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QFuture>
|
||||
#include <QLabel>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QShortcut>
|
||||
#include <QtConcurrentRun>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "edittagdialog.h"
|
||||
#include "trackselectiondialog.h"
|
||||
#include "ui_edittagdialog.h"
|
||||
#include "core/application.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/tagreaderclient.h"
|
||||
#include "core/utilities.h"
|
||||
#include "collection/collection.h"
|
||||
#include "collection/collectionbackend.h"
|
||||
#include "playlist/playlistdelegates.h"
|
||||
#include "covermanager/albumcovermanager.h"
|
||||
#include "covermanager/albumcoverloader.h"
|
||||
#include "covermanager/coverproviders.h"
|
||||
#include "covermanager/albumcoverchoicecontroller.h"
|
||||
#include "covermanager/coverfromurldialog.h"
|
||||
|
||||
const char *EditTagDialog::kHintText = QT_TR_NOOP("(different across multiple songs)");
|
||||
const char *EditTagDialog::kSettingsGroup = "EditTagDialog";
|
||||
|
||||
EditTagDialog::EditTagDialog(Application *app, QWidget *parent)
|
||||
: QDialog(parent),
|
||||
ui_(new Ui_EditTagDialog),
|
||||
app_(app),
|
||||
album_cover_choice_controller_(new AlbumCoverChoiceController(this)),
|
||||
loading_(false),
|
||||
ignore_edits_(false),
|
||||
#ifdef HAVE_GSTREAMER
|
||||
tag_fetcher_(new TagFetcher(this)),
|
||||
#endif
|
||||
cover_art_id_(0),
|
||||
cover_art_is_set_(false),
|
||||
results_dialog_(new TrackSelectionDialog(this))
|
||||
{
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
//QIcon nocover = IconLoader::Load("nocover");
|
||||
//cover_options_.default_output_image_ = AlbumCoverLoader::ScaleAndPad(cover_options_, nocover.pixmap(nocover.availableSizes().last()).toImage());
|
||||
cover_options_.default_output_image_ = AlbumCoverLoader::ScaleAndPad(cover_options_, QImage(":/pictures/noalbumart.png"));
|
||||
|
||||
connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64,QImage,QImage)), SLOT(ArtLoaded(quint64,QImage,QImage)));
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
connect(tag_fetcher_, SIGNAL(ResultAvailable(Song, SongList)), results_dialog_, SLOT(FetchTagFinished(Song, SongList)), Qt::QueuedConnection);
|
||||
connect(tag_fetcher_, SIGNAL(Progress(Song,QString)), results_dialog_, SLOT(FetchTagProgress(Song,QString)));
|
||||
connect(results_dialog_, SIGNAL(SongChosen(Song, Song)), SLOT(FetchTagSongChosen(Song, Song)));
|
||||
connect(results_dialog_, SIGNAL(finished(int)), tag_fetcher_, SLOT(Cancel()));
|
||||
#endif
|
||||
|
||||
album_cover_choice_controller_->SetApplication(app_);
|
||||
|
||||
ui_->setupUi(this);
|
||||
ui_->splitter->setSizes(QList<int>() << 200 << width() - 200);
|
||||
ui_->loading_label->hide();
|
||||
|
||||
ui_->fetch_tag->setIcon(QPixmap::fromImage(QImage(":/pictures/musicbrainz.png")));
|
||||
|
||||
// An editable field is one that has a label as a buddy. The label is
|
||||
// important because it gets turned bold when the field is changed.
|
||||
for (QLabel *label : findChildren<QLabel*>()) {
|
||||
QWidget *widget = label->buddy();
|
||||
if (widget) {
|
||||
// Store information about the field
|
||||
fields_ << FieldData(label, widget, widget->objectName());
|
||||
|
||||
// Connect the Reset signal
|
||||
if (dynamic_cast<ExtendedEditor*>(widget)) {
|
||||
connect(widget, SIGNAL(Reset()), SLOT(ResetField()));
|
||||
}
|
||||
|
||||
// Connect the edited signal
|
||||
if (qobject_cast<QLineEdit*>(widget)) {
|
||||
connect(widget, SIGNAL(textChanged(QString)), SLOT(FieldValueEdited()));
|
||||
}
|
||||
else if (qobject_cast<QPlainTextEdit*>(widget)) {
|
||||
connect(widget, SIGNAL(textChanged()), SLOT(FieldValueEdited()));
|
||||
}
|
||||
else if (qobject_cast<QSpinBox*>(widget)) {
|
||||
connect(widget, SIGNAL(valueChanged(int)), SLOT(FieldValueEdited()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the colour of all the labels on the summary page
|
||||
const bool light = palette().color(QPalette::Base).value() > 128;
|
||||
const QColor color = palette().color(QPalette::WindowText);
|
||||
QPalette summary_label_palette(palette());
|
||||
summary_label_palette.setColor(QPalette::WindowText, light ? color.lighter(150) : color.darker(150));
|
||||
|
||||
for (QLabel *label : ui_->summary_tab->findChildren<QLabel*>()) {
|
||||
if (label->property("field_label").toBool()) {
|
||||
label->setPalette(summary_label_palette);
|
||||
}
|
||||
}
|
||||
|
||||
// Pretend the summary text is just a label
|
||||
ui_->summary->setMaximumHeight(ui_->art->height() - ui_->summary_art_button->height() - 4);
|
||||
|
||||
connect(ui_->song_list->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(SelectionChanged()));
|
||||
connect(ui_->button_box, SIGNAL(clicked(QAbstractButton*)), SLOT(ButtonClicked(QAbstractButton*)));
|
||||
//connect(ui_->rating, SIGNAL(RatingChanged(float)), SLOT(SongRated(float)));
|
||||
connect(ui_->playcount_reset, SIGNAL(clicked()), SLOT(ResetPlayCounts()));
|
||||
connect(ui_->fetch_tag, SIGNAL(clicked()), SLOT(FetchTag()));
|
||||
|
||||
// Set up the album cover menu
|
||||
cover_menu_ = new QMenu(this);
|
||||
|
||||
QList<QAction*> actions = album_cover_choice_controller_->GetAllActions();
|
||||
|
||||
connect(album_cover_choice_controller_->cover_from_file_action(), SIGNAL(triggered()), this, SLOT(LoadCoverFromFile()));
|
||||
connect(album_cover_choice_controller_->cover_to_file_action(), SIGNAL(triggered()), this, SLOT(SaveCoverToFile()));
|
||||
connect(album_cover_choice_controller_->cover_from_url_action(), SIGNAL(triggered()), this, SLOT(LoadCoverFromURL()));
|
||||
connect(album_cover_choice_controller_->search_for_cover_action(), SIGNAL(triggered()), this, SLOT(SearchForCover()));
|
||||
connect(album_cover_choice_controller_->unset_cover_action(), SIGNAL(triggered()), this, SLOT(UnsetCover()));
|
||||
connect(album_cover_choice_controller_->show_cover_action(), SIGNAL(triggered()), this, SLOT(ShowCover()));
|
||||
|
||||
cover_menu_->addActions(actions);
|
||||
|
||||
ui_->summary_art_button->setMenu(cover_menu_);
|
||||
|
||||
ui_->art->installEventFilter(this);
|
||||
ui_->art->setAcceptDrops(true);
|
||||
|
||||
// Add the next/previous buttons
|
||||
previous_button_ = new QPushButton(IconLoader::Load("go-previous"), tr("Previous"), this);
|
||||
next_button_ = new QPushButton(IconLoader::Load("go-next"), tr("Next"), this);
|
||||
ui_->button_box->addButton(previous_button_, QDialogButtonBox::ResetRole);
|
||||
ui_->button_box->addButton(next_button_, QDialogButtonBox::ResetRole);
|
||||
|
||||
connect(previous_button_, SIGNAL(clicked()), SLOT(PreviousSong()));
|
||||
connect(next_button_, SIGNAL(clicked()), SLOT(NextSong()));
|
||||
|
||||
// Set some shortcuts for the buttons
|
||||
new QShortcut(QKeySequence::Back, previous_button_, SLOT(click()));
|
||||
new QShortcut(QKeySequence::Forward, next_button_, SLOT(click()));
|
||||
new QShortcut(QKeySequence::MoveToPreviousPage, previous_button_, SLOT(click()));
|
||||
new QShortcut(QKeySequence::MoveToNextPage, next_button_, SLOT(click()));
|
||||
|
||||
// Show the shortcuts as tooltips
|
||||
previous_button_->setToolTip(QString("%1 (%2 / %3)").arg(
|
||||
previous_button_->text(),
|
||||
QKeySequence(QKeySequence::Back).toString(QKeySequence::NativeText),
|
||||
QKeySequence(QKeySequence::MoveToPreviousPage).toString(QKeySequence::NativeText)));
|
||||
next_button_->setToolTip(QString("%1 (%2 / %3)").arg(
|
||||
next_button_->text(),
|
||||
QKeySequence(QKeySequence::Forward).toString(QKeySequence::NativeText),
|
||||
QKeySequence(QKeySequence::MoveToNextPage).toString(QKeySequence::NativeText)));
|
||||
|
||||
new TagCompleter(app_->collection_backend(), Playlist::Column_Artist, ui_->artist);
|
||||
new TagCompleter(app_->collection_backend(), Playlist::Column_Album, ui_->album);
|
||||
new TagCompleter(app_->collection_backend(), Playlist::Column_AlbumArtist, ui_->albumartist);
|
||||
new TagCompleter(app_->collection_backend(), Playlist::Column_Genre, ui_->genre);
|
||||
new TagCompleter(app_->collection_backend(), Playlist::Column_Composer, ui_->composer);
|
||||
new TagCompleter(app_->collection_backend(), Playlist::Column_Performer, ui_->performer);
|
||||
new TagCompleter(app_->collection_backend(), Playlist::Column_Grouping, ui_->grouping);
|
||||
|
||||
}
|
||||
|
||||
EditTagDialog::~EditTagDialog() {
|
||||
delete ui_;
|
||||
}
|
||||
|
||||
bool EditTagDialog::SetLoading(const QString &message) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
const bool loading = !message.isEmpty();
|
||||
if (loading == loading_) return false;
|
||||
loading_ = loading;
|
||||
|
||||
ui_->button_box->setEnabled(!loading);
|
||||
ui_->tab_widget->setEnabled(!loading);
|
||||
ui_->song_list->setEnabled(!loading);
|
||||
ui_->fetch_tag->setEnabled(!loading);
|
||||
ui_->loading_label->setVisible(loading);
|
||||
ui_->loading_label->set_text(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<EditTagDialog::Data> EditTagDialog::LoadData(const SongList &songs) const {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
QList<Data> ret;
|
||||
|
||||
for (const Song &song : songs) {
|
||||
if (song.IsEditable()) {
|
||||
// Try reloading the tags from file
|
||||
Song copy(song);
|
||||
TagReaderClient::Instance()->ReadFileBlocking(copy.url().toLocalFile(), ©);
|
||||
|
||||
if (copy.is_valid()) {
|
||||
copy.MergeUserSetData(song);
|
||||
ret << Data(copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EditTagDialog::SetSongs(const SongList &s, const PlaylistItemList &items) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
// Show the loading indicator
|
||||
if (!SetLoading(tr("Loading tracks") + "...")) return;
|
||||
|
||||
data_.clear();
|
||||
playlist_items_ = items;
|
||||
ui_->song_list->clear();
|
||||
|
||||
// Reload tags in the background
|
||||
QFuture<QList<Data>> future = QtConcurrent::run(this, &EditTagDialog::LoadData, s);
|
||||
NewClosure(future, this, SLOT(SetSongsFinished(QFuture<QList<EditTagDialog::Data>>)), future);
|
||||
}
|
||||
|
||||
void EditTagDialog::SetSongsFinished(QFuture<QList<Data>> future) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
if (!SetLoading(QString())) return;
|
||||
|
||||
data_ = future.result();
|
||||
if (data_.count() == 0) {
|
||||
// If there were no valid songs, disable everything
|
||||
ui_->song_list->setEnabled(false);
|
||||
ui_->tab_widget->setEnabled(false);
|
||||
|
||||
// Show a summary with empty information
|
||||
UpdateSummaryTab(Song());
|
||||
ui_->tab_widget->setCurrentWidget(ui_->summary_tab);
|
||||
|
||||
SetSongListVisibility(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the filenames to the list
|
||||
for (const Data &data : data_) {
|
||||
ui_->song_list->addItem(data.current_.basefilename());
|
||||
}
|
||||
|
||||
// Select all
|
||||
ui_->song_list->setCurrentRow(0);
|
||||
ui_->song_list->selectAll();
|
||||
|
||||
// Hide the list if there's only one song in it
|
||||
SetSongListVisibility(data_.count() != 1);
|
||||
}
|
||||
|
||||
void EditTagDialog::SetSongListVisibility(bool visible) {
|
||||
ui_->song_list->setVisible(visible);
|
||||
previous_button_->setEnabled(visible);
|
||||
next_button_->setEnabled(visible);
|
||||
}
|
||||
|
||||
QVariant EditTagDialog::Data::value(const Song &song, const QString &id) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
if (id == "title") return song.title();
|
||||
if (id == "artist") return song.artist();
|
||||
if (id == "album") return song.album();
|
||||
if (id == "albumartist") return song.albumartist();
|
||||
if (id == "composer") return song.composer();
|
||||
if (id == "performer") return song.performer();
|
||||
if (id == "grouping") return song.grouping();
|
||||
if (id == "genre") return song.genre();
|
||||
if (id == "comment") return song.comment();
|
||||
if (id == "track") return song.track();
|
||||
if (id == "disc") return song.disc();
|
||||
if (id == "year") return song.year();
|
||||
qLog(Warning) << "Unknown ID" << id;
|
||||
return QVariant();
|
||||
|
||||
}
|
||||
|
||||
void EditTagDialog::Data::set_value(const QString &id, const QVariant &value) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
if (id == "title") current_.set_title(value.toString());
|
||||
else if (id == "artist") current_.set_artist(value.toString());
|
||||
else if (id == "album") current_.set_album(value.toString());
|
||||
else if (id == "albumartist") current_.set_albumartist(value.toString());
|
||||
else if (id == "composer") current_.set_composer(value.toString());
|
||||
else if (id == "performer") current_.set_performer(value.toString());
|
||||
else if (id == "grouping") current_.set_grouping(value.toString());
|
||||
else if (id == "genre") current_.set_genre(value.toString());
|
||||
else if (id == "comment") current_.set_comment(value.toString());
|
||||
else if (id == "track") current_.set_track(value.toInt());
|
||||
else if (id == "disc") current_.set_disc(value.toInt());
|
||||
else if (id == "year") current_.set_year(value.toInt());
|
||||
else qLog(Warning) << "Unknown ID" << id;
|
||||
|
||||
}
|
||||
|
||||
bool EditTagDialog::DoesValueVary(const QModelIndexList &sel, const QString &id) const {
|
||||
QVariant value = data_[sel.first().row()].current_value(id);
|
||||
for (int i = 1; i < sel.count(); ++i) {
|
||||
if (value != data_[sel[i].row()].current_value(id)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditTagDialog::IsValueModified(const QModelIndexList &sel, const QString &id) const {
|
||||
|
||||
for (const QModelIndex &i : sel) {
|
||||
if (data_[i.row()].original_value(id) != data_[i.row()].current_value(id))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditTagDialog::InitFieldValue(const FieldData &field, const QModelIndexList &sel) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
const bool varies = DoesValueVary(sel, field.id_);
|
||||
// const bool modified = IsValueModified(sel, field.id_);
|
||||
|
||||
if (ExtendedEditor *editor = dynamic_cast<ExtendedEditor*>(field.editor_)) {
|
||||
editor->clear();
|
||||
editor->clear_hint();
|
||||
if (varies) {
|
||||
editor->set_hint(tr(EditTagDialog::kHintText));
|
||||
}
|
||||
else {
|
||||
editor->set_text(data_[sel[0].row()].current_value(field.id_).toString());
|
||||
}
|
||||
}
|
||||
|
||||
UpdateModifiedField(field, sel);
|
||||
}
|
||||
|
||||
void EditTagDialog::UpdateFieldValue(const FieldData &field, const QModelIndexList &sel) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
// Get the value from the field
|
||||
QVariant value;
|
||||
if (ExtendedEditor *editor = dynamic_cast<ExtendedEditor*>(field.editor_)) {
|
||||
value = editor->text();
|
||||
}
|
||||
|
||||
// Did we get it?
|
||||
if (!value.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set it in each selected song
|
||||
for (const QModelIndex &i : sel) {
|
||||
data_[i.row()].set_value(field.id_, value);
|
||||
}
|
||||
|
||||
UpdateModifiedField(field, sel);
|
||||
}
|
||||
|
||||
void EditTagDialog::UpdateModifiedField(const FieldData &field, const QModelIndexList &sel) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
const bool modified = IsValueModified(sel, field.id_);
|
||||
|
||||
// Update the boldness
|
||||
QFont new_font(font());
|
||||
new_font.setBold(modified);
|
||||
field.label_->setFont(new_font);
|
||||
field.editor_->setFont(new_font);
|
||||
}
|
||||
|
||||
void EditTagDialog::ResetFieldValue(const FieldData &field, const QModelIndexList &sel) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
// Reset each selected song
|
||||
for (const QModelIndex &i : sel) {
|
||||
Data &data = data_[i.row()];
|
||||
data.set_value(field.id_, data.original_value(field.id_));
|
||||
}
|
||||
|
||||
// Reset the field
|
||||
InitFieldValue(field, sel);
|
||||
}
|
||||
|
||||
void EditTagDialog::SelectionChanged() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
if (sel.isEmpty())
|
||||
return;
|
||||
|
||||
// Set the editable fields
|
||||
UpdateUI(sel);
|
||||
|
||||
// If we're editing multiple songs then we have to disable certain tabs
|
||||
const bool multiple = sel.count() > 1;
|
||||
ui_->tab_widget->setTabEnabled(ui_->tab_widget->indexOf(ui_->summary_tab), !multiple);
|
||||
|
||||
if (!multiple) {
|
||||
const Song &song = data_[sel.first().row()].original_;
|
||||
UpdateSummaryTab(song);
|
||||
UpdateStatisticsTab(song);
|
||||
}
|
||||
}
|
||||
|
||||
void EditTagDialog::UpdateUI(const QModelIndexList &sel){
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
ignore_edits_ = true;
|
||||
for (const FieldData &field : fields_) {
|
||||
InitFieldValue(field, sel);
|
||||
}
|
||||
ignore_edits_ = false;
|
||||
}
|
||||
|
||||
static void SetText(QLabel *label, int value, const QString &suffix, const QString &def = QString()) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
label->setText(value <= 0 ? def : (QString::number(value) + " " + suffix));
|
||||
}
|
||||
|
||||
static void SetDate(QLabel *label, uint time) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
if (time == std::numeric_limits<uint>::max()) { // -1
|
||||
label->setText(QObject::tr("Unknown"));
|
||||
}
|
||||
else {
|
||||
label->setText(QDateTime::fromTime_t(time).toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)));
|
||||
}
|
||||
}
|
||||
|
||||
void EditTagDialog::UpdateSummaryTab(const Song &song) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
cover_art_id_ = app_->album_cover_loader()->LoadImageAsync(cover_options_, song);
|
||||
|
||||
QString summary = "<b>" + song.PrettyTitleWithArtist().toHtmlEscaped() + "</b><br/>";
|
||||
|
||||
bool art_is_set = true;
|
||||
if (song.has_manually_unset_cover()) {
|
||||
summary += tr("Cover art manually unset").toHtmlEscaped();
|
||||
art_is_set = false;
|
||||
}
|
||||
else if (!song.art_manual().isEmpty()) {
|
||||
summary += tr("Cover art set from %1").arg(song.art_manual()).toHtmlEscaped();
|
||||
}
|
||||
else if (song.has_embedded_cover()) {
|
||||
summary += tr("Cover art from embedded image");
|
||||
}
|
||||
else if (!song.art_automatic().isEmpty()) {
|
||||
summary += tr("Cover art loaded automatically from %1").arg(song.art_automatic()).toHtmlEscaped();
|
||||
}
|
||||
else {
|
||||
summary += tr("Cover art not set").toHtmlEscaped();
|
||||
art_is_set = false;
|
||||
}
|
||||
|
||||
ui_->summary->setText(summary);
|
||||
|
||||
album_cover_choice_controller_->unset_cover_action()->setEnabled(art_is_set);
|
||||
album_cover_choice_controller_->show_cover_action()->setEnabled(art_is_set);
|
||||
ui_->summary_art_button->setEnabled(song.id() != -1);
|
||||
|
||||
ui_->length->setText(Utilities::PrettyTimeNanosec(song.length_nanosec()));
|
||||
|
||||
SetText(ui_->samplerate, song.samplerate(), "Hz");
|
||||
SetText(ui_->bitdepth, song.bitdepth(), "Bit");
|
||||
SetText(ui_->bitrate, song.bitrate(), tr("kbps"));
|
||||
SetDate(ui_->mtime, song.mtime());
|
||||
SetDate(ui_->ctime, song.ctime());
|
||||
|
||||
if (song.filesize() == -1) {
|
||||
ui_->filesize->setText(tr("Unknown"));
|
||||
}
|
||||
else {
|
||||
ui_->filesize->setText(Utilities::PrettySize(song.filesize()));
|
||||
}
|
||||
|
||||
ui_->filetype->setText(song.TextForFiletype());
|
||||
|
||||
if (song.url().scheme() == "file")
|
||||
ui_->filename->setText(QDir::toNativeSeparators(song.url().toLocalFile()));
|
||||
else
|
||||
ui_->filename->setText(song.url().toString());
|
||||
|
||||
album_cover_choice_controller_->search_for_cover_action()->setEnabled(app_->cover_providers()->HasAnyProviders());
|
||||
|
||||
}
|
||||
|
||||
void EditTagDialog::UpdateStatisticsTab(const Song &song) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
ui_->playcount->setText(QString::number(qMax(0, song.playcount())));
|
||||
ui_->skipcount->setText(QString::number(qMax(0, song.skipcount())));
|
||||
|
||||
ui_->lastplayed->setText(song.lastplayed() <= 0 ? tr("Never") : QDateTime::fromTime_t(song.lastplayed()).toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)));
|
||||
}
|
||||
|
||||
void EditTagDialog::ArtLoaded(quint64 id, const QImage &scaled, const QImage &original) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
if (id == cover_art_id_) {
|
||||
ui_->art->setPixmap(QPixmap::fromImage(scaled));
|
||||
original_ = original;
|
||||
}
|
||||
}
|
||||
|
||||
void EditTagDialog::FieldValueEdited() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
if (ignore_edits_) return;
|
||||
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
if (sel.isEmpty())
|
||||
return;
|
||||
|
||||
QWidget *w = qobject_cast<QWidget*>(sender());
|
||||
|
||||
// Find the field
|
||||
for (const FieldData &field : fields_) {
|
||||
if (field.editor_ == w) {
|
||||
UpdateFieldValue(field, sel);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditTagDialog::ResetField() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
if (sel.isEmpty())
|
||||
return;
|
||||
|
||||
QWidget *w = qobject_cast<QWidget*>(sender());
|
||||
|
||||
// Find the field
|
||||
for (const FieldData &field : fields_) {
|
||||
if (field.editor_ == w) {
|
||||
ignore_edits_ = true;
|
||||
ResetFieldValue(field, sel);
|
||||
ignore_edits_ = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Song *EditTagDialog::GetFirstSelected() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
if (sel.isEmpty()) return nullptr;
|
||||
return &data_[sel.first().row()].original_;
|
||||
}
|
||||
|
||||
void EditTagDialog::LoadCoverFromFile() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
Song *song = GetFirstSelected();
|
||||
if (!song) return;
|
||||
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
|
||||
QString cover = album_cover_choice_controller_->LoadCoverFromFile(song);
|
||||
|
||||
if (!cover.isEmpty()) UpdateCoverOf(*song, sel, cover);
|
||||
}
|
||||
|
||||
void EditTagDialog::SaveCoverToFile() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
Song *song = GetFirstSelected();
|
||||
if (!song) return;
|
||||
|
||||
album_cover_choice_controller_->SaveCoverToFile(*song, original_);
|
||||
}
|
||||
|
||||
void EditTagDialog::LoadCoverFromURL() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
Song *song = GetFirstSelected();
|
||||
if (!song) return;
|
||||
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
|
||||
QString cover = album_cover_choice_controller_->LoadCoverFromURL(song);
|
||||
|
||||
if (!cover.isEmpty()) UpdateCoverOf(*song, sel, cover);
|
||||
}
|
||||
|
||||
void EditTagDialog::SearchForCover() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
Song *song = GetFirstSelected();
|
||||
if (!song) return;
|
||||
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
|
||||
QString cover = album_cover_choice_controller_->SearchForCover(song);
|
||||
|
||||
if (!cover.isEmpty()) UpdateCoverOf(*song, sel, cover);
|
||||
}
|
||||
|
||||
void EditTagDialog::UnsetCover() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
Song *song = GetFirstSelected();
|
||||
if (!song) return;
|
||||
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
|
||||
QString cover = album_cover_choice_controller_->UnsetCover(song);
|
||||
UpdateCoverOf(*song, sel, cover);
|
||||
}
|
||||
|
||||
void EditTagDialog::ShowCover() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
Song *song = GetFirstSelected();
|
||||
if (!song) {
|
||||
return;
|
||||
}
|
||||
|
||||
album_cover_choice_controller_->ShowCover(*song);
|
||||
}
|
||||
|
||||
void EditTagDialog::UpdateCoverOf(const Song &selected, const QModelIndexList &sel, const QString &cover) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
if (!selected.is_valid() || selected.id() == -1) return;
|
||||
|
||||
UpdateSummaryTab(selected);
|
||||
|
||||
// Now check if we have any other songs cached that share that artist and
|
||||
// album (and would therefore be changed as well)
|
||||
for (int i = 0; i < data_.count(); ++i) {
|
||||
if (i == sel.first().row()) // Already changed this one
|
||||
continue;
|
||||
|
||||
Song *other_song = &data_[i].original_;
|
||||
if (selected.artist() == other_song->artist() && selected.album() == other_song->album()) {
|
||||
other_song->set_art_manual(cover);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void EditTagDialog::NextSong() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
if (ui_->song_list->count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int row = (ui_->song_list->currentRow() + 1) % ui_->song_list->count();
|
||||
ui_->song_list->setCurrentRow(row);
|
||||
}
|
||||
|
||||
void EditTagDialog::PreviousSong() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
if (ui_->song_list->count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int row = (ui_->song_list->currentRow() - 1 + ui_->song_list->count()) % ui_->song_list->count();
|
||||
ui_->song_list->setCurrentRow(row);
|
||||
}
|
||||
|
||||
void EditTagDialog::ButtonClicked(QAbstractButton *button) {
|
||||
if (button == ui_->button_box->button(QDialogButtonBox::Discard)) {
|
||||
reject();
|
||||
}
|
||||
}
|
||||
|
||||
void EditTagDialog::SaveData(const QList<Data> &data) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
for (int i = 0; i < data.count(); ++i) {
|
||||
const Data &ref = data[i];
|
||||
if (ref.current_.IsMetadataEqual(ref.original_)) continue;
|
||||
|
||||
if (!TagReaderClient::Instance()->SaveFileBlocking(ref.current_.url().toLocalFile(), ref.current_)) {
|
||||
emit Error(tr("An error occurred writing metadata to '%1'").arg(ref.current_.url().toLocalFile()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditTagDialog::accept() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
// Show the loading indicator
|
||||
if (!SetLoading(tr("Saving tracks") + "...")) return;
|
||||
|
||||
// Save tags in the background
|
||||
QFuture<void> future = QtConcurrent::run(this, &EditTagDialog::SaveData, data_);
|
||||
NewClosure(future, this, SLOT(AcceptFinished()));
|
||||
}
|
||||
|
||||
void EditTagDialog::AcceptFinished() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
if (!SetLoading(QString())) return;
|
||||
QDialog::accept();
|
||||
|
||||
}
|
||||
|
||||
bool EditTagDialog::eventFilter(QObject *o, QEvent *e) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
if (o == ui_->art) {
|
||||
switch (e->type()) {
|
||||
case QEvent::MouseButtonRelease:
|
||||
cover_menu_->popup(static_cast<QMouseEvent*>(e)->globalPos());
|
||||
break;
|
||||
|
||||
case QEvent::DragEnter: {
|
||||
QDragEnterEvent *event = static_cast<QDragEnterEvent*>(e);
|
||||
if (AlbumCoverChoiceController::CanAcceptDrag(event)) {
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case QEvent::Drop: {
|
||||
const QDropEvent *event = static_cast<QDropEvent*>(e);
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
Song *song = GetFirstSelected();
|
||||
|
||||
const QString cover = album_cover_choice_controller_->SaveCover(song, event);
|
||||
if (!cover.isEmpty()) {
|
||||
UpdateCoverOf(*song, sel, cover);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditTagDialog::showEvent(QShowEvent *e) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
// Set the dialog's height to the smallest possible
|
||||
resize(width(), sizeHint().height());
|
||||
|
||||
// Restore the tab that was current last time.
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
ui_->tab_widget->setCurrentIndex(s.value("current_tab").toInt());
|
||||
|
||||
QDialog::showEvent(e);
|
||||
}
|
||||
|
||||
void EditTagDialog::hideEvent(QHideEvent *e) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
// Save the current tab
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("current_tab", ui_->tab_widget->currentIndex());
|
||||
|
||||
QDialog::hideEvent(e);
|
||||
}
|
||||
|
||||
void EditTagDialog::ResetPlayCounts() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
if (sel.isEmpty())
|
||||
return;
|
||||
Song *song = &data_[sel.first().row()].original_;
|
||||
if (!song->is_valid() || song->id() == -1) return;
|
||||
|
||||
if (QMessageBox::question(this, tr("Reset play counts"), tr("Are you sure you want to reset this song's statistics?"), QMessageBox::Reset, QMessageBox::Cancel) != QMessageBox::Reset) {
|
||||
return;
|
||||
}
|
||||
|
||||
song->set_playcount(0);
|
||||
song->set_skipcount(0);
|
||||
song->set_lastplayed(-1);
|
||||
|
||||
app_->collection_backend()->ResetStatisticsAsync(song->id());
|
||||
UpdateStatisticsTab(*song);
|
||||
}
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
void EditTagDialog::FetchTag() {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
|
||||
SongList songs;
|
||||
|
||||
for (const QModelIndex &index : sel) {
|
||||
Song song = data_[index.row()].original_;
|
||||
if (!song.is_valid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
songs << song;
|
||||
}
|
||||
|
||||
if (songs.isEmpty()) return;
|
||||
|
||||
results_dialog_->Init(songs);
|
||||
tag_fetcher_->StartFetch(songs);
|
||||
|
||||
results_dialog_->show();
|
||||
}
|
||||
|
||||
void EditTagDialog::FetchTagSongChosen(const Song &original_song, const Song &new_metadata) {
|
||||
|
||||
// qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
const QString filename = original_song.url().toLocalFile();
|
||||
|
||||
// Find the song with this filename
|
||||
auto data_it =
|
||||
std::find_if(data_.begin(), data_.end(), [&filename](const Data &d) {
|
||||
return d.original_.url().toLocalFile() == filename;
|
||||
});
|
||||
if (data_it == data_.end()) {
|
||||
qLog(Warning) << "Could not find song to filename: " << filename;
|
||||
return;
|
||||
}
|
||||
|
||||
// Update song data
|
||||
data_it->current_.set_title(new_metadata.title());
|
||||
data_it->current_.set_artist(new_metadata.artist());
|
||||
data_it->current_.set_album(new_metadata.album());
|
||||
data_it->current_.set_track(new_metadata.track());
|
||||
data_it->current_.set_year(new_metadata.year());
|
||||
|
||||
// Is it currently being displayed in the UI?
|
||||
if (ui_->song_list->currentRow() == std::distance(data_.begin(), data_it)) {
|
||||
// Yes! Additionally update UI
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
UpdateUI(sel);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
180
src/dialogs/edittagdialog.h
Normal file
180
src/dialogs/edittagdialog.h
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef EDITTAGDIALOG_H
|
||||
#define EDITTAGDIALOG_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QModelIndexList>
|
||||
|
||||
#include "core/song.h"
|
||||
#include "covermanager/albumcoverloaderoptions.h"
|
||||
#include "musicbrainz/tagfetcher.h"
|
||||
#include "playlist/playlistitem.h"
|
||||
#include "widgets/lineedit.h"
|
||||
#include "trackselectiondialog.h"
|
||||
|
||||
class Application;
|
||||
class AlbumCoverChoiceController;
|
||||
class CollectionBackend;
|
||||
class Ui_EditTagDialog;
|
||||
|
||||
class QAbstractButton;
|
||||
class QItemSelection;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
|
||||
class EditTagDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
EditTagDialog(Application *app, QWidget *parent = nullptr);
|
||||
~EditTagDialog();
|
||||
|
||||
static const char *kHintText;
|
||||
static const char *kSettingsGroup;
|
||||
|
||||
void SetSongs(const SongList &songs, const PlaylistItemList &items = PlaylistItemList());
|
||||
|
||||
PlaylistItemList playlist_items() const { return playlist_items_; }
|
||||
|
||||
void accept();
|
||||
|
||||
signals:
|
||||
void Error(const QString &message);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *o, QEvent *e);
|
||||
void showEvent(QShowEvent*);
|
||||
void hideEvent(QHideEvent*);
|
||||
|
||||
private:
|
||||
struct Data {
|
||||
Data(const Song &song = Song()) : original_(song), current_(song) {}
|
||||
|
||||
static QVariant value(const Song &song, const QString &id);
|
||||
QVariant original_value(const QString &id) const {
|
||||
return value(original_, id);
|
||||
}
|
||||
QVariant current_value(const QString &id) const {
|
||||
return value(current_, id);
|
||||
}
|
||||
|
||||
void set_value(const QString &id, const QVariant &value);
|
||||
|
||||
Song original_;
|
||||
Song current_;
|
||||
};
|
||||
|
||||
private slots:
|
||||
void SetSongsFinished(QFuture<QList<EditTagDialog::Data>> future);
|
||||
void AcceptFinished();
|
||||
|
||||
void SelectionChanged();
|
||||
void FieldValueEdited();
|
||||
void ResetField();
|
||||
void ButtonClicked(QAbstractButton *button);
|
||||
void ResetPlayCounts();
|
||||
#ifdef HAVE_GSTREAMER
|
||||
void FetchTag();
|
||||
void FetchTagSongChosen(const Song &original_song, const Song &new_metadata);
|
||||
#endif
|
||||
|
||||
void ArtLoaded(quint64 id, const QImage &scaled, const QImage &original);
|
||||
|
||||
void LoadCoverFromFile();
|
||||
void SaveCoverToFile();
|
||||
void LoadCoverFromURL();
|
||||
void SearchForCover();
|
||||
void UnsetCover();
|
||||
void ShowCover();
|
||||
|
||||
void PreviousSong();
|
||||
void NextSong();
|
||||
|
||||
private:
|
||||
struct FieldData {
|
||||
FieldData(QLabel *label = nullptr, QWidget *editor = nullptr, const QString &id = QString())
|
||||
: label_(label), editor_(editor), id_(id) {}
|
||||
|
||||
QLabel *label_;
|
||||
QWidget *editor_;
|
||||
QString id_;
|
||||
};
|
||||
|
||||
Song *GetFirstSelected();
|
||||
void UpdateCoverOf(const Song &selected, const QModelIndexList &sel, const QString &cover);
|
||||
|
||||
bool DoesValueVary(const QModelIndexList &sel, const QString &id) const;
|
||||
bool IsValueModified(const QModelIndexList &sel, const QString &id) const;
|
||||
|
||||
void InitFieldValue(const FieldData &field, const QModelIndexList &sel);
|
||||
void UpdateFieldValue(const FieldData &field, const QModelIndexList &sel);
|
||||
void UpdateModifiedField(const FieldData &field, const QModelIndexList &sel);
|
||||
void ResetFieldValue(const FieldData &field, const QModelIndexList &sel);
|
||||
|
||||
void UpdateSummaryTab(const Song &song);
|
||||
void UpdateStatisticsTab(const Song &song);
|
||||
|
||||
void UpdateUI(const QModelIndexList &sel);
|
||||
|
||||
bool SetLoading(const QString &message);
|
||||
void SetSongListVisibility(bool visible);
|
||||
|
||||
// Called by QtConcurrentRun
|
||||
QList<Data> LoadData(const SongList &songs) const;
|
||||
void SaveData(const QList<Data> &data);
|
||||
|
||||
private:
|
||||
Ui_EditTagDialog *ui_;
|
||||
|
||||
Application *app_;
|
||||
AlbumCoverChoiceController *album_cover_choice_controller_;
|
||||
|
||||
bool loading_;
|
||||
|
||||
PlaylistItemList playlist_items_;
|
||||
QList<Data> data_;
|
||||
QList<FieldData> fields_;
|
||||
|
||||
bool ignore_edits_;
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
TagFetcher *tag_fetcher_;
|
||||
#endif
|
||||
|
||||
AlbumCoverLoaderOptions cover_options_;
|
||||
quint64 cover_art_id_;
|
||||
bool cover_art_is_set_;
|
||||
|
||||
// A copy of the original, unscaled album cover.
|
||||
QImage original_;
|
||||
|
||||
QMenu *cover_menu_;
|
||||
|
||||
QPushButton *previous_button_;
|
||||
QPushButton *next_button_;
|
||||
|
||||
TrackSelectionDialog *results_dialog_;
|
||||
};
|
||||
|
||||
#endif // EDITTAGDIALOG_H
|
||||
917
src/dialogs/edittagdialog.ui
Normal file
917
src/dialogs/edittagdialog.ui
Normal file
@@ -0,0 +1,917 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>EditTagDialog</class>
|
||||
<widget class="QDialog" name="EditTagDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>863</width>
|
||||
<height>635</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Edit track information</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../../data/data.qrc">
|
||||
<normaloff>:/icons/64x64/strawberry.png</normaloff>:/icons/64x64/strawberry.png</iconset>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QListWidget" name="song_list">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QTabWidget" name="tab_widget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="summary_tab">
|
||||
<attribute name="title">
|
||||
<string>Summary</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="art">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>124</width>
|
||||
<height>124</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTextEdit" name="summary">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QTextEdit {
|
||||
background: transparent;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="summary_art_button">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Change cover art</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="playcount_reset">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reset play counts</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_3" rowstretch="0,0,0,0,0,0,0,0,0">
|
||||
<property name="horizontalSpacing">
|
||||
<number>18</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="length_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Length</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="length">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="playcount_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Play count</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="playcount">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="skipcount_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Skip count</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QLabel" name="skipcount">
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="bitrate_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Bit rate</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="bitrate">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel" name="lastplayed_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string comment="A playlist's tag.">Last played</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QLabel" name="lastplayed">
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="samplerate_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sample rate</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="samplerate">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="bitdepth_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Bit depth</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="bitdepth">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="filesize_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>File size</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="filesize">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="filetype_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>File type</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1" colspan="3">
|
||||
<widget class="QLabel" name="filetype">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="mtime_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Date modified</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1" colspan="3">
|
||||
<widget class="QLabel" name="mtime">
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="ctime_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Date created</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1" colspan="3">
|
||||
<widget class="QLabel" name="ctime">
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="filename_label">
|
||||
<property name="text">
|
||||
<string>File name</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" colspan="3">
|
||||
<widget class="QLineEdit" name="filename">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QLineEdit {
|
||||
background: transparent;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="frame">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tags_tab">
|
||||
<attribute name="title">
|
||||
<string>Edit tags</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="title_label">
|
||||
<property name="text">
|
||||
<string>Title</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>title</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="LineEdit" name="title">
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="has_clear_button" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="track_label">
|
||||
<property name="text">
|
||||
<string>Track</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>track</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="SpinBox" name="track">
|
||||
<property name="correctionMode">
|
||||
<enum>QAbstractSpinBox::CorrectToNearestValue</enum>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
<property name="has_clear_button" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="artist_label">
|
||||
<property name="text">
|
||||
<string>Artist</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>artist</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="LineEdit" name="artist">
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="has_clear_button" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="disc_label">
|
||||
<property name="text">
|
||||
<string>Disc</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>disc</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="SpinBox" name="disc">
|
||||
<property name="correctionMode">
|
||||
<enum>QAbstractSpinBox::CorrectToNearestValue</enum>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
<property name="has_clear_button" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="album_label">
|
||||
<property name="text">
|
||||
<string>Album</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>album</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="LineEdit" name="album">
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="has_clear_button" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel" name="year_label">
|
||||
<property name="text">
|
||||
<string>Year</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>year</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="SpinBox" name="year">
|
||||
<property name="correctionMode">
|
||||
<enum>QAbstractSpinBox::CorrectToNearestValue</enum>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
<property name="has_clear_button" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="albumartist_label">
|
||||
<property name="text">
|
||||
<string>Album artist</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>albumartist</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="LineEdit" name="albumartist">
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="has_clear_button" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="composer_label">
|
||||
<property name="text">
|
||||
<string>Composer</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>composer</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="LineEdit" name="composer">
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="has_clear_button" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="performer_label">
|
||||
<property name="text">
|
||||
<string>Performer</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>performer</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="LineEdit" name="performer">
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="has_clear_button" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="grouping_label">
|
||||
<property name="text">
|
||||
<string>Grouping</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>grouping</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="LineEdit" name="grouping">
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="has_clear_button" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="genre_label">
|
||||
<property name="text">
|
||||
<string>Genre</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>genre</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="LineEdit" name="genre">
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="has_clear_button" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QPushButton" name="fetch_tag">
|
||||
<property name="text">
|
||||
<string>Complete tags automatically</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../data/data.qrc">
|
||||
<normaloff>:/pictures/musicbrainz.png</normaloff>:/pictures/musicbrainz.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>38</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="comment_label">
|
||||
<property name="text">
|
||||
<string>Comment</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>comment</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1" colspan="3">
|
||||
<widget class="TextEdit" name="comment">
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="has_clear_button" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="BusyIndicator" name="loading_label"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="button_box">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Discard|QDialogButtonBox::Save</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>BusyIndicator</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/busyindicator.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>LineEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>widgets/lineedit.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>RatingWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/ratingwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>TextEdit</class>
|
||||
<extends>QTextEdit</extends>
|
||||
<header>widgets/lineedit.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>SpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>widgets/lineedit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../data/data.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>button_box</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>EditTagDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>65</x>
|
||||
<y>552</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>84</x>
|
||||
<y>533</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>button_box</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>EditTagDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>236</x>
|
||||
<y>542</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>242</x>
|
||||
<y>532</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
75
src/dialogs/errordialog.cpp
Normal file
75
src/dialogs/errordialog.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "errordialog.h"
|
||||
#include "ui_errordialog.h"
|
||||
|
||||
ErrorDialog::ErrorDialog(QWidget *parent)
|
||||
: QDialog(parent),
|
||||
ui_(new Ui_ErrorDialog)
|
||||
{
|
||||
|
||||
ui_->setupUi(this);
|
||||
|
||||
QIcon warning_icon(style()->standardIcon(QStyle::SP_MessageBoxWarning));
|
||||
QPixmap warning_pixmap(warning_icon.pixmap(48));
|
||||
|
||||
QPalette messages_palette(ui_->messages->palette());
|
||||
messages_palette.setColor(QPalette::Base, messages_palette.color(QPalette::Background));
|
||||
|
||||
ui_->messages->setPalette(messages_palette);
|
||||
ui_->icon->setPixmap(warning_pixmap);
|
||||
|
||||
}
|
||||
|
||||
ErrorDialog::~ErrorDialog() {
|
||||
delete ui_;
|
||||
}
|
||||
|
||||
void ErrorDialog::ShowMessage(const QString &message) {
|
||||
|
||||
current_messages_ << message;
|
||||
UpdateContent();
|
||||
|
||||
show();
|
||||
raise();
|
||||
activateWindow();
|
||||
|
||||
}
|
||||
|
||||
void ErrorDialog::hideEvent(QHideEvent *) {
|
||||
current_messages_.clear();
|
||||
UpdateContent();
|
||||
}
|
||||
|
||||
void ErrorDialog::UpdateContent() {
|
||||
|
||||
QString html;
|
||||
for (const QString& message : current_messages_) {
|
||||
if (!html.isEmpty())
|
||||
html += "<hr/>";
|
||||
html += message.toHtmlEscaped();
|
||||
}
|
||||
ui_->messages->setHtml(html);
|
||||
|
||||
}
|
||||
|
||||
52
src/dialogs/errordialog.h
Normal file
52
src/dialogs/errordialog.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ERRORDIALOG_H
|
||||
#define ERRORDIALOG_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class Ui_ErrorDialog;
|
||||
|
||||
class ErrorDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ErrorDialog(QWidget *parent = nullptr);
|
||||
~ErrorDialog();
|
||||
|
||||
public slots:
|
||||
void ShowMessage(const QString& message);
|
||||
|
||||
protected:
|
||||
void hideEvent(QHideEvent *);
|
||||
|
||||
private:
|
||||
void UpdateContent();
|
||||
|
||||
Ui_ErrorDialog *ui_;
|
||||
|
||||
QStringList current_messages_;
|
||||
};
|
||||
|
||||
#endif // ERRORDIALOG_H
|
||||
|
||||
107
src/dialogs/errordialog.ui
Normal file
107
src/dialogs/errordialog.ui
Normal file
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ErrorDialog</class>
|
||||
<widget class="QDialog" name="ErrorDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>411</width>
|
||||
<height>180</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Strawberry Error</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../../data/data.qrc">
|
||||
<normaloff>:/icons/64x64/strawberry.png</normaloff>:/icons/64x64/strawberry.png</iconset>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="icon"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="messages">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../data/data.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ErrorDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ErrorDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
382
src/dialogs/organisedialog.cpp
Normal file
382
src/dialogs/organisedialog.cpp
Normal file
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "organisedialog.h"
|
||||
#include "ui_organisedialog.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QHash>
|
||||
#include <QMenu>
|
||||
#include <QPushButton>
|
||||
#include <QResizeEvent>
|
||||
#include <QSettings>
|
||||
#include <QSignalMapper>
|
||||
#include <QtConcurrentRun>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "organiseerrordialog.h"
|
||||
#include "core/musicstorage.h"
|
||||
#include "core/organise.h"
|
||||
#include "core/tagreaderclient.h"
|
||||
#include "core/utilities.h"
|
||||
#include "core/iconloader.h"
|
||||
|
||||
const char *OrganiseDialog::kDefaultFormat = "%artist/%album{ (Disc %disc)}/{%track - }%title.%extension";
|
||||
const char *OrganiseDialog::kSettingsGroup = "OrganiseDialog";
|
||||
|
||||
OrganiseDialog::OrganiseDialog(TaskManager *task_manager, QWidget *parent)
|
||||
: QDialog(parent),
|
||||
ui_(new Ui_OrganiseDialog),
|
||||
task_manager_(task_manager),
|
||||
total_size_(0),
|
||||
resized_by_user_(false) {
|
||||
|
||||
ui_->setupUi(this);
|
||||
connect(ui_->button_box->button(QDialogButtonBox::Reset), SIGNAL(clicked()), SLOT(Reset()));
|
||||
|
||||
ui_->aftercopying->setItemIcon(1, IconLoader::Load("edit-delete"));
|
||||
|
||||
// Valid tags
|
||||
QMap<QString, QString> tags;
|
||||
tags[tr("Title")] = "title";
|
||||
tags[tr("Album")] = "album";
|
||||
tags[tr("Artist")] = "artist";
|
||||
tags[tr("Artist's initial")] = "artistinitial";
|
||||
tags[tr("Album artist")] = "albumartist";
|
||||
tags[tr("Composer")] = "composer";
|
||||
tags[tr("Performer")] = "performer";
|
||||
tags[tr("Grouping")] = "grouping";
|
||||
tags[tr("Track")] = "track";
|
||||
tags[tr("Disc")] = "disc";
|
||||
tags[tr("Year")] = "year";
|
||||
tags[tr("Original year")] = "originalyear";
|
||||
tags[tr("Genre")] = "genre";
|
||||
tags[tr("Comment")] = "comment";
|
||||
tags[tr("Length")] = "length";
|
||||
tags[tr("Bitrate", "Refers to bitrate in file organise dialog.")] = "bitrate";
|
||||
tags[tr("Samplerate")] = "samplerate";
|
||||
tags[tr("File extension")] = "extension";
|
||||
|
||||
// Naming scheme input field
|
||||
new OrganiseFormat::SyntaxHighlighter(ui_->naming);
|
||||
|
||||
connect(ui_->destination, SIGNAL(currentIndexChanged(int)), SLOT(UpdatePreviews()));
|
||||
connect(ui_->naming, SIGNAL(textChanged()), SLOT(UpdatePreviews()));
|
||||
connect(ui_->replace_ascii, SIGNAL(toggled(bool)), SLOT(UpdatePreviews()));
|
||||
connect(ui_->replace_the, SIGNAL(toggled(bool)), SLOT(UpdatePreviews()));
|
||||
connect(ui_->replace_spaces, SIGNAL(toggled(bool)), SLOT(UpdatePreviews()));
|
||||
|
||||
// Get the titles of the tags to put in the insert menu
|
||||
QStringList tag_titles = tags.keys();
|
||||
qStableSort(tag_titles);
|
||||
|
||||
// Build the insert menu
|
||||
QMenu *tag_menu = new QMenu(this);
|
||||
QSignalMapper *tag_mapper = new QSignalMapper(this);
|
||||
for (const QString &title : tag_titles) {
|
||||
QAction *action = tag_menu->addAction(title, tag_mapper, SLOT(map()));
|
||||
tag_mapper->setMapping(action, tags[title]);
|
||||
}
|
||||
|
||||
connect(tag_mapper, SIGNAL(mapped(QString)), SLOT(InsertTag(QString)));
|
||||
ui_->insert->setMenu(tag_menu);
|
||||
}
|
||||
|
||||
OrganiseDialog::~OrganiseDialog() {
|
||||
delete ui_;
|
||||
}
|
||||
|
||||
void OrganiseDialog::SetDestinationModel(QAbstractItemModel *model, bool devices) {
|
||||
|
||||
ui_->destination->setModel(model);
|
||||
|
||||
ui_->eject_after->setVisible(devices);
|
||||
}
|
||||
|
||||
bool OrganiseDialog::SetSongs(const SongList &songs) {
|
||||
|
||||
total_size_ = 0;
|
||||
songs_.clear();
|
||||
|
||||
for (const Song &song : songs) {
|
||||
if (song.url().scheme() != "file") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (song.filesize() > 0) total_size_ += song.filesize();
|
||||
|
||||
songs_ << song;
|
||||
}
|
||||
|
||||
ui_->free_space->set_additional_bytes(total_size_);
|
||||
UpdatePreviews();
|
||||
SetLoadingSongs(false);
|
||||
|
||||
if (songs_future_.isRunning()) {
|
||||
songs_future_.cancel();
|
||||
}
|
||||
songs_future_ = QFuture<SongList>();
|
||||
|
||||
return songs_.count();
|
||||
|
||||
}
|
||||
|
||||
bool OrganiseDialog::SetUrls(const QList<QUrl> &urls) {
|
||||
|
||||
QStringList filenames;
|
||||
|
||||
// Only add file:// URLs
|
||||
for (const QUrl &url : urls) {
|
||||
if (url.scheme() == "file") {
|
||||
filenames << url.toLocalFile();
|
||||
}
|
||||
}
|
||||
|
||||
return SetFilenames(filenames);
|
||||
|
||||
}
|
||||
|
||||
bool OrganiseDialog::SetFilenames(const QStringList &filenames) {
|
||||
|
||||
songs_future_ = QtConcurrent::run(this, &OrganiseDialog::LoadSongsBlocking, filenames);
|
||||
NewClosure(songs_future_, [=]() { SetSongs(songs_future_.result()); });
|
||||
|
||||
SetLoadingSongs(true);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void OrganiseDialog::SetLoadingSongs(bool loading) {
|
||||
|
||||
if (loading) {
|
||||
ui_->preview_stack->setCurrentWidget(ui_->loading_page);
|
||||
ui_->button_box->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
}
|
||||
else {
|
||||
ui_->preview_stack->setCurrentWidget(ui_->preview_page);
|
||||
// The Ok button is enabled by UpdatePreviews
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SongList OrganiseDialog::LoadSongsBlocking(const QStringList &filenames) {
|
||||
|
||||
SongList songs;
|
||||
Song song;
|
||||
|
||||
QStringList filenames_copy = filenames;
|
||||
while (!filenames_copy.isEmpty()) {
|
||||
const QString filename = filenames_copy.takeFirst();
|
||||
|
||||
// If it's a directory, add all the files inside.
|
||||
if (QFileInfo(filename).isDir()) {
|
||||
const QDir dir(filename);
|
||||
for (const QString &entry : dir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Readable)) {
|
||||
filenames_copy << dir.filePath(entry);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
TagReaderClient::Instance()->ReadFileBlocking(filename, &song);
|
||||
if (song.is_valid()) songs << song;
|
||||
}
|
||||
|
||||
return songs;
|
||||
|
||||
}
|
||||
|
||||
void OrganiseDialog::SetCopy(bool copy) {
|
||||
ui_->aftercopying->setCurrentIndex(copy ? 0 : 1);
|
||||
}
|
||||
|
||||
void OrganiseDialog::InsertTag(const QString &tag) {
|
||||
ui_->naming->insertPlainText("%" + tag);
|
||||
}
|
||||
|
||||
Organise::NewSongInfoList OrganiseDialog::ComputeNewSongsFilenames(const SongList &songs, const OrganiseFormat &format) {
|
||||
|
||||
// Check if we will have multiple files with the same name.
|
||||
// If so, they will erase each other if the overwrite flag is set.
|
||||
// Better to rename them: e.g. foo.bar -> foo(2).bar
|
||||
QHash<QString, int> filenames;
|
||||
Organise::NewSongInfoList new_songs_info;
|
||||
|
||||
for (const Song &song : songs) {
|
||||
QString new_filename = format.GetFilenameForSong(song);
|
||||
if (filenames.contains(new_filename)) {
|
||||
QString song_number = QString::number(++filenames[new_filename]);
|
||||
new_filename = Utilities::PathWithoutFilenameExtension(new_filename) + "(" + song_number + ")." + QFileInfo(new_filename).suffix();
|
||||
}
|
||||
filenames.insert(new_filename, 1);
|
||||
new_songs_info << Organise::NewSongInfo(song, new_filename);
|
||||
}
|
||||
return new_songs_info;
|
||||
|
||||
}
|
||||
|
||||
void OrganiseDialog::UpdatePreviews() {
|
||||
|
||||
if (songs_future_.isRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QModelIndex destination = ui_->destination->model()->index(ui_->destination->currentIndex(), 0);
|
||||
std::shared_ptr<MusicStorage> storage;
|
||||
bool has_local_destination = false;
|
||||
|
||||
if (destination.isValid()) {
|
||||
storage = destination.data(MusicStorage::Role_Storage).value<std::shared_ptr<MusicStorage>>();
|
||||
if (storage) {
|
||||
has_local_destination = !storage->LocalPath().isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
// Update the free space bar
|
||||
quint64 capacity = destination.data(MusicStorage::Role_Capacity).toLongLong();
|
||||
quint64 free = destination.data(MusicStorage::Role_FreeSpace).toLongLong();
|
||||
|
||||
if (!capacity) {
|
||||
ui_->free_space->hide();
|
||||
}
|
||||
else {
|
||||
ui_->free_space->show();
|
||||
ui_->free_space->set_free_bytes(free);
|
||||
ui_->free_space->set_total_bytes(capacity);
|
||||
}
|
||||
|
||||
// Update the format object
|
||||
format_.set_format(ui_->naming->toPlainText());
|
||||
format_.set_replace_non_ascii(ui_->replace_ascii->isChecked());
|
||||
format_.set_replace_spaces(ui_->replace_spaces->isChecked());
|
||||
format_.set_replace_the(ui_->replace_the->isChecked());
|
||||
|
||||
const bool format_valid = !has_local_destination || format_.IsValid();
|
||||
|
||||
// Are we gonna enable the ok button?
|
||||
bool ok = format_valid && !songs_.isEmpty();
|
||||
if (capacity != 0 && total_size_ > free) ok = false;
|
||||
|
||||
ui_->button_box->button(QDialogButtonBox::Ok)->setEnabled(ok);
|
||||
if (!format_valid) return;
|
||||
|
||||
new_songs_info_ = ComputeNewSongsFilenames(songs_, format_);
|
||||
|
||||
// Update the previews
|
||||
ui_->preview->clear();
|
||||
ui_->preview_group->setVisible(has_local_destination);
|
||||
ui_->naming_group->setVisible(has_local_destination);
|
||||
if (has_local_destination) {
|
||||
for (const Organise::NewSongInfo &song_info : new_songs_info_) {
|
||||
QString filename = storage->LocalPath() + "/" + song_info.new_filename_;
|
||||
ui_->preview->addItem(QDir::toNativeSeparators(filename));
|
||||
}
|
||||
}
|
||||
|
||||
if (!resized_by_user_) {
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QSize OrganiseDialog::sizeHint() const { return QSize(650, 0); }
|
||||
|
||||
void OrganiseDialog::Reset() {
|
||||
|
||||
ui_->naming->setPlainText(kDefaultFormat);
|
||||
ui_->replace_ascii->setChecked(false);
|
||||
ui_->replace_spaces->setChecked(false);
|
||||
ui_->replace_the->setChecked(false);
|
||||
ui_->overwrite->setChecked(false);
|
||||
ui_->mark_as_listened->setChecked(false);
|
||||
ui_->eject_after->setChecked(false);
|
||||
|
||||
}
|
||||
|
||||
void OrganiseDialog::showEvent(QShowEvent*) {
|
||||
|
||||
resized_by_user_ = false;
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
ui_->naming->setPlainText(s.value("format", kDefaultFormat).toString());
|
||||
ui_->replace_ascii->setChecked(s.value("replace_ascii", false).toBool());
|
||||
ui_->replace_spaces->setChecked(s.value("replace_spaces", false).toBool());
|
||||
ui_->replace_the->setChecked(s.value("replace_the", false).toBool());
|
||||
ui_->overwrite->setChecked(s.value("overwrite", false).toBool());
|
||||
ui_->mark_as_listened->setChecked(s.value("mark_as_listened", false).toBool());
|
||||
ui_->eject_after->setChecked(s.value("eject_after", false).toBool());
|
||||
|
||||
QString destination = s.value("destination").toString();
|
||||
int index = ui_->destination->findText(destination);
|
||||
if (index != -1 && !destination.isEmpty()) {
|
||||
ui_->destination->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void OrganiseDialog::accept() {
|
||||
|
||||
QSettings s;
|
||||
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("format", ui_->naming->toPlainText());
|
||||
s.setValue("replace_ascii", ui_->replace_ascii->isChecked());
|
||||
s.setValue("replace_spaces", ui_->replace_spaces->isChecked());
|
||||
s.setValue("replace_the", ui_->replace_the->isChecked());
|
||||
s.setValue("overwrite", ui_->overwrite->isChecked());
|
||||
s.setValue("mark_as_listened", ui_->overwrite->isChecked());
|
||||
s.setValue("destination", ui_->destination->currentText());
|
||||
s.setValue("eject_after", ui_->eject_after->isChecked());
|
||||
|
||||
const QModelIndex destination = ui_->destination->model()->index(ui_->destination->currentIndex(), 0);
|
||||
std::shared_ptr<MusicStorage> storage = destination.data(MusicStorage::Role_StorageForceConnect).value<std::shared_ptr<MusicStorage>>();
|
||||
|
||||
if (!storage) return;
|
||||
|
||||
// It deletes itself when it's finished.
|
||||
const bool copy = ui_->aftercopying->currentIndex() == 0;
|
||||
Organise *organise = new Organise(task_manager_, storage, format_, copy, ui_->overwrite->isChecked(), ui_->mark_as_listened->isChecked(), new_songs_info_, ui_->eject_after->isChecked());
|
||||
connect(organise, SIGNAL(Finished(QStringList)), SLOT(OrganiseFinished(QStringList)));
|
||||
connect(organise, SIGNAL(FileCopied(int)), this, SIGNAL(FileCopied(int)));
|
||||
organise->Start();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void OrganiseDialog::OrganiseFinished(const QStringList &files_with_errors) {
|
||||
if (files_with_errors.isEmpty()) return;
|
||||
|
||||
error_dialog_.reset(new OrganiseErrorDialog);
|
||||
error_dialog_->Show(OrganiseErrorDialog::Type_Copy, files_with_errors);
|
||||
}
|
||||
|
||||
void OrganiseDialog::resizeEvent(QResizeEvent *e) {
|
||||
if (e->spontaneous()) {
|
||||
resized_by_user_ = true;
|
||||
}
|
||||
|
||||
QDialog::resizeEvent(e);
|
||||
}
|
||||
|
||||
110
src/dialogs/organisedialog.h
Normal file
110
src/dialogs/organisedialog.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ORGANISEDIALOG_H
|
||||
#define ORGANISEDIALOG_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QFuture>
|
||||
#include <QMap>
|
||||
#include <QUrl>
|
||||
|
||||
#include "gtest/gtest_prod.h"
|
||||
|
||||
#include "core/organise.h"
|
||||
#include "core/organiseformat.h"
|
||||
#include "core/song.h"
|
||||
|
||||
class CollectionWatcher;
|
||||
class OrganiseErrorDialog;
|
||||
class TaskManager;
|
||||
class Ui_OrganiseDialog;
|
||||
|
||||
class QAbstractItemModel;
|
||||
|
||||
class OrganiseDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OrganiseDialog(TaskManager *task_manager, QWidget *parent = nullptr);
|
||||
~OrganiseDialog();
|
||||
|
||||
static const char *kDefaultFormat;
|
||||
static const char *kSettingsGroup;
|
||||
|
||||
QSize sizeHint() const;
|
||||
|
||||
void SetDestinationModel(QAbstractItemModel *model, bool devices = false);
|
||||
|
||||
// These functions return true if any songs were actually added to the dialog.
|
||||
// SetSongs returns immediately, SetUrls and SetFilenames load the songs in
|
||||
// the background.
|
||||
bool SetSongs(const SongList &songs);
|
||||
bool SetUrls(const QList<QUrl> &urls);
|
||||
bool SetFilenames(const QStringList &filenames);
|
||||
|
||||
void SetCopy(bool copy);
|
||||
|
||||
signals:
|
||||
void FileCopied(int);
|
||||
|
||||
public slots:
|
||||
void accept();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *);
|
||||
void resizeEvent(QResizeEvent *);
|
||||
|
||||
private slots:
|
||||
void Reset();
|
||||
|
||||
void InsertTag(const QString &tag);
|
||||
void UpdatePreviews();
|
||||
|
||||
void OrganiseFinished(const QStringList &files_with_errors);
|
||||
|
||||
private:
|
||||
SongList LoadSongsBlocking(const QStringList &filenames);
|
||||
void SetLoadingSongs(bool loading);
|
||||
|
||||
static Organise::NewSongInfoList ComputeNewSongsFilenames(const SongList &songs, const OrganiseFormat &format);
|
||||
|
||||
Ui_OrganiseDialog *ui_;
|
||||
TaskManager *task_manager_;
|
||||
|
||||
OrganiseFormat format_;
|
||||
|
||||
QFuture<SongList> songs_future_;
|
||||
SongList songs_;
|
||||
Organise::NewSongInfoList new_songs_info_;
|
||||
quint64 total_size_;
|
||||
|
||||
std::unique_ptr<OrganiseErrorDialog> error_dialog_;
|
||||
|
||||
bool resized_by_user_;
|
||||
|
||||
FRIEND_TEST(OrganiseDialogTest, ComputeNewSongsFilenamesTest);
|
||||
};
|
||||
|
||||
#endif // ORGANISEDIALOG_H
|
||||
291
src/dialogs/organisedialog.ui
Normal file
291
src/dialogs/organisedialog.ui
Normal file
@@ -0,0 +1,291 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OrganiseDialog</class>
|
||||
<widget class="QDialog" name="OrganiseDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>588</width>
|
||||
<height>525</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Organise Files</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../../data/data.qrc">
|
||||
<normaloff>:/icons/64x64/strawberry.png</normaloff>:/icons/64x64/strawberry.png</iconset>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Destination</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="destination"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>After copying...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="aftercopying">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Keep the original files</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Delete the original files</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="FreeSpaceBar" name="free_space" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="eject_after">
|
||||
<property name="text">
|
||||
<string>Safely remove the device after copying</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="naming_group">
|
||||
<property name="title">
|
||||
<string>Naming options</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="LineTextEdit" name="naming">
|
||||
<property name="toolTip">
|
||||
<string><p>Tokens start with %, for example: %artist %album %title </p>
|
||||
|
||||
<p>If you surround sections of text that contain a token with curly-braces, that section will be hidden if the token is empty.</p></string>
|
||||
</property>
|
||||
<property name="lineWrapMode">
|
||||
<enum>QTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
<property name="acceptRichText">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="insert">
|
||||
<property name="text">
|
||||
<string>Insert...</string>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::InstantPopup</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="replace_the">
|
||||
<property name="text">
|
||||
<string>Ignore "The" in artist names</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="replace_spaces">
|
||||
<property name="text">
|
||||
<string>Replaces spaces with underscores</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="replace_ascii">
|
||||
<property name="text">
|
||||
<string>Restrict to ASCII characters</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="overwrite">
|
||||
<property name="text">
|
||||
<string>Overwrite existing files</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="mark_as_listened">
|
||||
<property name="text">
|
||||
<string>Mark as listened</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="preview_group">
|
||||
<property name="title">
|
||||
<string>Preview</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="preview_stack">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="preview_page">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="preview"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="loading_page">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>264</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="BusyIndicator" name="loading_indicator" native="true">
|
||||
<property name="text" stdset="0">
|
||||
<string>Loading...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>264</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="button_box">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>FreeSpaceBar</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/freespacebar.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>LineTextEdit</class>
|
||||
<extends>QTextEdit</extends>
|
||||
<header>widgets/linetextedit.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>BusyIndicator</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/busyindicator.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>destination</tabstop>
|
||||
<tabstop>aftercopying</tabstop>
|
||||
<tabstop>eject_after</tabstop>
|
||||
<tabstop>naming</tabstop>
|
||||
<tabstop>insert</tabstop>
|
||||
<tabstop>replace_the</tabstop>
|
||||
<tabstop>replace_spaces</tabstop>
|
||||
<tabstop>replace_ascii</tabstop>
|
||||
<tabstop>overwrite</tabstop>
|
||||
<tabstop>button_box</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../data/data.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>button_box</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>OrganiseDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>257</x>
|
||||
<y>487</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>button_box</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>OrganiseDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>325</x>
|
||||
<y>487</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
73
src/dialogs/organiseerrordialog.cpp
Normal file
73
src/dialogs/organiseerrordialog.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QUrl>
|
||||
|
||||
#include "organiseerrordialog.h"
|
||||
#include "ui_organiseerrordialog.h"
|
||||
|
||||
OrganiseErrorDialog::OrganiseErrorDialog(QWidget *parent) : QDialog(parent), ui_(new Ui_OrganiseErrorDialog) {
|
||||
|
||||
ui_->setupUi(this);
|
||||
|
||||
const int icon_size = style()->pixelMetric(QStyle::PM_MessageBoxIconSize, 0, this);
|
||||
QIcon icon = style()->standardIcon(QStyle::SP_MessageBoxCritical, 0, this);
|
||||
|
||||
ui_->icon->setPixmap(icon.pixmap(icon_size));
|
||||
|
||||
}
|
||||
|
||||
OrganiseErrorDialog::~OrganiseErrorDialog() {
|
||||
delete ui_;
|
||||
}
|
||||
|
||||
void OrganiseErrorDialog::Show(OperationType type, const SongList &songs_with_errors) {
|
||||
|
||||
QStringList files;
|
||||
for (const Song &song : songs_with_errors) {
|
||||
files << song.url().toLocalFile();
|
||||
}
|
||||
Show(type, files);
|
||||
|
||||
}
|
||||
|
||||
void OrganiseErrorDialog::Show(OperationType type, const QStringList &files_with_errors) {
|
||||
|
||||
QStringList sorted_files = files_with_errors;
|
||||
qStableSort(sorted_files);
|
||||
|
||||
switch (type) {
|
||||
case Type_Copy:
|
||||
setWindowTitle(tr("Error copying songs"));
|
||||
ui_->label->setText(tr("There were problems copying some songs. The following files could not be copied:"));
|
||||
break;
|
||||
|
||||
case Type_Delete:
|
||||
setWindowTitle(tr("Error deleting songs"));
|
||||
ui_->label->setText(tr("There were problems deleting some songs. The following files could not be deleted:"));
|
||||
break;
|
||||
}
|
||||
|
||||
ui_->list->addItems(sorted_files);
|
||||
|
||||
show();
|
||||
}
|
||||
51
src/dialogs/organiseerrordialog.h
Normal file
51
src/dialogs/organiseerrordialog.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ORGANISEERRORDIALOG_H
|
||||
#define ORGANISEERRORDIALOG_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "core/song.h"
|
||||
|
||||
class Ui_OrganiseErrorDialog;
|
||||
|
||||
class OrganiseErrorDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OrganiseErrorDialog(QWidget *parent = nullptr);
|
||||
~OrganiseErrorDialog();
|
||||
|
||||
enum OperationType {
|
||||
Type_Copy,
|
||||
Type_Delete,
|
||||
};
|
||||
|
||||
void Show(OperationType type, const SongList& songs_with_errors);
|
||||
void Show(OperationType type, const QStringList &files_with_errors);
|
||||
|
||||
private:
|
||||
Ui_OrganiseErrorDialog *ui_;
|
||||
};
|
||||
|
||||
#endif // ORGANISEERRORDIALOG_H
|
||||
91
src/dialogs/organiseerrordialog.ui
Normal file
91
src/dialogs/organiseerrordialog.ui
Normal file
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OrganiseErrorDialog</class>
|
||||
<widget class="QDialog" name="OrganiseErrorDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>779</width>
|
||||
<height>355</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="icon"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="list"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>OrganiseErrorDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>OrganiseErrorDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
316
src/dialogs/trackselectiondialog.cpp
Normal file
316
src/dialogs/trackselectiondialog.cpp
Normal file
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QFutureWatcher>
|
||||
#include <QPushButton>
|
||||
#include <QShortcut>
|
||||
#include <QTreeWidget>
|
||||
#include <QUrl>
|
||||
#include <QtConcurrentRun>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "trackselectiondialog.h"
|
||||
#include "ui_trackselectiondialog.h"
|
||||
|
||||
#include "core/tagreaderclient.h"
|
||||
#include "core/iconloader.h"
|
||||
|
||||
|
||||
TrackSelectionDialog::TrackSelectionDialog(QWidget *parent)
|
||||
: QDialog(parent),
|
||||
ui_(new Ui_TrackSelectionDialog),
|
||||
save_on_close_(false)
|
||||
{
|
||||
|
||||
// Setup dialog window
|
||||
ui_->setupUi(this);
|
||||
|
||||
connect(ui_->song_list, SIGNAL(currentRowChanged(int)), SLOT(UpdateStack()));
|
||||
connect(ui_->results, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(ResultSelected()));
|
||||
|
||||
ui_->splitter->setSizes(QList<int>() << 200 << width() - 200);
|
||||
SetLoading(QString());
|
||||
|
||||
// Add the next/previous buttons
|
||||
previous_button_ = new QPushButton(IconLoader::Load("go-previous"), tr("Previous"), this);
|
||||
next_button_ = new QPushButton(IconLoader::Load("go-next"), tr("Next"), this);
|
||||
ui_->button_box->addButton(previous_button_, QDialogButtonBox::ResetRole);
|
||||
ui_->button_box->addButton(next_button_, QDialogButtonBox::ResetRole);
|
||||
|
||||
connect(previous_button_, SIGNAL(clicked()), SLOT(PreviousSong()));
|
||||
connect(next_button_, SIGNAL(clicked()), SLOT(NextSong()));
|
||||
|
||||
// Set some shortcuts for the buttons
|
||||
new QShortcut(QKeySequence::Back, previous_button_, SLOT(click()));
|
||||
new QShortcut(QKeySequence::Forward, next_button_, SLOT(click()));
|
||||
new QShortcut(QKeySequence::MoveToPreviousPage, previous_button_, SLOT(click()));
|
||||
new QShortcut(QKeySequence::MoveToNextPage, next_button_, SLOT(click()));
|
||||
|
||||
// Resize columns
|
||||
ui_->results->setColumnWidth(0, 50); // Track column
|
||||
ui_->results->setColumnWidth(1, 50); // Year column
|
||||
ui_->results->setColumnWidth(2, 160); // Title column
|
||||
ui_->results->setColumnWidth(3, 160); // Artist column
|
||||
ui_->results->setColumnWidth(4, 160); // Album column
|
||||
|
||||
}
|
||||
|
||||
TrackSelectionDialog::~TrackSelectionDialog() {
|
||||
delete ui_;
|
||||
}
|
||||
|
||||
void TrackSelectionDialog::Init(const SongList &songs) {
|
||||
|
||||
ui_->song_list->clear();
|
||||
ui_->stack->setCurrentWidget(ui_->loading_page);
|
||||
data_.clear();
|
||||
|
||||
for (const Song &song : songs) {
|
||||
Data data;
|
||||
data.original_song_ = song;
|
||||
data_ << data;
|
||||
|
||||
QListWidgetItem *item = new QListWidgetItem(ui_->song_list);
|
||||
item->setText(QFileInfo(song.url().toLocalFile()).fileName());
|
||||
item->setForeground(palette().color(QPalette::Disabled, QPalette::Text));
|
||||
}
|
||||
|
||||
const bool multiple = songs.count() > 1;
|
||||
ui_->song_list->setVisible(multiple);
|
||||
next_button_->setEnabled(multiple);
|
||||
previous_button_->setEnabled(multiple);
|
||||
|
||||
ui_->song_list->setCurrentRow(0);
|
||||
|
||||
}
|
||||
|
||||
void TrackSelectionDialog::FetchTagProgress(const Song &original_song, const QString &progress) {
|
||||
|
||||
// Find the item with this filename
|
||||
int row = -1;
|
||||
for (int i = 0; i < data_.count(); ++i) {
|
||||
if (data_[i].original_song_.url() == original_song.url()) {
|
||||
row = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (row == -1) return;
|
||||
|
||||
data_[row].progress_string_ = progress;
|
||||
|
||||
// If it's the current item, update the display
|
||||
if (ui_->song_list->currentIndex().row() == row) {
|
||||
UpdateStack();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TrackSelectionDialog::FetchTagFinished(const Song &original_song, const SongList &songs_guessed) {
|
||||
|
||||
// Find the item with this filename
|
||||
int row = -1;
|
||||
for (int i = 0; i < data_.count(); ++i) {
|
||||
if (data_[i].original_song_.url() == original_song.url()) {
|
||||
row = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (row == -1) return;
|
||||
|
||||
// Set the color back to black
|
||||
ui_->song_list->item(row)->setForeground(palette().text());
|
||||
|
||||
// Add the results to the list
|
||||
data_[row].pending_ = false;
|
||||
data_[row].results_ = songs_guessed;
|
||||
|
||||
// If it's the current item, update the display
|
||||
if (ui_->song_list->currentIndex().row() == row) {
|
||||
UpdateStack();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TrackSelectionDialog::UpdateStack() {
|
||||
|
||||
const int row = ui_->song_list->currentRow();
|
||||
if (row < 0 || row >= data_.count()) return;
|
||||
|
||||
const Data &data = data_[row];
|
||||
|
||||
if (data.pending_) {
|
||||
ui_->stack->setCurrentWidget(ui_->loading_page);
|
||||
ui_->progress->set_text(data.progress_string_ + "...");
|
||||
return;
|
||||
}
|
||||
else if (data.results_.isEmpty()) {
|
||||
ui_->stack->setCurrentWidget(ui_->error_page);
|
||||
return;
|
||||
}
|
||||
ui_->stack->setCurrentWidget(ui_->results_page);
|
||||
|
||||
// Clear tree widget
|
||||
ui_->results->clear();
|
||||
|
||||
// Put the original tags at the top
|
||||
AddDivider(tr("Original tags"), ui_->results);
|
||||
AddSong(data.original_song_, -1, ui_->results);
|
||||
|
||||
// Fill tree view with songs
|
||||
AddDivider(tr("Suggested tags"), ui_->results);
|
||||
|
||||
int song_index = 0;
|
||||
for (const Song &song : data.results_) {
|
||||
AddSong(song, song_index++, ui_->results);
|
||||
}
|
||||
|
||||
// Find the item that was selected last time
|
||||
for (int i = 0; i < ui_->results->model()->rowCount(); ++i) {
|
||||
const QModelIndex index = ui_->results->model()->index(i, 0);
|
||||
const QVariant id = index.data(Qt::UserRole);
|
||||
if (!id.isNull() && id.toInt() == data.selected_result_) {
|
||||
ui_->results->setCurrentIndex(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrackSelectionDialog::AddDivider(const QString &text, QTreeWidget *parent) const {
|
||||
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(parent);
|
||||
item->setFirstColumnSpanned(true);
|
||||
item->setText(0, text);
|
||||
item->setFlags(Qt::NoItemFlags);
|
||||
item->setForeground(0, palette().color(QPalette::Disabled, QPalette::Text));
|
||||
|
||||
QFont bold_font(font());
|
||||
bold_font.setBold(true);
|
||||
item->setFont(0, bold_font);
|
||||
|
||||
}
|
||||
|
||||
void TrackSelectionDialog::AddSong(const Song &song, int result_index, QTreeWidget *parent) const {
|
||||
|
||||
QStringList values;
|
||||
values << ((song.track() > 0) ? QString::number(song.track()) : QString()) << ((song.year() > 0) ? QString::number(song.year()) : QString()) << song.title() << song.artist() << song.album();
|
||||
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(parent, values);
|
||||
item->setData(0, Qt::UserRole, result_index);
|
||||
item->setData(0, Qt::TextAlignmentRole, Qt::AlignRight);
|
||||
}
|
||||
|
||||
void TrackSelectionDialog::ResultSelected() {
|
||||
|
||||
if (!ui_->results->currentItem()) return;
|
||||
|
||||
const int song_row = ui_->song_list->currentRow();
|
||||
if (song_row == -1) return;
|
||||
|
||||
const int result_index = ui_->results->currentItem()->data(0, Qt::UserRole).toInt();
|
||||
data_[song_row].selected_result_ = result_index;
|
||||
|
||||
}
|
||||
|
||||
void TrackSelectionDialog::SetLoading(const QString &message) {
|
||||
|
||||
const bool loading = !message.isEmpty();
|
||||
|
||||
ui_->button_box->setEnabled(!loading);
|
||||
ui_->splitter->setEnabled(!loading);
|
||||
ui_->loading_label->setVisible(loading);
|
||||
ui_->loading_label->set_text(message);
|
||||
|
||||
}
|
||||
|
||||
void TrackSelectionDialog::SaveData(const QList<Data> &data) {
|
||||
|
||||
for (int i = 0; i < data.count(); ++i) {
|
||||
const Data &ref = data[i];
|
||||
if (ref.pending_ || ref.results_.isEmpty() || ref.selected_result_ == -1)
|
||||
continue;
|
||||
|
||||
const Song &new_metadata = ref.results_[ref.selected_result_];
|
||||
|
||||
Song copy(ref.original_song_);
|
||||
copy.set_title(new_metadata.title());
|
||||
copy.set_artist(new_metadata.artist());
|
||||
copy.set_album(new_metadata.album());
|
||||
copy.set_track(new_metadata.track());
|
||||
copy.set_year(new_metadata.year());
|
||||
|
||||
if (!TagReaderClient::Instance()->SaveFileBlocking(copy.url().toLocalFile(), copy)) {
|
||||
qLog(Warning) << "Failed to write new auto-tags to" << copy.url().toLocalFile();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TrackSelectionDialog::accept() {
|
||||
|
||||
if (save_on_close_) {
|
||||
SetLoading(tr("Saving tracks") + "...");
|
||||
|
||||
// Save tags in the background
|
||||
QFuture<void> future = QtConcurrent::run(&TrackSelectionDialog::SaveData, data_);
|
||||
QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
|
||||
watcher->setFuture(future);
|
||||
connect(watcher, SIGNAL(finished()), SLOT(AcceptFinished()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
|
||||
for (const Data &data : data_) {
|
||||
if (data.pending_ || data.results_.isEmpty() || data.selected_result_ == -1)
|
||||
continue;
|
||||
|
||||
const Song &new_metadata = data.results_[data.selected_result_];
|
||||
|
||||
emit SongChosen(data.original_song_, new_metadata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TrackSelectionDialog::AcceptFinished() {
|
||||
|
||||
QFutureWatcher<void> *watcher = dynamic_cast<QFutureWatcher<void>*>(sender());
|
||||
if (!watcher) return;
|
||||
watcher->deleteLater();
|
||||
|
||||
SetLoading(QString());
|
||||
QDialog::accept();
|
||||
|
||||
}
|
||||
|
||||
void TrackSelectionDialog::NextSong() {
|
||||
int row = (ui_->song_list->currentRow() + 1) % ui_->song_list->count();
|
||||
ui_->song_list->setCurrentRow(row);
|
||||
}
|
||||
|
||||
void TrackSelectionDialog::PreviousSong() {
|
||||
int row = (ui_->song_list->currentRow() - 1 + ui_->song_list->count()) % ui_->song_list->count();
|
||||
ui_->song_list->setCurrentRow(row);
|
||||
}
|
||||
93
src/dialogs/trackselectiondialog.h
Normal file
93
src/dialogs/trackselectiondialog.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TRACKSELECTIONDIALOG_H
|
||||
#define TRACKSELECTIONDIALOG_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "core/song.h"
|
||||
|
||||
class Ui_TrackSelectionDialog;
|
||||
class QTreeWidget;
|
||||
|
||||
class TrackSelectionDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TrackSelectionDialog(QWidget *parent = nullptr);
|
||||
~TrackSelectionDialog();
|
||||
|
||||
void set_save_on_close(bool save_on_close) { save_on_close_ = save_on_close; }
|
||||
|
||||
void Init(const SongList &songs);
|
||||
|
||||
public slots:
|
||||
void FetchTagProgress(const Song &original_song, const QString &progress);
|
||||
void FetchTagFinished(const Song &original_song, const SongList &songs_guessed);
|
||||
|
||||
// QDialog
|
||||
void accept();
|
||||
|
||||
signals:
|
||||
void SongChosen(const Song &original_song, const Song &new_metadata);
|
||||
|
||||
private slots:
|
||||
void UpdateStack();
|
||||
|
||||
void NextSong();
|
||||
void PreviousSong();
|
||||
|
||||
void ResultSelected();
|
||||
void AcceptFinished();
|
||||
|
||||
private:
|
||||
Ui_TrackSelectionDialog *ui_;
|
||||
|
||||
struct Data {
|
||||
Data() : pending_(true), selected_result_(0) {}
|
||||
|
||||
Song original_song_;
|
||||
bool pending_;
|
||||
QString progress_string_;
|
||||
SongList results_;
|
||||
int selected_result_;
|
||||
};
|
||||
|
||||
void AddDivider(const QString &text, QTreeWidget *parent) const;
|
||||
void AddSong(const Song &song, int result_index, QTreeWidget *parent) const;
|
||||
|
||||
void SetLoading(const QString &message);
|
||||
static void SaveData(const QList<Data> &data);
|
||||
|
||||
private:
|
||||
QList<Data> data_;
|
||||
|
||||
QPushButton *previous_button_;
|
||||
QPushButton *next_button_;
|
||||
|
||||
bool save_on_close_;
|
||||
};
|
||||
|
||||
#endif // TRACKSELECTIONDIALOG_H
|
||||
|
||||
|
||||
290
src/dialogs/trackselectiondialog.ui
Normal file
290
src/dialogs/trackselectiondialog.ui
Normal file
@@ -0,0 +1,290 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TrackSelectionDialog</class>
|
||||
<widget class="QDialog" name="TrackSelectionDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>773</width>
|
||||
<height>375</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Tag fetcher</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../../data/data.qrc">
|
||||
<normaloff>:/icons/64x64/strawberry.png</normaloff>:/icons/64x64/strawberry.png</iconset>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QListWidget" name="song_list">
|
||||
<property name="uniformItemSizes">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStackedWidget" name="stack">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="loading_page">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>133</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="BusyIndicator" name="progress" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>133</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="error_page">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>124</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QLabel { font-weight: bold; }</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sorry</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Strawberry was unable to find results for this file</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>124</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="results_page">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QLabel { font-weight: bold; }</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Select best possible match</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="results">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="headerDefaultSectionSize">
|
||||
<number>150</number>
|
||||
</attribute>
|
||||
<attribute name="headerMinimumSectionSize">
|
||||
<number>50</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Track</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Year</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Title</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Artist</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Album</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="BusyIndicator" name="loading_label" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="button_box">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>BusyIndicator</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/busyindicator.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../data/data.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>button_box</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>TrackSelectionDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>button_box</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>TrackSelectionDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
Reference in New Issue
Block a user