Rewrite collection model and search

Fixes #392
This commit is contained in:
Jonas Kvinge
2021-06-27 22:54:08 +02:00
parent ea1e4541c0
commit e477449cd4
52 changed files with 2321 additions and 2637 deletions

View File

@@ -131,10 +131,11 @@
#include "collection/collection.h"
#include "collection/collectionbackend.h"
#include "collection/collectiondirectorymodel.h"
#include "collection/collectionviewcontainer.h"
#include "collection/collectionfilterwidget.h"
#include "collection/collectionfilter.h"
#include "collection/collectionmodel.h"
#include "collection/collectionview.h"
#include "collection/collectionviewcontainer.h"
#include "playlist/playlist.h"
#include "playlist/playlistbackend.h"
#include "playlist/playlistcontainer.h"
@@ -335,7 +336,6 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
playlist_add_to_another_(nullptr),
playlistitem_actions_separator_(nullptr),
playlist_rescan_songs_(nullptr),
collection_sort_model_(new QSortFilterProxyModel(this)),
track_position_timer_(new QTimer(this)),
track_slider_timer_(new QTimer(this)),
keep_running_(false),
@@ -416,23 +416,13 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
ui_->volume->SetValue(volume);
VolumeChanged(volume);
// Models
qLog(Debug) << "Creating models";
collection_sort_model_->setSourceModel(app_->collection()->model());
collection_sort_model_->setSortRole(CollectionModel::Role_SortText);
collection_sort_model_->setDynamicSortFilter(true);
collection_sort_model_->setSortLocaleAware(true);
collection_sort_model_->sort(0);
qLog(Debug) << "Creating models finished";
QObject::connect(ui_->playlist, &PlaylistContainer::ViewSelectionModelChanged, this, &MainWindow::PlaylistViewSelectionModelChanged);
ui_->playlist->SetManager(app_->playlist_manager());
ui_->playlist->view()->Init(app_);
collection_view_->view()->setModel(collection_sort_model_);
collection_view_->view()->setModel(app_->collection()->model()->filter());
collection_view_->view()->SetApplication(app_);
#ifndef Q_OS_WIN
device_view_->view()->SetApplication(app_);
@@ -692,7 +682,7 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
QAction *collection_config_action = new QAction(IconLoader::Load(QStringLiteral("configure")), tr("Configure collection..."), this);
QObject::connect(collection_config_action, &QAction::triggered, this, &MainWindow::ShowCollectionConfig);
collection_view_->filter_widget()->SetSettingsGroup(QLatin1String(CollectionSettingsPage::kSettingsGroup));
collection_view_->filter_widget()->Init(app_->collection()->model());
collection_view_->filter_widget()->Init(app_->collection()->model(), app_->collection()->model()->filter());
QAction *separator = new QAction(this);
separator->setSeparator(true);

View File

@@ -68,6 +68,7 @@ class AlbumCoverManager;
class Application;
class ContextView;
class CollectionViewContainer;
class CollectionFilter;
class AlbumCoverChoiceController;
class CommandlineOptions;
#ifndef Q_OS_WIN
@@ -376,8 +377,6 @@ class MainWindow : public QMainWindow, public PlatformInterface {
QModelIndex playlist_menu_index_;
QSortFilterProxyModel *collection_sort_model_;
QTimer *track_position_timer_;
QTimer *track_slider_timer_;
Settings settings_;

View File

@@ -33,9 +33,9 @@
template<typename T>
class SimpleTreeItem {
public:
explicit SimpleTreeItem(int _type, SimpleTreeModel<T> *_model); // For the root item
explicit SimpleTreeItem(int _type, const QString &_key, T *_parent = nullptr);
explicit SimpleTreeItem(int _type, T *_parent = nullptr);
explicit SimpleTreeItem(SimpleTreeModel<T> *_model); // For the root item
explicit SimpleTreeItem(const QString &_key, T *_parent = nullptr);
explicit SimpleTreeItem(T *_parent = nullptr);
virtual ~SimpleTreeItem();
void InsertNotify(T *_parent);
@@ -49,13 +49,11 @@ class SimpleTreeItem {
QString DisplayText() const { return display_text; }
QString SortText() const { return sort_text; }
int type;
QString key;
QString container_key;
QString sort_text;
QString display_text;
int row;
bool lazy_loaded;
T *parent;
QList<T*> children;
@@ -65,19 +63,15 @@ class SimpleTreeItem {
};
template<typename T>
SimpleTreeItem<T>::SimpleTreeItem(int _type, SimpleTreeModel<T> *_model)
: type(_type),
row(0),
lazy_loaded(true),
SimpleTreeItem<T>::SimpleTreeItem(SimpleTreeModel<T> *_model)
: row(0),
parent(nullptr),
child_model(nullptr),
model(_model) {}
template<typename T>
SimpleTreeItem<T>::SimpleTreeItem(int _type, const QString &_key, T *_parent)
: type(_type),
key(_key),
lazy_loaded(false),
SimpleTreeItem<T>::SimpleTreeItem(const QString &_container_key, T *_parent)
: container_key(_container_key),
parent(_parent),
child_model(nullptr),
model(_parent ? _parent->model : nullptr) {
@@ -88,10 +82,8 @@ SimpleTreeItem<T>::SimpleTreeItem(int _type, const QString &_key, T *_parent)
}
template<typename T>
SimpleTreeItem<T>::SimpleTreeItem(int _type, T *_parent)
: type(_type),
lazy_loaded(false),
parent(_parent),
SimpleTreeItem<T>::SimpleTreeItem(T *_parent)
: parent(_parent),
child_model(nullptr),
model(_parent ? _parent->model : nullptr) {
if (parent) {
@@ -112,7 +104,7 @@ void SimpleTreeItem<T>::InsertNotify(T *_parent) {
}
template<typename T>
void SimpleTreeItem<T>::DeleteNotify(int child_row) {
void SimpleTreeItem<T>::DeleteNotify(const int child_row) {
model->BeginDelete(static_cast<T*>(this), child_row);
delete children.takeAt(child_row);

View File

@@ -35,12 +35,10 @@ class SimpleTreeModel : public QAbstractItemModel {
// QAbstractItemModel
int columnCount(const QModelIndex &parent) const override;
QModelIndex index(int row, int, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex index(const int row, const int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &idx) const override;
int rowCount(const QModelIndex &parent) const override;
bool hasChildren(const QModelIndex &parent) const override;
bool canFetchMore(const QModelIndex &parent) const override;
void fetchMore(const QModelIndex &parent) override;
T *IndexToItem(const QModelIndex &idx) const;
QModelIndex ItemToIndex(T *item) const;
@@ -52,9 +50,6 @@ class SimpleTreeModel : public QAbstractItemModel {
void EndDelete();
void EmitDataChanged(T *item);
protected:
virtual void LazyPopulate(T *item) { item->lazy_loaded = true; }
protected:
T *root_;
};
@@ -81,7 +76,9 @@ int SimpleTreeModel<T>::columnCount(const QModelIndex&) const {
}
template<typename T>
QModelIndex SimpleTreeModel<T>::index(int row, int, const QModelIndex &parent) const {
QModelIndex SimpleTreeModel<T>::index(const int row, const int column, const QModelIndex &parent) const {
Q_UNUSED(column);
T *parent_item = IndexToItem(parent);
if (!parent_item || row < 0 || parent_item->children.count() <= row)
@@ -106,25 +103,8 @@ int SimpleTreeModel<T>::rowCount(const QModelIndex &parent) const {
template<typename T>
bool SimpleTreeModel<T>::hasChildren(const QModelIndex &parent) const {
T *item = IndexToItem(parent);
if (!item) return false;
if (item->lazy_loaded)
return !item->children.isEmpty();
else
return true;
}
template<typename T>
bool SimpleTreeModel<T>::canFetchMore(const QModelIndex &parent) const {
T *item = IndexToItem(parent);
return item && !item->lazy_loaded;
}
template<typename T>
void SimpleTreeModel<T>::fetchMore(const QModelIndex &parent) {
T *item = IndexToItem(parent);
if (item && !item->lazy_loaded) {
LazyPopulate(item);
}
if (!item) return 0;
return !item->children.isEmpty();
}
template<typename T>

View File

@@ -144,35 +144,41 @@ const QStringList Song::kColumns = QStringList() << QStringLiteral("title")
const QStringList Song::kRowIdColumns = QStringList() << QStringLiteral("ROWID") << kColumns;
const QString Song::kColumnSpec = Song::kColumns.join(QStringLiteral(", "));
const QString Song::kRowIdColumnSpec = Song::kRowIdColumns.join(QStringLiteral(", "));
const QString Song::kBindSpec = Utilities::Prepend(QStringLiteral(":"), Song::kColumns).join(QStringLiteral(", "));
const QString Song::kUpdateSpec = Utilities::Updateify(Song::kColumns).join(QStringLiteral(", "));
const QString Song::kColumnSpec = kColumns.join(QStringLiteral(", "));
const QString Song::kRowIdColumnSpec = kRowIdColumns.join(QStringLiteral(", "));
const QString Song::kBindSpec = Utilities::Prepend(QStringLiteral(":"), kColumns).join(QStringLiteral(", "));
const QString Song::kUpdateSpec = Utilities::Updateify(kColumns).join(QStringLiteral(", "));
// used to indicate, what columns can be filtered numerically. Used by the CollectionQuery.
const QStringList Song::kNumericalColumns = QStringList() << QStringLiteral("year")
<< QStringLiteral("length")
<< QStringLiteral("samplerate")
<< QStringLiteral("bitdepth")
<< QStringLiteral("bitrate")
<< QStringLiteral("rating")
<< QStringLiteral("playcount")
<< QStringLiteral("skipcount");
const QStringList Song::kTextSearchColumns = QStringList() << QStringLiteral("title")
<< QStringLiteral("album")
<< QStringLiteral("artist")
<< QStringLiteral("albumartist")
<< QStringLiteral("composer")
<< QStringLiteral("performer")
<< QStringLiteral("grouping")
<< QStringLiteral("genre")
<< QStringLiteral("comment");
const QStringList Song::kIntSearchColumns = QStringList() << QStringLiteral("track")
<< QStringLiteral("year")
<< QStringLiteral("samplerate")
<< QStringLiteral("bitdepth")
<< QStringLiteral("bitrate");
const QStringList Song::kFtsColumns = QStringList() << QStringLiteral("ftstitle")
<< QStringLiteral("ftsalbum")
<< QStringLiteral("ftsartist")
<< QStringLiteral("ftsalbumartist")
<< QStringLiteral("ftscomposer")
<< QStringLiteral("ftsperformer")
<< QStringLiteral("ftsgrouping")
<< QStringLiteral("ftsgenre")
<< QStringLiteral("ftscomment");
const QStringList Song::kUIntSearchColumns = QStringList() << QStringLiteral("playcount")
<< QStringLiteral("skipcount");
const QString Song::kFtsColumnSpec = Song::kFtsColumns.join(QStringLiteral(", "));
const QString Song::kFtsBindSpec = Utilities::Prepend(QStringLiteral(":"), Song::kFtsColumns).join(QStringLiteral(", "));
const QString Song::kFtsUpdateSpec = Utilities::Updateify(Song::kFtsColumns).join(QStringLiteral(", "));
const QStringList Song::kInt64SearchColumns = QStringList() << QStringLiteral("length");
const QStringList Song::kFloatSearchColumns = QStringList() << QStringLiteral("rating");
const QStringList Song::kNumericalSearchColumns = QStringList() << kIntSearchColumns
<< kUIntSearchColumns
<< kInt64SearchColumns
<< kFloatSearchColumns;
const QStringList Song::kSearchColumns = QStringList() << kTextSearchColumns
<< kNumericalSearchColumns;
const Song::RegularExpressionList Song::kAlbumDisc = Song::RegularExpressionList()
<< QRegularExpression(QStringLiteral("\\s+-*\\s*(Disc|CD)\\s*([0-9]{1,2})$"), QRegularExpression::CaseInsensitiveOption)
@@ -199,10 +205,33 @@ const Song::RegularExpressionList Song::kTitleMisc = Song::RegularExpressionList
const QStringList Song::kArticles = QStringList() << QStringLiteral("the ") << QStringLiteral("a ") << QStringLiteral("an ");
const QStringList Song::kAcceptedExtensions = QStringList() << QStringLiteral("wav") << QStringLiteral("flac") << QStringLiteral("wv") << QStringLiteral("ogg") << QStringLiteral("oga") << QStringLiteral("opus") << QStringLiteral("spx") << QStringLiteral("ape") << QStringLiteral("mpc")
<< QStringLiteral("mp2") << QStringLiteral("mp3") << QStringLiteral("m4a") << QStringLiteral("mp4") << QStringLiteral("aac") << QStringLiteral("asf") << QStringLiteral("asx") << QStringLiteral("wma")
<< QStringLiteral("aif << aiff") << QStringLiteral("mka") << QStringLiteral("tta") << QStringLiteral("dsf") << QStringLiteral("dsd")
<< QStringLiteral("ac3") << QStringLiteral("dts") << QStringLiteral("spc") << QStringLiteral("vgm");
const QStringList Song::kAcceptedExtensions = QStringList() << QStringLiteral("wav")
<< QStringLiteral("flac")
<< QStringLiteral("wv")
<< QStringLiteral("ogg")
<< QStringLiteral("oga")
<< QStringLiteral("opus")
<< QStringLiteral("spx")
<< QStringLiteral("ape")
<< QStringLiteral("mpc")
<< QStringLiteral("mp2")
<< QStringLiteral("mp3")
<< QStringLiteral("m4a")
<< QStringLiteral("mp4")
<< QStringLiteral("aac")
<< QStringLiteral("asf")
<< QStringLiteral("asx")
<< QStringLiteral("wma")
<< QStringLiteral("aif")
<< QStringLiteral("aiff")
<< QStringLiteral("mka")
<< QStringLiteral("tta")
<< QStringLiteral("dsf")
<< QStringLiteral("dsd")
<< QStringLiteral("ac3")
<< QStringLiteral("dts")
<< QStringLiteral("spc")
<< QStringLiteral("vgm");
struct Song::Private : public QSharedData {
@@ -1781,20 +1810,6 @@ void Song::BindToQuery(SqlQuery *query) const {
}
void Song::BindToFtsQuery(SqlQuery *query) const {
query->BindValue(QStringLiteral(":ftstitle"), d->title_);
query->BindValue(QStringLiteral(":ftsalbum"), d->album_);
query->BindValue(QStringLiteral(":ftsartist"), d->artist_);
query->BindValue(QStringLiteral(":ftsalbumartist"), d->albumartist_);
query->BindValue(QStringLiteral(":ftscomposer"), d->composer_);
query->BindValue(QStringLiteral(":ftsperformer"), d->performer_);
query->BindValue(QStringLiteral(":ftsgrouping"), d->grouping_);
query->BindValue(QStringLiteral(":ftsgenre"), d->genre_);
query->BindValue(QStringLiteral(":ftscomment"), d->comment_);
}
#ifdef HAVE_DBUS
void Song::ToXesam(QVariantMap *map) const {

View File

@@ -119,12 +119,13 @@ class Song {
static const QString kBindSpec;
static const QString kUpdateSpec;
static const QStringList kNumericalColumns;
static const QStringList kFtsColumns;
static const QString kFtsColumnSpec;
static const QString kFtsBindSpec;
static const QString kFtsUpdateSpec;
static const QStringList kTextSearchColumns;
static const QStringList kIntSearchColumns;
static const QStringList kUIntSearchColumns;
static const QStringList kInt64SearchColumns;
static const QStringList kFloatSearchColumns;
static const QStringList kNumericalSearchColumns;
static const QStringList kSearchColumns;
using RegularExpressionList = QList<QRegularExpression>;
static const RegularExpressionList kAlbumDisc;
@@ -439,7 +440,6 @@ class Song {
// Save
void BindToQuery(SqlQuery *query) const;
void BindToFtsQuery(SqlQuery *query) const;
#ifdef HAVE_DBUS
void ToXesam(QVariantMap *map) const;
#endif

View File

@@ -236,7 +236,7 @@ SongLoader::Result SongLoader::LoadLocal(const QString &filename) {
QMutexLocker l(collection_backend_->db()->Mutex());
QSqlDatabase db(collection_backend_->db()->Connect());
CollectionQuery query(db, collection_backend_->songs_table(), collection_backend_->fts_table());
CollectionQuery query(db, collection_backend_->songs_table());
query.SetColumnSpec(QStringLiteral("%songs_table.ROWID, ") + Song::kColumnSpec);
query.AddWhere(QStringLiteral("url"), url.toEncoded());