Add support for fetching tags for multiple songs at once. Fixes issue #1399

This commit is contained in:
David Sansome 2011-03-05 19:24:44 +00:00
parent 34f8c124f5
commit 4a020c0c04
6 changed files with 461 additions and 280 deletions

View File

@ -40,8 +40,6 @@
#include <QtDebug>
const char* EditTagDialog::kHintText = QT_TR_NOOP("(different across multiple songs)");
const char* EditTagDialog::kTagFetchText = QT_TR_NOOP("Complete tags automatically");
const char* EditTagDialog::kTagFetchOnLoadText = QT_TR_NOOP("Generating audio fingerprint and fetching results...");
EditTagDialog::EditTagDialog(QWidget* parent)
: QDialog(parent),
@ -51,7 +49,7 @@ EditTagDialog::EditTagDialog(QWidget* parent)
loading_(false),
ignore_edits_(false),
#ifdef HAVE_LIBTUNEPIMP
tag_fetcher_(new TagFetcher()),
tag_fetcher_(new TagFetcher(this)),
#endif
cover_loader_(new BackgroundThreadImplementation<AlbumCoverLoader, AlbumCoverLoader>(this)),
cover_art_id_(0),
@ -64,17 +62,16 @@ EditTagDialog::EditTagDialog(QWidget* parent)
SLOT(ArtLoaded(quint64,QImage,QImage)));
#ifdef HAVE_LIBTUNEPIMP
connect(tag_fetcher_, SIGNAL(FetchFinished(QString, SongList)),
this, SLOT(FetchTagFinished(QString, SongList)), Qt::QueuedConnection);
connect(results_dialog_, SIGNAL(SongChoosen(QString, Song)), SLOT(FetchTagSongChoosen(QString, Song)));
results_dialog_, SLOT(FetchTagFinished(QString, SongList)),
Qt::QueuedConnection);
connect(results_dialog_, SIGNAL(SongChosen(QString, Song)),
SLOT(FetchTagSongChosen(QString, Song)));
#endif
ui_->setupUi(this);
ui_->splitter->setSizes(QList<int>() << 200 << width() - 200);
ui_->loading_container->hide();
#ifdef HAVE_LIBTUNEPIMP
ui_->fetch_tag->setText(tr(kTagFetchText));
#else
ui_->fetch_tag->setDisabled(true);
#ifndef HAVE_LIBTUNEPIMP
ui_->fetch_tag->setVisible(false);
#endif
@ -128,7 +125,6 @@ EditTagDialog::EditTagDialog(QWidget* parent)
connect(ui_->playcount_reset, SIGNAL(clicked()), SLOT(ResetPlayCounts()));
#ifdef HAVE_LIBTUNEPIMP
connect(ui_->fetch_tag, SIGNAL(clicked()), SLOT(FetchTag()));
ui_->fetch_tag->setText(tr(kTagFetchText));
#endif
// Set up the album cover menu
@ -172,9 +168,6 @@ EditTagDialog::EditTagDialog(QWidget* parent)
}
EditTagDialog::~EditTagDialog() {
#ifdef HAVE_LIBTUNEPIMP
delete tag_fetcher_;
# endif
delete ui_;
}
@ -245,6 +238,7 @@ void EditTagDialog::SetSongsFinished() {
}
// Select all
ui_->song_list->setCurrentRow(0);
ui_->song_list->selectAll();
// Hide the list if there's only one song in it
@ -253,9 +247,6 @@ void EditTagDialog::SetSongsFinished() {
previous_button_->setEnabled(multiple);
next_button_->setEnabled(multiple);
// Display tag fetcher if there's only one song
ui_->fetch_tag->setVisible(!multiple);
ui_->tab_widget->setCurrentWidget(multiple ? ui_->tags_tab : ui_->summary_tab);
}
@ -696,69 +687,50 @@ void EditTagDialog::ResetPlayCounts() {
void EditTagDialog::FetchTag() {
#ifdef HAVE_LIBTUNEPIMP
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
if (sel.isEmpty())
return;
Song* song = &data_[sel.first().row()].original_;
if (!song->is_valid())
return;
tag_fetcher_->FetchFromFile(song->filename());
ui_->fetch_tag->setDisabled(true); // disable button, will be re-enabled later
ui_->fetch_tag->setText(tr(kTagFetchOnLoadText));
QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
#endif
}
void EditTagDialog::FetchTagFinished(const QString& filename, const SongList& songs_guessed) {
#ifdef HAVE_LIBTUNEPIMP
// Restore cursor
QApplication::restoreOverrideCursor();
SongList songs;
// Get current song
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
if (sel.isEmpty())
return;
Song* current_song = &data_[sel.first().row()].original_;
// Check if the user has closed edit tag dialog or if it has reopened a new one.
// If so, ignore this signal; else open a dialog for selecting best result
if(isVisible() && current_song->filename() == filename) {
// Restore some components as "no working"
ui_->fetch_tag->setDisabled(false);
ui_->fetch_tag->setText(tr(kTagFetchText));
// If no songs have been guessed, just display a message
if(songs_guessed.empty()) {
QMessageBox::information(this, tr("Sorry"), tr("Clementine was unable to find results for this file"));
} else { // Else, display song's tags selection dialog only if edittagdialog is still opened
results_dialog_->Init(filename, songs_guessed);
results_dialog_->show();
foreach (const QModelIndex& index, sel) {
Song song = data_[index.row()].original_;
if (!song.is_valid()) {
continue;
}
songs << song;
tag_fetcher_->FetchFromFile(song.filename());
}
if (songs.isEmpty())
return;
results_dialog_->Init(songs);
results_dialog_->show();
#endif
}
void EditTagDialog::FetchTagSongChoosen(const QString& filename, const Song& song_choosen) {
// Get current song
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
if (sel.isEmpty())
return;
Song* current_song = &data_[sel.first().row()].original_;
if (!current_song->is_valid())
return;
// Check it's still the same song, using filename
if(filename != current_song->filename()) {
// different song: we shouldn't erase tags with wrong metadata
return;
}
void EditTagDialog::FetchTagSongChosen(const QString& filename, const Song& new_metadata) {
// Fill tags' fields
if(!song_choosen.title().isEmpty())
ui_->title->set_text(song_choosen.title());
if(!song_choosen.album().isEmpty())
ui_->album->set_text(song_choosen.album());
if(!song_choosen.album().isEmpty())
ui_->artist->set_text(song_choosen.artist());
if(song_choosen.track() > 0)
ui_->track->set_text(QString::number(song_choosen.track()));
// Find the song with this filename
for (int i=0 ; i<data_.count() ; ++i) {
Data* data = &data_[i];
if (data->original_.filename() != filename)
continue;
// Is it currently being displayed in the UI?
if (ui_->song_list->currentRow() == i) {
// Yes! We can just set the fields in the UI
ui_->title->set_text(new_metadata.title());
ui_->artist->set_text(new_metadata.artist());
ui_->album->set_text(new_metadata.album());
ui_->track->setValue(new_metadata.track());
} else {
data->current_.set_title(new_metadata.title());
data->current_.set_artist(new_metadata.artist());
data->current_.set_album(new_metadata.album());
data->current_.set_track(new_metadata.track());
}
break;
}
}

View File

@ -49,8 +49,6 @@ public:
~EditTagDialog();
static const char* kHintText;
static const char* kTagFetchText;
static const char* kTagFetchOnLoadText;
void SetSongs(const SongList& songs, const PlaylistItemList& items = PlaylistItemList());
void SetTagCompleter(LibraryBackend* backend);
@ -77,8 +75,7 @@ private slots:
void SongRated(float rating);
void ResetPlayCounts();
void FetchTag();
void FetchTagFinished(const QString& filename, const SongList& songs_guessed);
void FetchTagSongChoosen(const QString& filename, const Song& song);
void FetchTagSongChosen(const QString& filename, const Song& new_metadata);
void ArtLoaded(quint64 id, const QImage& scaled, const QImage& original);
@ -167,7 +164,7 @@ private:
QPushButton* previous_button_;
QPushButton* next_button_;
TrackSelectionDialog *results_dialog_;
TrackSelectionDialog* results_dialog_;
};
#endif // EDITTAGDIALOG_H

View File

@ -819,7 +819,11 @@
</widget>
</item>
<item row="6" column="1">
<widget class="QPushButton" name="fetch_tag"/>
<widget class="QPushButton" name="fetch_tag">
<property name="text">
<string>Complete tags automatically</string>
</property>
</widget>
</item>
</layout>
</widget>

View File

@ -15,8 +15,12 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "iconloader.h"
#include "trackselectiondialog.h"
#include <QFileInfo>
#include <QPushButton>
#include <QShortcut>
#include <QtDebug>
TrackSelectionDialog::TrackSelectionDialog(QWidget *parent)
@ -25,57 +29,183 @@ TrackSelectionDialog::TrackSelectionDialog(QWidget *parent)
{
// Setup dialog window
ui_->setupUi(this);
setModal(true);
connect(ui_->resultsTreeWidget, SIGNAL(itemDoubleClicked (QTreeWidgetItem*, int)), this, SLOT(accept()));
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);
// 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, 175); // Title column
ui_->results->setColumnWidth(2, 175); // Artist column
ui_->results->setColumnWidth(3, 175); // Album column
}
TrackSelectionDialog::~TrackSelectionDialog() {
ui_->resultsTreeWidget->clear();
delete ui_;
}
void TrackSelectionDialog::Init(const QString& filename, const SongList& songs) {
current_filename_ = filename;
current_songs_ = songs;
void TrackSelectionDialog::Init(const SongList& songs) {
ui_->song_list->clear();
ui_->stack->setCurrentWidget(ui_->loading_page);
data_.clear();
// Set filename
ui_->filenameLineEdit->setText(current_filename_); //TODO: use basefilename, it's nicer
foreach (const Song& song, songs) {
Data data;
data.original_song_ = song;
data_ << data;
// Clear tree widget
ui_->resultsTreeWidget->clear();
// Fill tree view with songs
int song_index = 0;
foreach(const Song& song, songs) {
QStringList valuesStringList;
if (song.track() > 0) {
valuesStringList << QString::number(song.track());
}
valuesStringList << song.title() << song.artist() << song.album();
QTreeWidgetItem* item = new QTreeWidgetItem(
ui_->resultsTreeWidget, valuesStringList);
if(song_index==0) { // if it's the first item, set focus on it
ui_->resultsTreeWidget->setCurrentItem(item);
}
item->setData(0, Qt::UserRole, song_index++); // To remember this widget corresponds to the ith song of our list
QListWidgetItem* item = new QListWidgetItem(ui_->song_list);
item->setText(QFileInfo(song.filename()).fileName());
item->setForeground(palette().color(QPalette::Disabled, QPalette::Text));
}
// Resize columns
ui_->resultsTreeWidget->setColumnWidth(0, 50); // Track column
ui_->resultsTreeWidget->setColumnWidth(1, 175); // Title column
ui_->resultsTreeWidget->setColumnWidth(2, 175); // Artist column
ui_->resultsTreeWidget->setColumnWidth(3, 175); // Album column
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::FetchTagFinished(const QString& filename,
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_.filename() == filename) {
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);
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;
foreach (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());
values << 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::accept() {
QDialog::accept();
QTreeWidgetItem *item =ui_->resultsTreeWidget->currentItem();
if(!item) { // no item selected
return;
}
int selected_song_index = item->data(0, Qt::UserRole).toInt();
if(selected_song_index >= 0 && selected_song_index < current_songs_.size()) {
emit SongChoosen(current_filename_, current_songs_[selected_song_index]);
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_]);
}
}
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);
}

View File

@ -32,18 +32,45 @@ public:
TrackSelectionDialog(QWidget *parent = 0);
~TrackSelectionDialog();
void Init(const QString& filename, const SongList& songs);
void Init(const SongList& songs);
public slots:
void FetchTagFinished(const QString& filename, const SongList& songs_guessed);
// QDialog
void accept();
signals:
void SongChoosen(const QString& filename, const Song& song);
void SongChosen(const QString& filename, const Song& song);
private slots:
void UpdateStack();
void NextSong();
void PreviousSong();
void ResultSelected();
private:
Ui_TrackSelectionDialog *ui_;
void AddDivider(const QString& text, QTreeWidget* parent) const;
void AddSong(const Song& song, int result_index, QTreeWidget* parent) const;
QString current_filename_;
SongList current_songs_;
private:
Ui_TrackSelectionDialog* ui_;
struct Data {
Data() : pending_(true), selected_result_(0) {}
Song original_song_;
bool pending_;
SongList results_;
int selected_result_;
};
QList<Data> data_;
QPushButton* previous_button_;
QPushButton* next_button_;
};
#endif // TRACKSELECTIONDIALOG_H

View File

@ -6,178 +6,222 @@
<rect>
<x>0</x>
<y>0</y>
<width>613</width>
<height>366</height>
<width>773</width>
<height>375</height>
</rect>
</property>
<property name="windowTitle">
<string>Results fetched</string>
<string>Tag fetcher</string>
</property>
<property name="windowIcon">
<iconset resource="../../data/data.qrc">
<normaloff>:/icon.png</normaloff>:/icon.png</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="fileNameGroupBox">
<property name="title">
<string>File name</string>
<widget class="QSplitter" name="splitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLineEdit" name="filenameLineEdit">
<property name="palette">
<palette>
<active>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="0">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="0">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="0">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="0">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="0">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="0">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="0">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="0">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="0">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
<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>1</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="label_2"/>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Identifying song...</string>
</property>
</widget>
</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>Clementine 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>
<property name="frame">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
<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>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>
<widget class="QGroupBox" name="resultsGroupBox">
<property name="title">
<string>Select best possible match</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTreeWidget" name="resultsTreeWidget">
<property name="editTriggers">
<set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set>
</property>
<attribute name="headerDefaultSectionSize">
<number>150</number>
</attribute>
<attribute name="headerMinimumSectionSize">
<number>50</number>
</attribute>
<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>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>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<widget class="QDialogButtonBox" name="button_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -194,12 +238,19 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>BusyIndicator</class>
<extends>QLabel</extends>
<header>widgets/busyindicator.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../../data/data.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<sender>button_box</sender>
<signal>accepted()</signal>
<receiver>TrackSelectionDialog</receiver>
<slot>accept()</slot>
@ -215,7 +266,7 @@
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<sender>button_box</sender>
<signal>rejected()</signal>
<receiver>TrackSelectionDialog</receiver>
<slot>reject()</slot>