Add global search suggestions
This commit is contained in:
parent
fe4e214a78
commit
9d6b72b4ab
@ -151,6 +151,7 @@ set(SOURCES
|
||||
globalsearch/searchproviderstatuswidget.cpp
|
||||
globalsearch/simplesearchprovider.cpp
|
||||
globalsearch/somafmsearchprovider.cpp
|
||||
globalsearch/suggestionwidget.cpp
|
||||
globalsearch/urlsearchprovider.cpp
|
||||
|
||||
internet/digitallyimportedclient.cpp
|
||||
@ -421,6 +422,7 @@ set(HEADERS
|
||||
globalsearch/groovesharksearchprovider.h
|
||||
globalsearch/searchprovider.h
|
||||
globalsearch/simplesearchprovider.h
|
||||
globalsearch/suggestionwidget.h
|
||||
|
||||
internet/digitallyimportedclient.h
|
||||
internet/digitallyimportedservicebase.h
|
||||
@ -608,6 +610,7 @@ set(UI
|
||||
globalsearch/globalsearchsettingspage.ui
|
||||
globalsearch/globalsearchview.ui
|
||||
globalsearch/searchproviderstatuswidget.ui
|
||||
globalsearch/suggestionwidget.ui
|
||||
|
||||
internet/digitallyimportedsettingspage.ui
|
||||
internet/groovesharksettingspage.ui
|
||||
|
@ -29,6 +29,7 @@ DigitallyImportedSearchProvider::DigitallyImportedSearchProvider(
|
||||
|
||||
set_safe_words(QStringList() << "sky.fm" << "skyfm" << "di.fm" << "difm"
|
||||
<< "digitallyimported");
|
||||
set_max_suggestion_count(5);
|
||||
|
||||
connect(service_, SIGNAL(StreamsChanged()), SLOT(MaybeRecreateItems()));
|
||||
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <QTimerEvent>
|
||||
#include <QUrl>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
const int GlobalSearch::kDelayedSearchTimeoutMs = 200;
|
||||
const char* GlobalSearch::kSettingsGroup = "GlobalSearch";
|
||||
const int GlobalSearch::kMaxResultsPerEmission = 100;
|
||||
@ -357,22 +359,28 @@ void GlobalSearch::SaveProvidersSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
QStringList GlobalSearch::GetSuggestions(int max) {
|
||||
QStringList GlobalSearch::GetSuggestions(int count) {
|
||||
QStringList ret;
|
||||
QList<SearchProvider*> eligible_providers;
|
||||
|
||||
// Get count suggestions from each provider
|
||||
foreach (SearchProvider* provider, providers_.keys()) {
|
||||
if (is_provider_enabled(provider) && provider->can_give_suggestions()) {
|
||||
eligible_providers << provider;
|
||||
foreach (QString suggestion, provider->GetSuggestions(count)) {
|
||||
suggestion = suggestion.trimmed().toLower();
|
||||
|
||||
if (!suggestion.isEmpty()) {
|
||||
ret << suggestion;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (ret.count() < max && !eligible_providers.isEmpty()) {
|
||||
SearchProvider* provider = eligible_providers.takeAt(qrand() % eligible_providers.count());
|
||||
QString suggestion = provider->GetSuggestion().trimmed();
|
||||
if (!suggestion.isEmpty())
|
||||
ret << suggestion;
|
||||
}
|
||||
// Randomize the suggestions
|
||||
std::random_shuffle(ret.begin(), ret.end());
|
||||
|
||||
// Only return the first count
|
||||
while (ret.length() > count) {
|
||||
ret.removeLast();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ public:
|
||||
int SearchAsync(const QString& query);
|
||||
int LoadArtAsync(const SearchProvider::Result& result);
|
||||
MimeData* LoadTracks(const SearchProvider::ResultList& results);
|
||||
QStringList GetSuggestions(int max);
|
||||
QStringList GetSuggestions(int count);
|
||||
|
||||
void CancelSearch(int id);
|
||||
void CancelArt(int id);
|
||||
|
@ -22,10 +22,12 @@
|
||||
#include "globalsearchview.h"
|
||||
#include "searchprovider.h"
|
||||
#include "searchproviderstatuswidget.h"
|
||||
#include "suggestionwidget.h"
|
||||
#include "ui_globalsearchview.h"
|
||||
#include "core/application.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/mimedata.h"
|
||||
#include "core/timeconstants.h"
|
||||
#include "library/librarymodel.h"
|
||||
|
||||
#include <QMenu>
|
||||
@ -34,6 +36,8 @@
|
||||
#include <QTimer>
|
||||
|
||||
const int GlobalSearchView::kSwapModelsTimeoutMsec = 250;
|
||||
const int GlobalSearchView::kMaxSuggestions = 10;
|
||||
const int GlobalSearchView::kUpdateSuggestionsTimeoutMsec = 60 * kMsecPerSec;
|
||||
|
||||
GlobalSearchView::GlobalSearchView(Application* app, QWidget* parent)
|
||||
: QWidget(parent),
|
||||
@ -48,7 +52,10 @@ GlobalSearchView::GlobalSearchView(Application* app, QWidget* parent)
|
||||
front_proxy_(new GlobalSearchSortModel(this)),
|
||||
back_proxy_(new GlobalSearchSortModel(this)),
|
||||
current_proxy_(front_proxy_),
|
||||
swap_models_timer_(new QTimer(this))
|
||||
swap_models_timer_(new QTimer(this)),
|
||||
update_suggestions_timer_(new QTimer(this)),
|
||||
search_icon_(IconLoader::Load("search")),
|
||||
warning_icon_(IconLoader::Load("dialog-warning"))
|
||||
{
|
||||
ui_->setupUi(this);
|
||||
|
||||
@ -71,8 +78,10 @@ GlobalSearchView::GlobalSearchView(Application* app, QWidget* parent)
|
||||
ui_->help_frame->setBackgroundRole(QPalette::Base);
|
||||
QVBoxLayout* enabled_layout = new QVBoxLayout(ui_->enabled_list);
|
||||
QVBoxLayout* disabled_layout = new QVBoxLayout(ui_->disabled_list);
|
||||
QVBoxLayout* suggestions_layout = new QVBoxLayout(ui_->suggestions_list);
|
||||
enabled_layout->setContentsMargins(16, 0, 16, 6);
|
||||
disabled_layout->setContentsMargins(16, 0, 16, 6);
|
||||
disabled_layout->setContentsMargins(16, 0, 16, 32);
|
||||
suggestions_layout->setContentsMargins(16, 0, 16, 6);
|
||||
|
||||
// Set the colour of the help text to the disabled text colour
|
||||
QPalette help_palette = ui_->help_text->palette();
|
||||
@ -81,6 +90,14 @@ GlobalSearchView::GlobalSearchView(Application* app, QWidget* parent)
|
||||
help_palette.setColor(QPalette::Inactive, QPalette::Text, help_color);
|
||||
ui_->help_text->setPalette(help_palette);
|
||||
|
||||
// Create suggestion widgets
|
||||
for (int i=0 ; i<kMaxSuggestions ; ++i) {
|
||||
SuggestionWidget* widget = new SuggestionWidget(search_icon_);
|
||||
connect(widget, SIGNAL(SuggestionClicked(QString)), SLOT(StartSearch(QString)));
|
||||
suggestions_layout->addWidget(widget);
|
||||
suggestion_widgets_ << widget;
|
||||
}
|
||||
|
||||
// Make it bold
|
||||
QFont help_font = ui_->help_text->font();
|
||||
help_font.setBold(true);
|
||||
@ -99,6 +116,9 @@ GlobalSearchView::GlobalSearchView(Application* app, QWidget* parent)
|
||||
swap_models_timer_->setInterval(kSwapModelsTimeoutMsec);
|
||||
connect(swap_models_timer_, SIGNAL(timeout()), SLOT(SwapModels()));
|
||||
|
||||
update_suggestions_timer_->setInterval(kUpdateSuggestionsTimeoutMsec);
|
||||
connect(update_suggestions_timer_, SIGNAL(timeout()), SLOT(UpdateSuggestions()));
|
||||
|
||||
// These have to be queued connections because they may get emitted before
|
||||
// our call to Search() (or whatever) returns and we add the ID to the map.
|
||||
connect(engine_, SIGNAL(ResultsAvailable(int,SearchProvider::ResultList)),
|
||||
@ -106,8 +126,6 @@ GlobalSearchView::GlobalSearchView(Application* app, QWidget* parent)
|
||||
Qt::QueuedConnection);
|
||||
connect(engine_, SIGNAL(ArtLoaded(int,QPixmap)), SLOT(ArtLoaded(int,QPixmap)),
|
||||
Qt::QueuedConnection);
|
||||
|
||||
ReloadSettings();
|
||||
}
|
||||
|
||||
GlobalSearchView::~GlobalSearchView() {
|
||||
@ -139,7 +157,7 @@ void GlobalSearchView::ReloadSettings() {
|
||||
}
|
||||
|
||||
SearchProviderStatusWidget* widget =
|
||||
new SearchProviderStatusWidget(engine_, provider);
|
||||
new SearchProviderStatusWidget(warning_icon_, engine_, provider);
|
||||
|
||||
parent->layout()->addWidget(widget);
|
||||
provider_status_widgets_ << widget;
|
||||
@ -155,6 +173,19 @@ void GlobalSearchView::ReloadSettings() {
|
||||
back_model_->set_use_pretty_covers(pretty);
|
||||
}
|
||||
|
||||
void GlobalSearchView::UpdateSuggestions() {
|
||||
const QStringList suggestions = engine_->GetSuggestions(kMaxSuggestions);
|
||||
|
||||
for (int i=0 ; i<suggestions.count() ; ++i) {
|
||||
suggestion_widgets_[i]->SetText(suggestions[i]);
|
||||
suggestion_widgets_[i]->show();
|
||||
}
|
||||
|
||||
for (int i=suggestions.count() ; i<kMaxSuggestions ; ++i) {
|
||||
suggestion_widgets_[i]->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalSearchView::StartSearch(const QString& query) {
|
||||
ui_->search->set_text(query);
|
||||
TextEdited(query);
|
||||
@ -365,3 +396,14 @@ void GlobalSearchView::OpenSelectedInNewPlaylist() {
|
||||
data->open_in_new_playlist_ = true;
|
||||
emit AddToPlaylist(data);
|
||||
}
|
||||
|
||||
void GlobalSearchView::showEvent(QShowEvent* e) {
|
||||
UpdateSuggestions();
|
||||
update_suggestions_timer_->start();
|
||||
QWidget::showEvent(e);
|
||||
}
|
||||
|
||||
void GlobalSearchView::hideEvent(QHideEvent* e) {
|
||||
update_suggestions_timer_->stop();
|
||||
QWidget::hideEvent(e);
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
class Application;
|
||||
class GlobalSearchModel;
|
||||
class SearchProviderStatusWidget;
|
||||
class SuggestionWidget;
|
||||
class Ui_GlobalSearchView;
|
||||
|
||||
class QMimeData;
|
||||
@ -42,14 +43,21 @@ public:
|
||||
~GlobalSearchView();
|
||||
|
||||
static const int kSwapModelsTimeoutMsec;
|
||||
static const int kMaxSuggestions;
|
||||
static const int kUpdateSuggestionsTimeoutMsec;
|
||||
|
||||
// Called by the delegate
|
||||
void LazyLoadArt(const QModelIndex& index);
|
||||
|
||||
// QWidget
|
||||
void showEvent(QShowEvent* e);
|
||||
void hideEvent(QHideEvent* e);
|
||||
|
||||
// QObject
|
||||
bool eventFilter(QObject* object, QEvent* event);
|
||||
|
||||
public slots:
|
||||
void ReloadSettings();
|
||||
void StartSearch(const QString& query);
|
||||
|
||||
signals:
|
||||
@ -57,7 +65,7 @@ signals:
|
||||
void OpenSettingsAtPage(SettingsDialog::Page page);
|
||||
|
||||
private slots:
|
||||
void ReloadSettings();
|
||||
void UpdateSuggestions();
|
||||
|
||||
void SwapModels();
|
||||
void TextEdited(const QString& text);
|
||||
@ -99,8 +107,13 @@ private:
|
||||
QMap<int, QModelIndex> art_requests_;
|
||||
|
||||
QTimer* swap_models_timer_;
|
||||
QTimer* update_suggestions_timer_;
|
||||
|
||||
QList<SearchProviderStatusWidget*> provider_status_widgets_;
|
||||
QList<SuggestionWidget*> suggestion_widgets_;
|
||||
|
||||
QIcon search_icon_;
|
||||
QIcon warning_icon_;
|
||||
};
|
||||
|
||||
#endif // GLOBALSEARCHVIEW_H
|
||||
|
@ -67,85 +67,102 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="help_frame">
|
||||
<property name="autoFillBackground">
|
||||
<widget class="QScrollArea" name="help_frame">
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>32</number>
|
||||
<widget class="QWidget" name="help_frame_contents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>435</width>
|
||||
<height>605</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>32</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>32</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>64</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="help_text">
|
||||
<property name="text">
|
||||
<string>Enter search terms above to find music on your computer and on the internet</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Clementine will find music in:</string>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>16</number>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>32</number>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="enabled_list" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="disabled_label">
|
||||
<property name="text">
|
||||
<string>But these sources are disabled:</string>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>64</number>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="disabled_list" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Why not try...</string>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="help_text">
|
||||
<property name="text">
|
||||
<string>Enter search terms above to find music on your computer and on the internet</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Clementine will find music in:</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="enabled_list" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="disabled_label">
|
||||
<property name="text">
|
||||
<string>But these sources are disabled:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="disabled_list" native="true"/>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="suggestions_list" native="true"/>
|
||||
</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>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -29,6 +29,7 @@ LastFMSearchProvider::LastFMSearchProvider(LastFMService* service,
|
||||
icon_ = ScaleAndPad(QImage(":last.fm/as.png"));
|
||||
|
||||
set_safe_words(QStringList() << "lastfm" << "last.fm");
|
||||
set_max_suggestion_count(3);
|
||||
|
||||
connect(service, SIGNAL(SavedItemsChanged()), SLOT(MaybeRecreateItems()));
|
||||
|
||||
|
@ -75,7 +75,7 @@ MimeData* LibrarySearchProvider::LoadTracks(const ResultList& results) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString LibrarySearchProvider::GetSuggestion() {
|
||||
QStringList LibrarySearchProvider::GetSuggestions(int count) {
|
||||
// We'd like to use order by random(), but that's O(n) in sqlite, so instead
|
||||
// get the largest ROWID and pick a couple of random numbers within that
|
||||
// range.
|
||||
@ -86,13 +86,19 @@ QString LibrarySearchProvider::GetSuggestion() {
|
||||
q.SetIncludeUnavailable(true);
|
||||
q.SetLimit(1);
|
||||
|
||||
QStringList ret;
|
||||
|
||||
if (!backend_->ExecQuery(&q) || !q.Next()) {
|
||||
return QString();
|
||||
return ret;
|
||||
}
|
||||
|
||||
const int largest_rowid = q.Value(0).toInt();
|
||||
|
||||
for (int attempt=0 ; attempt<10 ; ++attempt) {
|
||||
for (int attempt=0 ; attempt<count*5 ; ++attempt) {
|
||||
if (ret.count() >= count) {
|
||||
break;
|
||||
}
|
||||
|
||||
LibraryQuery q;
|
||||
q.SetColumnSpec("artist, album");
|
||||
q.SetIncludeUnavailable(true);
|
||||
@ -107,12 +113,12 @@ QString LibrarySearchProvider::GetSuggestion() {
|
||||
const QString album = q.Value(1).toString();
|
||||
|
||||
if (!artist.isEmpty() && !album.isEmpty())
|
||||
return (qrand() % 2 == 0) ? artist : album;
|
||||
ret << ((qrand() % 2 == 0) ? artist : album);
|
||||
else if (!artist.isEmpty())
|
||||
return artist;
|
||||
ret << artist;
|
||||
else if (!album.isEmpty())
|
||||
return album;
|
||||
ret << album;
|
||||
}
|
||||
|
||||
return QString();
|
||||
return ret;
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ public:
|
||||
|
||||
ResultList Search(int id, const QString& query);
|
||||
MimeData* LoadTracks(const ResultList& results);
|
||||
QString GetSuggestion();
|
||||
QStringList GetSuggestions(int count);
|
||||
|
||||
private:
|
||||
LibraryBackendInterface* backend_;
|
||||
|
@ -28,6 +28,8 @@ SavedRadioSearchProvider::SavedRadioSearchProvider(SavedRadio* service,
|
||||
Init(tr("Your radio streams"), "savedradio", IconLoader::Load("document-open-remote"),
|
||||
MimeDataContainsUrlsOnly);
|
||||
|
||||
set_max_suggestion_count(3);
|
||||
|
||||
connect(service_, SIGNAL(StreamsChanged()), SLOT(MaybeRecreateItems()));
|
||||
|
||||
RecreateItems();
|
||||
|
@ -129,10 +129,10 @@ public:
|
||||
// Result, unless the MimeDataContainsUrlsOnly flag is set.
|
||||
virtual MimeData* LoadTracks(const ResultList& results);
|
||||
|
||||
// Returns an example search string to display in the UI. The provider should
|
||||
// pick one of its items at random. Remember to set the CanGiveSuggestions
|
||||
// hint.
|
||||
virtual QString GetSuggestion() { return QString(); }
|
||||
// Returns some example search strings to display in the UI. The provider
|
||||
// should pick some of its items at random and return between 0 and count
|
||||
// strings. Remember to set the CanGiveSuggestions hint.
|
||||
virtual QStringList GetSuggestions(int count) { return QStringList(); }
|
||||
|
||||
// If provider needs user login to search and play songs, this method should
|
||||
// be reimplemented
|
||||
|
@ -24,9 +24,10 @@
|
||||
|
||||
#include <QMouseEvent>
|
||||
|
||||
SearchProviderStatusWidget::SearchProviderStatusWidget(GlobalSearch* engine,
|
||||
SearchProvider* provider,
|
||||
QWidget* parent)
|
||||
SearchProviderStatusWidget::SearchProviderStatusWidget(
|
||||
const QIcon& warning_icon,
|
||||
GlobalSearch* engine, SearchProvider* provider,
|
||||
QWidget* parent)
|
||||
: QWidget(parent),
|
||||
ui_(new Ui_SearchProviderStatusWidget),
|
||||
engine_(engine),
|
||||
@ -51,7 +52,7 @@ SearchProviderStatusWidget::SearchProviderStatusWidget(GlobalSearch* engine,
|
||||
|
||||
ui_->disabled_reason->setMinimumWidth(disabled_width);
|
||||
ui_->disabled_reason->setText(logged_in ? disabled_text : not_logged_in_text);
|
||||
ui_->disabled_icon->setPixmap(IconLoader::Load("dialog-warning").pixmap(16));
|
||||
ui_->disabled_icon->setPixmap(warning_icon.pixmap(16));
|
||||
|
||||
ui_->disabled_reason->installEventFilter(this);
|
||||
}
|
||||
|
@ -26,7 +26,8 @@ class Ui_SearchProviderStatusWidget;
|
||||
|
||||
class SearchProviderStatusWidget : public QWidget {
|
||||
public:
|
||||
SearchProviderStatusWidget(GlobalSearch* engine, SearchProvider* provider,
|
||||
SearchProviderStatusWidget(const QIcon& warning_icon,
|
||||
GlobalSearch* engine, SearchProvider* provider,
|
||||
QWidget* parent = 0);
|
||||
~SearchProviderStatusWidget();
|
||||
|
||||
|
@ -39,6 +39,7 @@ SimpleSearchProvider::Item::Item(const Song& song, const QString& keyword)
|
||||
SimpleSearchProvider::SimpleSearchProvider(Application* app, QObject* parent)
|
||||
: BlockingSearchProvider(app, parent),
|
||||
result_limit_(kDefaultResultLimit),
|
||||
max_suggestion_count_(-1),
|
||||
items_dirty_(true),
|
||||
has_searched_before_(false)
|
||||
{
|
||||
@ -99,19 +100,28 @@ void SimpleSearchProvider::SetItems(const ItemList& items) {
|
||||
}
|
||||
}
|
||||
|
||||
QString SimpleSearchProvider::GetSuggestion() {
|
||||
QStringList SimpleSearchProvider::GetSuggestions(int count) {
|
||||
if (max_suggestion_count_ != -1) {
|
||||
count = qMin(max_suggestion_count_, count);
|
||||
}
|
||||
|
||||
QStringList ret;
|
||||
QMutexLocker l(&items_mutex_);
|
||||
|
||||
if (items_.isEmpty())
|
||||
return QString();
|
||||
return ret;
|
||||
|
||||
for (int attempt=0 ; attempt<count*5 ; ++attempt) {
|
||||
if (ret.count() >= count) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (int attempt=0 ; attempt<10 ; ++attempt) {
|
||||
const Item& item = items_[qrand() % items_.count()];
|
||||
if (!item.keyword_.isEmpty())
|
||||
return item.keyword_;
|
||||
ret << item.keyword_;
|
||||
if (!item.metadata_.title().isEmpty())
|
||||
return item.metadata_.title();
|
||||
ret << item.metadata_.title();
|
||||
}
|
||||
|
||||
return QString();
|
||||
return ret;
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ public:
|
||||
ResultList Search(int id, const QString& query);
|
||||
|
||||
// SearchProvider
|
||||
QString GetSuggestion();
|
||||
QStringList GetSuggestions(int count);
|
||||
|
||||
protected slots:
|
||||
// Calls RecreateItems now if the user has done a global search with this
|
||||
@ -54,6 +54,7 @@ protected:
|
||||
|
||||
int result_limit() const { return result_limit_; }
|
||||
void set_result_limit(int result_limit) { result_limit_ = result_limit; }
|
||||
void set_max_suggestion_count(int count) { max_suggestion_count_ = count; }
|
||||
QStringList safe_words() const { return safe_words_; }
|
||||
void set_safe_words(const QStringList& safe_words) { safe_words_ = safe_words; }
|
||||
|
||||
@ -66,6 +67,7 @@ protected:
|
||||
private:
|
||||
int result_limit_;
|
||||
QStringList safe_words_;
|
||||
int max_suggestion_count_;
|
||||
|
||||
QMutex items_mutex_;
|
||||
ItemList items_;
|
||||
|
@ -24,6 +24,7 @@ SomaFMSearchProvider::SomaFMSearchProvider(SomaFMService* service, Application*
|
||||
{
|
||||
Init("SomaFM", "somafm", QIcon(":/providers/somafm.png"), CanGiveSuggestions);
|
||||
set_result_limit(3);
|
||||
set_max_suggestion_count(3);
|
||||
icon_ = ScaleAndPad(QImage(":/providers/somafm.png"));
|
||||
|
||||
connect(service, SIGNAL(StreamsChanged()), SLOT(MaybeRecreateItems()));
|
||||
|
76
src/globalsearch/suggestionwidget.cpp
Normal file
76
src/globalsearch/suggestionwidget.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2012, David Sansome <me@davidsansome.com>
|
||||
|
||||
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 "suggestionwidget.h"
|
||||
#include "ui_suggestionwidget.h"
|
||||
#include "ui/iconloader.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
|
||||
SuggestionWidget::SuggestionWidget(const QIcon& search_icon, QWidget* parent)
|
||||
: QWidget(parent),
|
||||
ui_(new Ui_SuggestionWidget)
|
||||
{
|
||||
ui_->setupUi(this);
|
||||
|
||||
ui_->icon->setPixmap(search_icon.pixmap(16));
|
||||
ui_->name->installEventFilter(this);
|
||||
}
|
||||
|
||||
SuggestionWidget::~SuggestionWidget() {
|
||||
delete ui_;
|
||||
}
|
||||
|
||||
void SuggestionWidget::SetText(const QString& text) {
|
||||
ui_->name->setText(text);
|
||||
}
|
||||
|
||||
bool SuggestionWidget::eventFilter(QObject* object, QEvent* event) {
|
||||
if (object != ui_->name) {
|
||||
return QWidget::eventFilter(object, event);
|
||||
}
|
||||
|
||||
QFont font(ui_->name->font());
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::Enter:
|
||||
font.setUnderline(true);
|
||||
ui_->name->setFont(font);
|
||||
break;
|
||||
|
||||
case QEvent::Leave:
|
||||
font.setUnderline(false);
|
||||
ui_->name->setFont(font);
|
||||
break;
|
||||
|
||||
case QEvent::MouseButtonRelease: {
|
||||
QMouseEvent* e = static_cast<QMouseEvent*>(event);
|
||||
if (e->button() == Qt::LeftButton) {
|
||||
QString text = ui_->name->text();
|
||||
text.replace(QRegExp("\\W"), " ");
|
||||
|
||||
emit SuggestionClicked(text.simplified());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
44
src/globalsearch/suggestionwidget.h
Normal file
44
src/globalsearch/suggestionwidget.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2012, David Sansome <me@davidsansome.com>
|
||||
|
||||
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 SUGGESTIONWIDGET_H
|
||||
#define SUGGESTIONWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class Ui_SuggestionWidget;
|
||||
|
||||
class SuggestionWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SuggestionWidget(const QIcon& search_icon, QWidget* parent = 0);
|
||||
~SuggestionWidget();
|
||||
|
||||
bool eventFilter(QObject* object, QEvent* event);
|
||||
|
||||
public slots:
|
||||
void SetText(const QString& text);
|
||||
|
||||
signals:
|
||||
void SuggestionClicked(const QString& query);
|
||||
|
||||
private:
|
||||
Ui_SuggestionWidget* ui_;
|
||||
};
|
||||
|
||||
#endif // SUGGESTIONWIDGET_H
|
53
src/globalsearch/suggestionwidget.ui
Normal file
53
src/globalsearch/suggestionwidget.ui
Normal file
@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SuggestionWidget</class>
|
||||
<widget class="QWidget" name="SuggestionWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>464</width>
|
||||
<height>110</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="icon">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="name">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -219,6 +219,7 @@ MainWindow::MainWindow(Application* app,
|
||||
IconLoader::Load("folder-sound"), true, app_, this));
|
||||
|
||||
app_->global_search()->ReloadSettings();
|
||||
global_search_view_->ReloadSettings();
|
||||
|
||||
connect(global_search_view_, SIGNAL(AddToPlaylist(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
|
||||
connect(global_search_view_, SIGNAL(OpenSettingsAtPage(SettingsDialog::Page)), SLOT(OpenSettingsDialogAtPage(SettingsDialog::Page)));
|
||||
|
Loading…
x
Reference in New Issue
Block a user