Somewhat-working smart playlist preview widget for the wizard. Still needs a lot of polish.

This commit is contained in:
David Sansome 2010-10-29 18:41:49 +00:00
parent fb224608ae
commit 2a480f7b1c
55 changed files with 829 additions and 38 deletions

View File

@ -133,6 +133,8 @@ set(SOURCES
smartplaylists/playlistgeneratorinserter.cpp
smartplaylists/smartplaylistcontainer.cpp
smartplaylists/smartplaylistmodel.cpp
smartplaylists/smartplaylistsearch.cpp
smartplaylists/smartplaylistsearchpreview.cpp
smartplaylists/smartplaylistsearchterm.cpp
smartplaylists/smartplaylistsearchtermwidget.cpp
smartplaylists/smartplaylistview.cpp
@ -293,6 +295,7 @@ set(HEADERS
smartplaylists/playlistgeneratorinserter.h
smartplaylists/smartplaylistcontainer.h
smartplaylists/smartplaylistmodel.h
smartplaylists/smartplaylistsearchpreview.h
smartplaylists/smartplaylistsearchtermwidget.h
smartplaylists/smartplaylistview.h
smartplaylists/smartplaylistwizard.h
@ -378,6 +381,7 @@ set(UI
radio/radioviewcontainer.ui
smartplaylists/smartplaylistcontainer.ui
smartplaylists/smartplaylistsearchpreview.ui
smartplaylists/smartplaylistsearchtermwidget.ui
smartplaylists/smartplaylistwizard.ui

View File

@ -19,6 +19,7 @@
#include "sqlrow.h"
#include "core/database.h"
#include "core/scopedtransaction.h"
#include "smartplaylists/smartplaylistsearch.h"
#include <QDir>
#include <QVariant>
@ -808,16 +809,13 @@ bool LibraryBackend::ExecQuery(LibraryQuery *q) {
return !db_->CheckErrors(q->Exec(db_->Connect(), songs_table_, fts_table_));
}
SongList LibraryBackend::FindSongs(const QString& where_sql,
const QString& order_sql, int limit) {
SongList LibraryBackend::FindSongs(const SmartPlaylistSearch& search) {
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
// Build the query
QString sql = "SELECT ROWID, " + Song::kColumnSpec + " FROM " + songs_table_;
if (!where_sql.isEmpty()) sql += " WHERE " + where_sql;
if (!order_sql.isEmpty()) sql += " ORDER BY " + order_sql;
if (limit) sql += " LIMIT " + QString::number(limit);
QString sql = search.ToSql(songs_table());
qDebug() << sql;
// Run the query
SongList ret;

View File

@ -27,6 +27,7 @@
#include <boost/shared_ptr.hpp>
class Database;
class SmartPlaylistSearch;
class LibraryBackendInterface : public QObject {
public:
@ -131,7 +132,7 @@ class LibraryBackend : public LibraryBackendInterface {
void RemoveDirectory(const Directory& dir);
bool ExecQuery(LibraryQuery* q);
SongList FindSongs(const QString& where_sql, const QString& order_sql, int limit);
SongList FindSongs(const SmartPlaylistSearch& search);
void IncrementPlayCountAsync(int id);
void IncrementSkipCountAsync(int id);

View File

@ -19,19 +19,14 @@
#include <QSettings>
const int PlaylistGenerator::kDefaultLimit = 15;
const int PlaylistGenerator::kDefaultLimit = 20;
PlaylistGenerator::PlaylistGenerator()
: QObject(NULL),
backend_(NULL),
limit_(kDefaultLimit)
backend_(NULL)
{
}
void PlaylistGenerator::Load(const QSettings& s) {
limit_ = s.value("limit", kDefaultLimit).toInt();
}
PlaylistGeneratorPtr PlaylistGenerator::Create(const QString& type) {
if (type == "Query")
return PlaylistGeneratorPtr(new QueryPlaylistGenerator);

View File

@ -38,10 +38,9 @@ public:
void set_library(LibraryBackend* backend) { backend_ = backend; }
int limit() const { return limit_; }
QString name() const { return name_; }
virtual void Load(const QSettings& s);
virtual void Load(const QSettings& s) {}
virtual PlaylistItemList Generate() = 0;
signals:
@ -50,7 +49,6 @@ signals:
protected:
LibraryBackend* backend_;
int limit_;
QString name_;
};

View File

@ -25,15 +25,19 @@ QueryPlaylistGenerator::QueryPlaylistGenerator()
{
}
void QueryPlaylistGenerator::Load(const SmartPlaylistSearch& search) {
search_ = search;
}
void QueryPlaylistGenerator::Load(const QSettings& s) {
PlaylistGenerator::Load(s);
name_ = s.value("name").toString();
where_ = s.value("where").toString();
order_ = s.value("order").toString();
QDataStream stream(s.value("search").toByteArray());
stream >> search_;
}
PlaylistItemList QueryPlaylistGenerator::Generate() {
SongList songs = backend_->FindSongs(where_, order_, limit_);
SongList songs = backend_->FindSongs(search_);
PlaylistItemList items;
foreach (const Song& song, songs) {

View File

@ -18,17 +18,18 @@
#define QUERYPLAYLISTGENERATOR_H
#include "playlistgenerator.h"
#include "smartplaylistsearch.h"
class QueryPlaylistGenerator : public PlaylistGenerator {
public:
QueryPlaylistGenerator();
void Load(const SmartPlaylistSearch& search);
void Load(const QSettings& s);
PlaylistItemList Generate();
private:
QString where_;
QString order_;
SmartPlaylistSearch search_;
};
#endif // QUERYPLAYLISTGENERATOR_H

View File

@ -0,0 +1,79 @@
/* This file is part of Clementine.
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "smartplaylistsearch.h"
#include "core/song.h"
#include <QStringList>
SmartPlaylistSearch::SmartPlaylistSearch() {
Reset();
}
void SmartPlaylistSearch::Reset() {
terms_.clear();
sort_type_ = Sort_Random;
sort_field_ = SmartPlaylistSearchTerm::Field_Title;
limit_ = -1;
}
QString SmartPlaylistSearch::ToSql(const QString& songs_table) const {
QString sql = "SELECT ROWID," + Song::kColumnSpec + " FROM " + songs_table;
// Add search terms
QStringList term_sql;
foreach (const SmartPlaylistSearchTerm& term, terms_) {
term_sql += term.ToSql();
}
if (!terms_.isEmpty()) {
sql += " WHERE " + term_sql.join(" AND ");
}
// Add sort by
if (sort_type_ == Sort_Random) {
sql += " ORDER BY random()";
} else {
sql += " ORDER BY " + SmartPlaylistSearchTerm::FieldColumnName(sort_field_)
+ (sort_type_ == Sort_FieldAsc ? " ASC" : " DESC");
}
// Add limit
if (limit_ != -1) {
sql += " LIMIT " + QString::number(limit_);
}
return sql;
}
QDataStream& operator <<(QDataStream& s, const SmartPlaylistSearch& search) {
s << search.terms_;
s << quint8(search.sort_type_);
s << quint8(search.sort_field_);
s << qint32(search.limit_);
return s;
}
QDataStream& operator >>(QDataStream& s, SmartPlaylistSearch& search) {
quint8 sort_type, sort_field;
qint32 limit;
s >> search.terms_ >> sort_type >> sort_field >> limit;
search.sort_type_ = SmartPlaylistSearch::SortType(sort_type);
search.sort_field_ = SmartPlaylistSearchTerm::Field(sort_field);
search.limit_ = limit;
return s;
}

View File

@ -0,0 +1,47 @@
/* This file is part of Clementine.
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SMARTPLAYLISTSEARCH_H
#define SMARTPLAYLISTSEARCH_H
#include "smartplaylistsearchterm.h"
class SmartPlaylistSearch {
public:
SmartPlaylistSearch();
// These values are persisted, so add to the end of the enum only
enum SortType {
Sort_Random = 0,
Sort_FieldAsc,
Sort_FieldDesc,
};
bool is_valid() const { return !terms_.isEmpty(); }
QList<SmartPlaylistSearchTerm> terms_;
SortType sort_type_;
SmartPlaylistSearchTerm::Field sort_field_;
int limit_;
void Reset();
QString ToSql(const QString& songs_table) const;
};
QDataStream& operator <<(QDataStream& s, const SmartPlaylistSearch& search);
QDataStream& operator >>(QDataStream& s, SmartPlaylistSearch& search);
#endif // SMARTPLAYLISTSEARCH_H

View File

@ -0,0 +1,125 @@
/* This file is part of Clementine.
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "queryplaylistgenerator.h"
#include "smartplaylistsearchpreview.h"
#include "ui_smartplaylistsearchpreview.h"
#include "playlist/playlistdelegates.h"
#include <QFutureWatcher>
#include <QtConcurrentRun>
typedef QFuture<PlaylistItemList> Future;
typedef QFutureWatcher<PlaylistItemList> FutureWatcher;
SmartPlaylistSearchPreview::SmartPlaylistSearchPreview(QWidget *parent)
: QWidget(parent),
ui_(new Ui_SmartPlaylistSearchPreview)
{
ui_->setupUi(this);
QFont bold_font;
bold_font.setBold(true);
ui_->preview_label->setFont(bold_font);
ui_->busy_container->hide();
fields_ << SmartPlaylistSearchTerm::Field_Artist;
fields_ << SmartPlaylistSearchTerm::Field_Album;
fields_ << SmartPlaylistSearchTerm::Field_Title;
fields_ << SmartPlaylistSearchTerm::Field_Length;
fields_ << SmartPlaylistSearchTerm::Field_PlayCount;
fields_ << SmartPlaylistSearchTerm::Field_SkipCount;
fields_ << SmartPlaylistSearchTerm::Field_Rating;
QStringList column_names;
foreach (SmartPlaylistSearchTerm::Field field, fields_) {
column_names << SmartPlaylistSearchTerm::FieldName(field);
}
ui_->tree->setHeaderLabels(column_names);
ui_->tree->setItemDelegateForColumn(0, new TextItemDelegate(this));
ui_->tree->setItemDelegateForColumn(1, new TextItemDelegate(this));
ui_->tree->setItemDelegateForColumn(2, new TextItemDelegate(this));
ui_->tree->setItemDelegateForColumn(3, new LengthItemDelegate(this));
ui_->tree->setItemDelegateForColumn(6, new RatingItemDelegate(this));
}
SmartPlaylistSearchPreview::~SmartPlaylistSearchPreview() {
delete ui_;
}
void SmartPlaylistSearchPreview::set_library(LibraryBackend* backend) {
backend_ = backend;
}
void SmartPlaylistSearchPreview::Update(const SmartPlaylistSearch& search) {
if (generator_) {
// It's busy generating something already
pending_search_ = search;
return;
}
RunSearch(search);
}
void SmartPlaylistSearchPreview::RunSearch(const SmartPlaylistSearch& search) {
generator_.reset(new QueryPlaylistGenerator);
generator_->set_library(backend_);
generator_->Load(search);
ui_->busy_container->show();
ui_->count_label->hide();
Future future = QtConcurrent::run(generator_.get(), &QueryPlaylistGenerator::Generate);
FutureWatcher* watcher = new FutureWatcher(this);
watcher->setFuture(future);
connect(watcher, SIGNAL(finished()), SLOT(SearchFinished()));
}
void SmartPlaylistSearchPreview::SearchFinished() {
FutureWatcher* watcher = static_cast<FutureWatcher*>(sender());
watcher->deleteLater();
generator_.reset();
PlaylistItemList items = watcher->result();
int count = 0;
ui_->tree->clear();
foreach (PlaylistItemPtr item, items) {
if (count >= PlaylistGenerator::kDefaultLimit)
break;
count ++;
QTreeWidgetItem* tree_item = new QTreeWidgetItem;
tree_item->setData(0, Qt::DisplayRole, item->Metadata().album());
tree_item->setData(1, Qt::DisplayRole, item->Metadata().artist());
tree_item->setData(2, Qt::DisplayRole, item->Metadata().title());
tree_item->setData(3, Qt::DisplayRole, item->Metadata().length());
tree_item->setData(4, Qt::DisplayRole, item->Metadata().playcount());
tree_item->setData(5, Qt::DisplayRole, item->Metadata().skipcount());
tree_item->setData(6, Qt::DisplayRole, item->Metadata().rating());
ui_->tree->addTopLevelItem(tree_item);
}
if (items.count() > count) {
ui_->count_label->setText(tr("%1 songs found (showing %2)").arg(items.count()).arg(count));
} else {
ui_->count_label->setText(tr("%1 songs found").arg(items.count()));
}
ui_->busy_container->hide();
ui_->count_label->show();
}

View File

@ -0,0 +1,57 @@
/* This file is part of Clementine.
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SMARTPLAYLISTSEARCHPREVIEW_H
#define SMARTPLAYLISTSEARCHPREVIEW_H
#include "smartplaylistsearch.h"
#include <QWidget>
#include <boost/scoped_ptr.hpp>
class LibraryBackend;
class QueryPlaylistGenerator;
class Ui_SmartPlaylistSearchPreview;
class SmartPlaylistSearchPreview : public QWidget {
Q_OBJECT
public:
SmartPlaylistSearchPreview(QWidget *parent = 0);
~SmartPlaylistSearchPreview();
void set_library(LibraryBackend* backend);
void Update(const SmartPlaylistSearch& search);
private:
void RunSearch(const SmartPlaylistSearch& search);
private slots:
void SearchFinished();
private:
Ui_SmartPlaylistSearchPreview* ui_;
QList<SmartPlaylistSearchTerm::Field> fields_;
LibraryBackend* backend_;
SmartPlaylistSearch pending_search_;
boost::scoped_ptr<QueryPlaylistGenerator> generator_;
};
#endif // SMARTPLAYLISTSEARCHPREVIEW_H

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SmartPlaylistSearchPreview</class>
<widget class="QWidget" name="SmartPlaylistSearchPreview">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>651</width>
<height>377</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="margin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="preview_label">
<property name="text">
<string>Preview</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>
<item>
<widget class="QLabel" name="count_label"/>
</item>
<item>
<widget class="QWidget" name="busy_container" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="BusyIndicator" name="widget" native="true"/>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Loading...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTreeWidget" name="tree">
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>BusyIndicator</class>
<extends>QWidget</extends>
<header>widgets/busyindicator.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -46,6 +46,17 @@ QString SmartPlaylistSearchTerm::ToSql() const {
return QString();
}
bool SmartPlaylistSearchTerm::is_valid() const {
switch (TypeOf(field_)) {
case Type_Text: return !value_.toString().isEmpty();
case Type_Date: return value_.toInt() != 0;
case Type_Number: return value_.toInt() >= 0;
case Type_Rating: return value_.toFloat() >= 0.0;
case Type_Time: return true;
}
return false;
}
SmartPlaylistSearchTerm::Type SmartPlaylistSearchTerm::TypeOf(Field field) {
switch (field) {
case Field_Length:

View File

@ -80,6 +80,7 @@ public:
QVariant value_;
QString ToSql() const;
bool is_valid() const;
static Type TypeOf(Field field);
static QList<Operator> OperatorsForType(Type type);

View File

@ -70,6 +70,13 @@ SmartPlaylistSearchTermWidget::SmartPlaylistSearchTermWidget(LibraryBackend* lib
connect(ui_->field, SIGNAL(currentIndexChanged(int)), SLOT(FieldChanged(int)));
connect(ui_->remove, SIGNAL(clicked()), SIGNAL(RemoveClicked()));
connect(ui_->field, SIGNAL(currentIndexChanged(int)), SIGNAL(Changed()));
connect(ui_->value_date, SIGNAL(dateChanged(QDate)), SIGNAL(Changed()));
connect(ui_->value_number, SIGNAL(valueChanged(int)), SIGNAL(Changed()));
connect(ui_->value_rating, SIGNAL(RatingChanged(float)), SIGNAL(Changed()));
connect(ui_->value_text, SIGNAL(textChanged(QString)), SIGNAL(Changed()));
connect(ui_->value_time, SIGNAL(timeChanged(QTime)), SIGNAL(Changed()));
// Populate the combo boxes
for (int i=0 ; i<SmartPlaylistSearchTerm::FieldCount ; ++i) {
ui_->field->addItem(SmartPlaylistSearchTerm::FieldName(SmartPlaylistSearchTerm::Field(i)));

View File

@ -48,6 +48,8 @@ signals:
void Clicked();
void RemoveClicked();
void Changed();
protected:
void showEvent(QShowEvent*);
void enterEvent(QEvent*);

View File

@ -14,10 +14,24 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "smartplaylistsearchpreview.h"
#include "smartplaylistsearchtermwidget.h"
#include "smartplaylistwizard.h"
#include "ui_smartplaylistwizard.h"
SmartPlaylistWizard::SearchPage::SearchPage(QWidget* parent)
: QWizardPage(parent)
{
}
bool SmartPlaylistWizard::SearchPage::isComplete() const {
foreach (SmartPlaylistSearchTermWidget* widget, terms_) {
if (!widget->Term().is_valid())
return false;
}
return true;
}
SmartPlaylistWizard::SmartPlaylistWizard(LibraryBackend* library, QWidget* parent)
: QWizard(parent),
ui_(new Ui_SmartPlaylistWizard),
@ -25,14 +39,21 @@ SmartPlaylistWizard::SmartPlaylistWizard(LibraryBackend* library, QWidget* paren
{
ui_->setupUi(this);
new_search_term_ = new SmartPlaylistSearchTermWidget(library_, this);
new_search_term_->SetActive(false);
connect(new_search_term_, SIGNAL(Clicked()), SLOT(AddSearchTerm()));
// Create the new search term widget
ui_->page_query_search->new_term_ = new SmartPlaylistSearchTermWidget(library_, this);
ui_->page_query_search->new_term_->SetActive(false);
connect(ui_->page_query_search->new_term_, SIGNAL(Clicked()), SLOT(AddSearchTerm()));
search_term_layout_ = new QVBoxLayout(ui_->page_query_search);
search_term_layout_->addWidget(new_search_term_);
search_term_layout_->addStretch();
// Add an empty initial term
ui_->page_query_search->layout_ = new QVBoxLayout(ui_->page_query_search);
ui_->page_query_search->layout_->addWidget(ui_->page_query_search->new_term_);
ui_->page_query_search->layout_->addStretch();
AddSearchTerm();
// Add the preview widget at the bottom
ui_->page_query_search->preview_ = new SmartPlaylistSearchPreview(this);
ui_->page_query_search->preview_->set_library(library_);
ui_->page_query_search->layout_->addWidget(ui_->page_query_search->preview_);
}
SmartPlaylistWizard::~SmartPlaylistWizard() {
@ -43,9 +64,13 @@ void SmartPlaylistWizard::AddSearchTerm() {
SmartPlaylistSearchTermWidget* widget =
new SmartPlaylistSearchTermWidget(library_, this);
connect(widget, SIGNAL(RemoveClicked()), SLOT(RemoveSearchTerm()));
connect(widget, SIGNAL(Changed()), SLOT(UpdateTermPreview()));
search_term_layout_->insertWidget(search_terms_.count(), widget);
search_terms_ << widget;
ui_->page_query_search->layout_->insertWidget(
ui_->page_query_search->terms_.count(), widget);
ui_->page_query_search->terms_ << widget;
UpdateTermPreview();
}
void SmartPlaylistWizard::RemoveSearchTerm() {
@ -54,10 +79,33 @@ void SmartPlaylistWizard::RemoveSearchTerm() {
if (!widget)
return;
const int index = search_terms_.indexOf(widget);
const int index = ui_->page_query_search->terms_.indexOf(widget);
if (index == -1)
return;
delete search_terms_.takeAt(index);
delete ui_->page_query_search->terms_.takeAt(index);
UpdateTermPreview();
}
void SmartPlaylistWizard::UpdateTermPreview() {
SmartPlaylistSearch search = MakeSearch();
emit ui_->page_query_search->completeChanged();
if (!search.is_valid())
return;
ui_->page_query_search->preview_->Update(search);
}
SmartPlaylistSearch SmartPlaylistWizard::MakeSearch() const {
SmartPlaylistSearch ret;
foreach (SmartPlaylistSearchTermWidget* widget, ui_->page_query_search->terms_) {
SmartPlaylistSearchTerm term = widget->Term();
if (!term.is_valid())
return SmartPlaylistSearch();
ret.terms_ << term;
}
return ret;
}

View File

@ -17,9 +17,12 @@
#ifndef SMARTPLAYLISTWIZARD_H
#define SMARTPLAYLISTWIZARD_H
#include "smartplaylistsearch.h"
#include <QWizard>
class LibraryBackend;
class SmartPlaylistSearchPreview;
class SmartPlaylistSearchTermWidget;
class Ui_SmartPlaylistWizard;
@ -32,17 +35,31 @@ public:
SmartPlaylistWizard(LibraryBackend* library, QWidget* parent);
~SmartPlaylistWizard();
class SearchPage : public QWizardPage {
friend class SmartPlaylistWizard;
public:
SearchPage(QWidget* parent = 0);
bool isComplete() const;
QVBoxLayout* layout_;
QList<SmartPlaylistSearchTermWidget*> terms_;
SmartPlaylistSearchTermWidget* new_term_;
SmartPlaylistSearchPreview* preview_;
};
private slots:
void AddSearchTerm();
void RemoveSearchTerm();
void UpdateTermPreview();
private:
SmartPlaylistSearch MakeSearch() const;
private:
Ui_SmartPlaylistWizard* ui_;
LibraryBackend* library_;
QVBoxLayout* search_term_layout_;
QList<SmartPlaylistSearchTermWidget*> search_terms_;
SmartPlaylistSearchTermWidget* new_search_term_;
};
#endif // SMARTPLAYLISTWIZARD_H

View File

@ -81,7 +81,7 @@ QLabel {
</item>
</layout>
</widget>
<widget class="QWizardPage" name="page_query_search">
<widget class="SmartPlaylistWizard::SearchPage" name="page_query_search">
<property name="styleSheet">
<string notr="true">#termframe {
border: 1px solid grey;
@ -206,9 +206,33 @@ margin-left: 9px;
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="SmartPlaylistSearchPreview" name="widget" native="true"/>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>SmartPlaylistSearchPreview</class>
<extends>QWidget</extends>
<header>smartplaylists/smartplaylistsearchpreview.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>SmartPlaylistWizard::SearchPage</class>
<extends>QWizardPage</extends>
<header>smartplaylistwizard.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>

View File

@ -61,6 +61,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr "%1 cancó"
msgid "%1 songs"
msgstr "%1 cançons"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -63,6 +63,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -63,6 +63,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -63,6 +63,14 @@ msgstr "%1 Titel"
msgid "%1 songs"
msgstr "%1 Titel"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -64,6 +64,14 @@ msgstr "%1 τραγούδι"
msgid "%1 songs"
msgstr "%1 τραγούδια"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -61,6 +61,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -61,6 +61,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -63,6 +63,14 @@ msgstr "%1 canción"
msgid "%1 songs"
msgstr "%1 canciones"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -63,6 +63,14 @@ msgstr "%1 chanson"
msgid "%1 songs"
msgstr "%1 chansons"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr "%1 canción"
msgid "%1 songs"
msgstr "%1 cancións"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr "%1 szám"
msgid "%1 songs"
msgstr "%1 szám"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -63,6 +63,14 @@ msgstr "%1 brano"
msgid "%1 songs"
msgstr "%1 brani"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -61,6 +61,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr "%1 lied"
msgid "%1 songs"
msgstr "%1 liedjes"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -61,6 +61,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -63,6 +63,14 @@ msgstr "%1 utwór"
msgid "%1 songs"
msgstr "%1 utwory"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -64,6 +64,14 @@ msgstr "%1 música"
msgid "%1 songs"
msgstr "%1 músicas"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr "%1 música"
msgid "%1 songs"
msgstr "%1 músicas"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -61,6 +61,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -61,6 +61,14 @@ msgstr "%1 композиция"
msgid "%1 songs"
msgstr "%1 композиций"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -63,6 +63,14 @@ msgstr "%1 pieseň"
msgid "%1 songs"
msgstr "%1 piesní"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr "%1 skladba"
msgid "%1 songs"
msgstr "%1 skladb"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr "%1 şarkı"
msgid "%1 songs"
msgstr "%1 şarkı"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -52,6 +52,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr "%1 пісня"
msgid "%1 songs"
msgstr "%1 пісень"
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -61,6 +61,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -62,6 +62,14 @@ msgstr ""
msgid "%1 songs"
msgstr ""
#, qt-format
msgid "%1 songs found"
msgstr ""
#, qt-format
msgid "%1 songs found (showing %2)"
msgstr ""
#, qt-format
msgid "%1 total plays"
msgstr ""

View File

@ -128,6 +128,7 @@ void RatingWidget::paintEvent(QPaintEvent* e) {
void RatingWidget::mousePressEvent(QMouseEvent* e) {
rating_ = RatingPainter::RatingForPos(e->pos(), rect());
emit RatingChanged(rating_);
}
void RatingWidget::mouseMoveEvent(QMouseEvent* e) {

View File

@ -47,6 +47,9 @@ public:
float rating() const { return rating_; }
void set_rating(float rating);
signals:
void RatingChanged(float rating);
protected:
void paintEvent(QPaintEvent*);
void mousePressEvent(QMouseEvent* e);