Add global search suggestions

This commit is contained in:
David Sansome 2012-06-10 23:05:30 +01:00
parent fe4e214a78
commit 9d6b72b4ab
21 changed files with 395 additions and 113 deletions

View File

@ -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

View File

@ -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()));

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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>

View File

@ -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()));

View File

@ -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;
}

View File

@ -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_;

View File

@ -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();

View File

@ -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

View File

@ -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);
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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_;

View File

@ -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()));

View 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;
}

View 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

View 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>

View File

@ -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)));