Add a "complete tags automatically" shortcut to the playlist context menu. Fixes issue #1532
This commit is contained in:
parent
99101c8442
commit
c2445edad5
@ -302,5 +302,6 @@
|
||||
<file>icons/22x22/network-server.png</file>
|
||||
<file>icons/32x32/network-server.png</file>
|
||||
<file>icons/48x48/network-server.png</file>
|
||||
<file>providers/musicbrainz.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
BIN
data/providers/musicbrainz.png
Normal file
BIN
data/providers/musicbrainz.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
@ -57,7 +57,6 @@ TagFetcher::TagFetcher(QObject *parent)
|
||||
}
|
||||
|
||||
TagFetcher::~TagFetcher() {
|
||||
delete network_;
|
||||
tp_Delete(pimp_);
|
||||
}
|
||||
|
||||
|
@ -64,8 +64,8 @@ EditTagDialog::EditTagDialog(QWidget* parent)
|
||||
connect(tag_fetcher_, SIGNAL(FetchFinished(QString, SongList)),
|
||||
results_dialog_, SLOT(FetchTagFinished(QString, SongList)),
|
||||
Qt::QueuedConnection);
|
||||
connect(results_dialog_, SIGNAL(SongChosen(QString, Song)),
|
||||
SLOT(FetchTagSongChosen(QString, Song)));
|
||||
connect(results_dialog_, SIGNAL(SongChosen(Song, Song)),
|
||||
SLOT(FetchTagSongChosen(Song, Song)));
|
||||
#endif
|
||||
|
||||
ui_->setupUi(this);
|
||||
@ -708,7 +708,8 @@ void EditTagDialog::FetchTag() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void EditTagDialog::FetchTagSongChosen(const QString& filename, const Song& new_metadata) {
|
||||
void EditTagDialog::FetchTagSongChosen(const Song& original_song, const Song& new_metadata) {
|
||||
const QString filename = original_song.filename();
|
||||
|
||||
// Find the song with this filename
|
||||
for (int i=0 ; i<data_.count() ; ++i) {
|
||||
|
@ -75,7 +75,7 @@ private slots:
|
||||
void SongRated(float rating);
|
||||
void ResetPlayCounts();
|
||||
void FetchTag();
|
||||
void FetchTagSongChosen(const QString& filename, const Song& new_metadata);
|
||||
void FetchTagSongChosen(const Song& original_song, const Song& new_metadata);
|
||||
|
||||
void ArtLoaded(quint64 id, const QImage& scaled, const QImage& original);
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
</widget>
|
||||
<widget class="QTabWidget" name="tab_widget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="summary_tab">
|
||||
<attribute name="title">
|
||||
@ -823,6 +823,16 @@
|
||||
<property name="text">
|
||||
<string>Complete tags automatically</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../data/data.qrc">
|
||||
<normaloff>:/providers/musicbrainz.png</normaloff>:/providers/musicbrainz.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>38</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "core/player.h"
|
||||
#include "core/songloader.h"
|
||||
#include "core/stylesheetloader.h"
|
||||
#include "core/tagfetcher.h"
|
||||
#include "core/taskmanager.h"
|
||||
#include "devices/devicemanager.h"
|
||||
#include "devices/devicestatefiltermodel.h"
|
||||
@ -78,6 +79,7 @@
|
||||
#include "ui/qtsystemtrayicon.h"
|
||||
#include "ui/settingsdialog.h"
|
||||
#include "ui/systemtrayicon.h"
|
||||
#include "ui/trackselectiondialog.h"
|
||||
#include "ui/windows7thumbbar.h"
|
||||
#include "version.h"
|
||||
#include "widgets/errordialog.h"
|
||||
@ -296,6 +298,7 @@ MainWindow::MainWindow(
|
||||
ui_->action_full_library_scan->setIcon(IconLoader::Load("view-refresh"));
|
||||
ui_->action_rain->setIcon(IconLoader::Load("weather-showers-scattered"));
|
||||
|
||||
|
||||
// File view connections
|
||||
connect(file_view_, SIGNAL(AddToPlaylist(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
|
||||
connect(file_view_, SIGNAL(PathChanged(QString)), SLOT(FilePathChanged(QString)));
|
||||
@ -322,6 +325,7 @@ MainWindow::MainWindow(
|
||||
connect(ui_->action_renumber_tracks, SIGNAL(triggered()), SLOT(RenumberTracks()));
|
||||
connect(ui_->action_selection_set_value, SIGNAL(triggered()), SLOT(SelectionSetValue()));
|
||||
connect(ui_->action_edit_value, SIGNAL(triggered()), SLOT(EditValue()));
|
||||
connect(ui_->action_auto_complete_tags, SIGNAL(triggered()), SLOT(AutoCompleteTags()));
|
||||
connect(ui_->action_configure, SIGNAL(triggered()), SLOT(OpenSettingsDialog()));
|
||||
connect(ui_->action_about, SIGNAL(triggered()), SLOT(ShowAboutDialog()));
|
||||
connect(ui_->action_about_qt, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
|
||||
@ -471,6 +475,9 @@ MainWindow::MainWindow(
|
||||
playlist_menu_->addAction(ui_->action_edit_value);
|
||||
playlist_menu_->addAction(ui_->action_renumber_tracks);
|
||||
playlist_menu_->addAction(ui_->action_selection_set_value);
|
||||
#ifdef HAVE_LIBTUNEPIMP
|
||||
playlist_menu_->addAction(ui_->action_auto_complete_tags);
|
||||
#endif
|
||||
playlist_menu_->addSeparator();
|
||||
playlist_copy_to_library_ = playlist_menu_->addAction(IconLoader::Load("edit-copy"), tr("Copy to library..."), this, SLOT(PlaylistCopyToLibrary()));
|
||||
playlist_move_to_library_ = playlist_menu_->addAction(IconLoader::Load("go-jump"), tr("Move to library..."), this, SLOT(PlaylistMoveToLibrary()));
|
||||
@ -1108,6 +1115,8 @@ void MainWindow::PlaylistRightClick(const QPoint& global_pos, const QModelIndex&
|
||||
// those is not CUE related
|
||||
ui_->action_edit_track->setEnabled(editable);
|
||||
ui_->action_edit_track->setVisible(editable);
|
||||
ui_->action_auto_complete_tags->setEnabled(editable);
|
||||
ui_->action_auto_complete_tags->setVisible(editable);
|
||||
// the rest of the read / write actions work only when there are no CUEs
|
||||
// involved
|
||||
if(cue_selected)
|
||||
@ -1871,3 +1880,47 @@ void MainWindow::Exit() {
|
||||
}
|
||||
qApp->quit();
|
||||
}
|
||||
|
||||
void MainWindow::AutoCompleteTags() {
|
||||
// Create the tag fetching stuff if it hasn't been already
|
||||
if (!tag_fetcher_) {
|
||||
tag_fetcher_.reset(new TagFetcher);
|
||||
track_selection_dialog_.reset(new TrackSelectionDialog);
|
||||
track_selection_dialog_->set_save_on_close(true);
|
||||
|
||||
connect(tag_fetcher_.get(), SIGNAL(FetchFinished(QString, SongList)),
|
||||
track_selection_dialog_.get(), SLOT(FetchTagFinished(QString, SongList)),
|
||||
Qt::QueuedConnection);
|
||||
connect(track_selection_dialog_.get(), SIGNAL(accepted()),
|
||||
SLOT(AutoCompleteTagsAccepted()));
|
||||
}
|
||||
|
||||
// Get the selected songs and start fetching tags for them
|
||||
SongList songs;
|
||||
autocomplete_tag_items_.clear();
|
||||
foreach (const QModelIndex& index,
|
||||
ui_->playlist->view()->selectionModel()->selection().indexes()) {
|
||||
if (index.column() != 0)
|
||||
continue;
|
||||
int row = playlists_->current()->proxy()->mapToSource(index).row();
|
||||
PlaylistItemPtr item(playlists_->current()->item_at(row));
|
||||
Song song = item->Metadata();
|
||||
|
||||
if (song.IsEditable()) {
|
||||
songs << song;
|
||||
autocomplete_tag_items_ << item;
|
||||
tag_fetcher_->FetchFromFile(song.filename());
|
||||
}
|
||||
}
|
||||
|
||||
track_selection_dialog_->Init(songs);
|
||||
track_selection_dialog_->show();
|
||||
}
|
||||
void MainWindow::AutoCompleteTagsAccepted() {
|
||||
foreach (PlaylistItemPtr item, autocomplete_tag_items_) {
|
||||
item->Reload();
|
||||
}
|
||||
|
||||
// This is really lame but we don't know what rows have changed
|
||||
ui_->playlist->view()->update();
|
||||
}
|
||||
|
@ -66,7 +66,9 @@ class Song;
|
||||
class SongInfoBase;
|
||||
class SongInfoView;
|
||||
class SystemTrayIcon;
|
||||
class TagFetcher;
|
||||
class TaskManager;
|
||||
class TrackSelectionDialog;
|
||||
class TranscodeDialog;
|
||||
class VisualisationContainer;
|
||||
class WiimotedevShortcuts;
|
||||
@ -157,6 +159,8 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
||||
void RenumberTracks();
|
||||
void SelectionSetValue();
|
||||
void EditValue();
|
||||
void AutoCompleteTags();
|
||||
void AutoCompleteTagsAccepted();
|
||||
void PlaylistUndoRedoChanged(QAction* undo, QAction* redo);
|
||||
|
||||
void PlaylistCopyToLibrary();
|
||||
@ -283,6 +287,9 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
||||
boost::scoped_ptr<OrganiseDialog> organise_dialog_;
|
||||
boost::scoped_ptr<QueueManager> queue_manager_;
|
||||
|
||||
boost::scoped_ptr<TagFetcher> tag_fetcher_;
|
||||
boost::scoped_ptr<TrackSelectionDialog> track_selection_dialog_;
|
||||
PlaylistItemList autocomplete_tag_items_;
|
||||
|
||||
#ifdef ENABLE_VISUALISATIONS
|
||||
boost::scoped_ptr<VisualisationContainer> visualisation_;
|
||||
|
@ -390,7 +390,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1131</width>
|
||||
<height>25</height>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_music">
|
||||
@ -764,6 +764,18 @@
|
||||
<string>Do a full library rescan</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_auto_complete_tags">
|
||||
<property name="icon">
|
||||
<iconset resource="../../data/data.qrc">
|
||||
<normaloff>:/providers/musicbrainz.png</normaloff>:/providers/musicbrainz.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Complete tags automatically...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+T</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
|
@ -19,13 +19,16 @@
|
||||
#include "trackselectiondialog.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QFutureWatcher>
|
||||
#include <QPushButton>
|
||||
#include <QShortcut>
|
||||
#include <QtConcurrentRun>
|
||||
#include <QtDebug>
|
||||
|
||||
TrackSelectionDialog::TrackSelectionDialog(QWidget *parent)
|
||||
: QDialog(parent),
|
||||
ui_(new Ui_TrackSelectionDialog)
|
||||
ui_(new Ui_TrackSelectionDialog),
|
||||
save_on_close_(false)
|
||||
{
|
||||
// Setup dialog window
|
||||
ui_->setupUi(this);
|
||||
@ -35,6 +38,7 @@ TrackSelectionDialog::TrackSelectionDialog(QWidget *parent)
|
||||
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);
|
||||
@ -188,17 +192,68 @@ void TrackSelectionDialog::ResultSelected() {
|
||||
data_[song_row].selected_result_ = result_index;
|
||||
}
|
||||
|
||||
void TrackSelectionDialog::SetLoading(const QString& message) {
|
||||
const bool loading = !message.isEmpty();
|
||||
|
||||
ui_->loading_container->setVisible(loading);
|
||||
ui_->button_box->setEnabled(!loading);
|
||||
ui_->splitter->setEnabled(!loading);
|
||||
ui_->loading_label->setText(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.Save();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
foreach (const Data& data, data_) {
|
||||
if (data.pending_ || data.results_.isEmpty() || data.selected_result_ == -1)
|
||||
continue;
|
||||
|
||||
emit SongChosen(data.original_song_.filename(), data.results_[data.selected_result_]);
|
||||
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);
|
||||
|
@ -32,6 +32,8 @@ public:
|
||||
TrackSelectionDialog(QWidget *parent = 0);
|
||||
~TrackSelectionDialog();
|
||||
|
||||
void set_save_on_close(bool save_on_close) { save_on_close_ = save_on_close; }
|
||||
|
||||
void Init(const SongList& songs);
|
||||
|
||||
public slots:
|
||||
@ -41,7 +43,7 @@ public slots:
|
||||
void accept();
|
||||
|
||||
signals:
|
||||
void SongChosen(const QString& filename, const Song& song);
|
||||
void SongChosen(const Song& original_song, const Song& new_metadata);
|
||||
|
||||
private slots:
|
||||
void UpdateStack();
|
||||
@ -50,10 +52,7 @@ private slots:
|
||||
void PreviousSong();
|
||||
|
||||
void ResultSelected();
|
||||
|
||||
private:
|
||||
void AddDivider(const QString& text, QTreeWidget* parent) const;
|
||||
void AddSong(const Song& song, int result_index, QTreeWidget* parent) const;
|
||||
void AcceptFinished();
|
||||
|
||||
private:
|
||||
Ui_TrackSelectionDialog* ui_;
|
||||
@ -67,10 +66,19 @@ private:
|
||||
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
|
||||
|
@ -221,20 +221,53 @@
|
||||
</widget>
|
||||
</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>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QWidget" name="loading_container" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="BusyIndicator" name="label_6">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="loading_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user