1
0
mirror of https://github.com/clementine-player/Clementine synced 2025-01-31 11:35:24 +01:00

Hash the information that's used by GlobalSearchWidget::CanCombineResults, avoiding doing a O(n^2) search when adding new results.

This commit is contained in:
David Sansome 2011-11-08 21:41:41 +00:00
parent b3f6e46c23
commit d7720223ec
3 changed files with 107 additions and 23 deletions

View File

@ -82,6 +82,9 @@ GlobalSearchWidget::GlobalSearchWidget(QWidget* parent)
back_proxy_->setDynamicSortFilter(true);
back_proxy_->sort(0);
combine_cache_[front_model_] = new CombineCache(front_model_);
combine_cache_[back_model_] = new CombineCache(back_model_);
// Set up the popup
view_->setObjectName("popup");
view_->setWindowFlags(Qt::Popup);
@ -142,6 +145,7 @@ GlobalSearchWidget::GlobalSearchWidget(QWidget* parent)
GlobalSearchWidget::~GlobalSearchWidget() {
delete ui_;
qDeleteAll(combine_cache_.values());
}
void GlobalSearchWidget::Init(GlobalSearch* engine) {
@ -229,6 +233,7 @@ void GlobalSearchWidget::TextEdited(const QString& text) {
// Add results to the back model, switch models after some delay.
back_model_->clear();
combine_cache_[back_model_]->Clear();
current_model_ = back_model_;
current_proxy_ = back_proxy_;
order_arrived_counter_ = 0;
@ -270,35 +275,30 @@ void GlobalSearchWidget::AddResults(int id, const SearchProvider::ResultList& re
current_model_->appendRow(item);
QModelIndex index = item->index();
combine_cache_[current_model_]->Insert(index);
if (combine_identical_results_) {
// Maybe we can combine this result with an identical result from another
// provider. We can use the sorted model to narrow the scope of the
// search a bit - look at the result after the current one, then all the
// results before.
QModelIndex my_proxy_index = current_proxy_->mapFromSource(item->index());
QModelIndexList candidates;
candidates << my_proxy_index.sibling(my_proxy_index.row() + 1, 0);
// provider.
QModelIndexList candidates = combine_cache_[current_model_]->FindCandidates(index);
for (int i=my_proxy_index.row()-1 ; i>=0 ; --i) {
candidates << my_proxy_index.sibling(i, 0);
}
foreach (const QModelIndex& index, candidates) {
if (!index.isValid())
foreach (const QModelIndex& candidate, candidates) {
if (!candidate.isValid())
continue;
CombineAction action = CanCombineResults(my_proxy_index, index);
CombineAction action = CanCombineResults(index, candidate);
switch (action) {
case CannotCombine:
continue;
case LeftPreferred:
CombineResults(my_proxy_index, index);
CombineResults(index, candidate);
break;
case RightPreferred:
CombineResults(index, my_proxy_index);
CombineResults(candidate, index);
break;
}
@ -614,6 +614,8 @@ GlobalSearchWidget::CombineAction GlobalSearchWidget::CanCombineResults(
const SearchProvider::Result r2 = right.data(Role_PrimaryResult)
.value<SearchProvider::Result>();
// If you change the logic here remember to change CombineCache::Hash too.
if (r1.match_quality_ != r2.match_quality_ || r1.type_ != r2.type_)
return CannotCombine;
@ -645,8 +647,8 @@ GlobalSearchWidget::CombineAction GlobalSearchWidget::CanCombineResults(
}
void GlobalSearchWidget::CombineResults(const QModelIndex& superior, const QModelIndex& inferior) {
QStandardItem* superior_item = current_model_->itemFromIndex(current_proxy_->mapToSource(superior));
QStandardItem* inferior_item = current_model_->itemFromIndex(current_proxy_->mapToSource(inferior));
QStandardItem* superior_item = current_model_->itemFromIndex(superior);
QStandardItem* inferior_item = current_model_->itemFromIndex(inferior);
SearchProvider::ResultList superior_results =
superior_item->data(Role_AllResults).value<SearchProvider::ResultList>();
@ -656,6 +658,7 @@ void GlobalSearchWidget::CombineResults(const QModelIndex& superior, const QMode
superior_results.append(inferior_results);
superior_item->setData(QVariant::fromValue(superior_results), Role_AllResults);
combine_cache_[current_model_]->Remove(inferior_item->index());
current_model_->invisibleRootItem()->removeRow(inferior_item->row());
}
@ -710,3 +713,66 @@ void GlobalSearchWidget::NextSuggestion() {
ui_->search->set_hint(hint);
}
GlobalSearchWidget::CombineCache::CombineCache(QAbstractItemModel* model)
: model_(model)
{
}
uint GlobalSearchWidget::CombineCache::Hash(const QModelIndex& index) {
const SearchProvider::Result r = index.data(Role_PrimaryResult)
.value<SearchProvider::Result>();
uint ret = qHash(r.match_quality_) ^ qHash(r.type_);
switch (r.type_) {
case globalsearch::Type_Track:
ret ^= qHash(r.metadata_.title());
// fallthrough
case globalsearch::Type_Album:
ret ^= qHash(r.metadata_.album());
ret ^= qHash(r.metadata_.artist());
break;
case globalsearch::Type_Stream:
ret ^= qHash(r.metadata_.url().toString());
break;
}
return ret;
}
void GlobalSearchWidget::CombineCache::Insert(const QModelIndex& index) {
data_.insert(Hash(index), index.row());
}
void GlobalSearchWidget::CombineCache::Remove(const QModelIndex& index) {
// This is really inefficient but we're not doing it much - find any items
// with a row greater than this one and shuffle them down one.
for (QMultiMap<uint, int>::iterator it = data_.begin() ; it != data_.end() ; ++it) {
if (it.value() > index.row())
(*it) --;
}
// Now remove the row itself.
QMultiMap<uint, int>::iterator it = data_.find(Hash(index), index.row());
if (it != data_.end())
data_.erase(it);
}
QModelIndexList GlobalSearchWidget::CombineCache::FindCandidates(
const QModelIndex& result) const {
QModelIndexList ret;
foreach (int row, data_.values(Hash(result))) {
if (row != result.row()) {
ret << model_->index(row, 0);
}
}
return ret;
}
void GlobalSearchWidget::CombineCache::Clear() {
data_.clear();
}

View File

@ -112,6 +112,22 @@ private:
RightPreferred // The two results can be combined - the right one is better
};
class CombineCache {
public:
CombineCache(QAbstractItemModel* model);
QModelIndexList FindCandidates(const QModelIndex& result) const;
void Insert(const QModelIndex& index);
void Remove(const QModelIndex& index);
void Clear();
static uint Hash(const QModelIndex& index);
private:
QAbstractItemModel* model_;
QMultiMap<uint, int> data_;
};
void RepositionPopup();
CombineAction CanCombineResults(const QModelIndex& left, const QModelIndex& right) const;
void CombineResults(const QModelIndex& superior, const QModelIndex& inferior);
@ -140,6 +156,8 @@ private:
QStandardItemModel* back_model_;
QStandardItemModel* current_model_;
QMap<QStandardItemModel*, CombineCache*> combine_cache_;
QSortFilterProxyModel* front_proxy_;
QSortFilterProxyModel* back_proxy_;
QSortFilterProxyModel* current_proxy_;

View File

@ -335,7 +335,7 @@ msgstr ""
msgid "Add action"
msgstr ""
#: globalsearch/globalsearchwidget.cpp:101
#: globalsearch/globalsearchwidget.cpp:104
msgid "Add and play now"
msgstr ""
@ -439,7 +439,7 @@ msgstr ""
msgid "Add to another playlist"
msgstr ""
#: globalsearch/globalsearchwidget.cpp:100
#: globalsearch/globalsearchwidget.cpp:103
#: ../bin/src/ui_albumcovermanager.h:152
msgid "Add to playlist"
msgstr ""
@ -3095,7 +3095,7 @@ msgstr ""
msgid "Queue selected tracks"
msgstr ""
#: globalsearch/globalsearchwidget.cpp:102 library/libraryview.cpp:251
#: globalsearch/globalsearchwidget.cpp:105 library/libraryview.cpp:251
#: ui/mainwindow.cpp:1338
msgid "Queue track"
msgstr ""
@ -3234,11 +3234,11 @@ msgstr ""
msgid "Repeat track"
msgstr ""
#: globalsearch/globalsearchwidget.cpp:104
#: globalsearch/globalsearchwidget.cpp:107
msgid "Replace and play now"
msgstr ""
#: devices/deviceview.cpp:218 globalsearch/globalsearchwidget.cpp:103
#: devices/deviceview.cpp:218 globalsearch/globalsearchwidget.cpp:106
#: internet/internetservice.cpp:63 library/libraryview.cpp:245
#: widgets/fileviewlist.cpp:33
msgid "Replace current playlist"
@ -4542,7 +4542,7 @@ msgstr ""
msgid "does not contain"
msgstr ""
#: globalsearch/globalsearchwidget.cpp:708
#: globalsearch/globalsearchwidget.cpp:711
msgid "e.g."
msgstr ""