From 6562258db5809057a5c8d58ef5d7b55b78d36525 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Sat, 20 Aug 2022 14:51:19 +0200 Subject: [PATCH] CollectionModel: Make separating albums by grouping optional Fixes #1018 --- src/collection/collectionfilterwidget.cpp | 139 +++++++---- src/collection/collectionfilterwidget.h | 23 +- src/collection/collectionmodel.cpp | 266 +++++++++++----------- src/collection/collectionmodel.h | 30 ++- src/collection/groupbydialog.cpp | 15 +- src/collection/groupbydialog.h | 4 +- src/collection/groupbydialog.ui | 11 +- src/collection/savedgroupingmanager.cpp | 39 ++-- src/collection/savedgroupingmanager.h | 14 +- src/internet/internetsearchview.cpp | 3 +- 10 files changed, 315 insertions(+), 229 deletions(-) diff --git a/src/collection/collectionfilterwidget.cpp b/src/collection/collectionfilterwidget.cpp index 807ed74e..cc7cb6dd 100644 --- a/src/collection/collectionfilterwidget.cpp +++ b/src/collection/collectionfilterwidget.cpp @@ -2,6 +2,7 @@ * Strawberry Music Player * This file was part of Clementine. * Copyright 2010, David Sansome + * Copyright 2019-2022, Jonas Kvinge * * Strawberry is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,7 +59,12 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent) : QWidget(parent), ui_(new Ui_CollectionFilterWidget), model_(nullptr), - group_by_dialog_(new GroupByDialog), + group_by_dialog_(new GroupByDialog(this)), + groupings_manager_(nullptr), + filter_age_menu_(nullptr), + group_by_menu_(nullptr), + collection_menu_(nullptr), + group_by_group_(nullptr), filter_delay_(new QTimer(this)), filter_applies_to_model_(true), delay_behaviour_(DelayedOnLargeLibraries) { @@ -114,13 +120,8 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent) filter_ages_[ui_->filter_age_three_months] = 60 * 60 * 24 * 30 * 3; filter_ages_[ui_->filter_age_year] = 60 * 60 * 24 * 365; - // "Group by ..." - group_by_group_ = CreateGroupByActions(this); - group_by_menu_ = new QMenu(tr("Group by"), this); - group_by_menu_->addActions(group_by_group_->actions()); - QObject::connect(group_by_group_, &QActionGroup::triggered, this, &CollectionFilterWidget::GroupByClicked); QObject::connect(ui_->save_grouping, &QAction::triggered, this, &CollectionFilterWidget::SaveGroupBy); QObject::connect(ui_->manage_groupings, &QAction::triggered, this, &CollectionFilterWidget::ShowGroupingManager); @@ -147,8 +148,8 @@ void CollectionFilterWidget::Init(CollectionModel *model) { if (model_) { QObject::disconnect(model_, nullptr, this, nullptr); - QObject::disconnect(model_, nullptr, group_by_dialog_.get(), nullptr); - QObject::disconnect(group_by_dialog_.get(), nullptr, model_, nullptr); + QObject::disconnect(model_, nullptr, group_by_dialog_, nullptr); + QObject::disconnect(group_by_dialog_, nullptr, model_, nullptr); QList filter_ages = filter_ages_.keys(); for (QAction *action : filter_ages) { QObject::disconnect(action, &QAction::triggered, model_, nullptr); @@ -158,9 +159,9 @@ void CollectionFilterWidget::Init(CollectionModel *model) { model_ = model; // Connect signals - QObject::connect(model_, &CollectionModel::GroupingChanged, group_by_dialog_.get(), &GroupByDialog::CollectionGroupingChanged); + QObject::connect(model_, &CollectionModel::GroupingChanged, group_by_dialog_, &GroupByDialog::CollectionGroupingChanged); QObject::connect(model_, &CollectionModel::GroupingChanged, this, &CollectionFilterWidget::GroupingChanged); - QObject::connect(group_by_dialog_.get(), &GroupByDialog::Accepted, model_, &CollectionModel::SetGroupBy); + QObject::connect(group_by_dialog_, &GroupByDialog::Accepted, model_, &CollectionModel::SetGroupBy); QList filter_ages = filter_ages_.keys(); for (QAction *action : filter_ages) { @@ -176,15 +177,31 @@ void CollectionFilterWidget::Init(CollectionModel *model) { if (s.contains(group_by_version())) version = s.value(group_by_version(), 0).toInt(); if (version == 1) { model_->SetGroupBy(CollectionModel::Grouping( - CollectionModel::GroupBy(s.value(group_by(1), static_cast(CollectionModel::GroupBy_AlbumArtist)).toInt()), - CollectionModel::GroupBy(s.value(group_by(2), static_cast(CollectionModel::GroupBy_AlbumDisc)).toInt()), - CollectionModel::GroupBy(s.value(group_by(3), static_cast(CollectionModel::GroupBy_None)).toInt()))); + CollectionModel::GroupBy(s.value(group_by_key(1), static_cast(CollectionModel::GroupBy_AlbumArtist)).toInt()), + CollectionModel::GroupBy(s.value(group_by_key(2), static_cast(CollectionModel::GroupBy_AlbumDisc)).toInt()), + CollectionModel::GroupBy(s.value(group_by_key(3), static_cast(CollectionModel::GroupBy_None)).toInt())), s.value(separate_albums_by_grouping_key(), false).toBool()); } else { - model_->SetGroupBy(CollectionModel::Grouping(CollectionModel::GroupBy_AlbumArtist, CollectionModel::GroupBy_AlbumDisc, CollectionModel::GroupBy_None)); + model_->SetGroupBy(CollectionModel::Grouping(CollectionModel::GroupBy_AlbumArtist, CollectionModel::GroupBy_AlbumDisc, CollectionModel::GroupBy_None), false); } s.endGroup(); } + +} + +void CollectionFilterWidget::SetSettingsGroup(const QString &settings_group) { + + settings_group_ = settings_group; + saved_groupings_settings_group_ = SavedGroupingManager::GetSavedGroupingsSettingsGroup(settings_group); + + UpdateGroupByActions(); + +} + +void CollectionFilterWidget::SetSettingsPrefix(const QString &prefix) { + + settings_prefix_ = prefix; + } void CollectionFilterWidget::ReloadSettings() { @@ -198,21 +215,10 @@ void CollectionFilterWidget::ReloadSettings() { } -QString CollectionFilterWidget::group_by() { +QString CollectionFilterWidget::group_by_version() const { if (settings_prefix_.isEmpty()) { - return QString("group_by"); - } - else { - return QString("%1_group_by").arg(settings_prefix_); - } - -} - -QString CollectionFilterWidget::group_by_version() { - - if (settings_prefix_.isEmpty()) { - return QString("group_by_version"); + return "group_by_version"; } else { return QString("%1_group_by_version").arg(settings_prefix_); @@ -220,7 +226,29 @@ QString CollectionFilterWidget::group_by_version() { } -QString CollectionFilterWidget::group_by(const int number) { return group_by() + QString::number(number); } +QString CollectionFilterWidget::group_by_key() const { + + if (settings_prefix_.isEmpty()) { + return "group_by"; + } + else { + return QString("%1_group_by").arg(settings_prefix_); + } + +} + +QString CollectionFilterWidget::group_by_key(const int number) const { return group_by_key() + QString::number(number); } + +QString CollectionFilterWidget::separate_albums_by_grouping_key() const { + + if (settings_prefix_.isEmpty()) { + return "separate_albums_by_grouping"; + } + else { + return QString("%1_separate_albums_by_grouping").arg(settings_prefix_); + } + +} void CollectionFilterWidget::UpdateGroupByActions() { @@ -229,7 +257,7 @@ void CollectionFilterWidget::UpdateGroupByActions() { delete group_by_group_; } - group_by_group_ = CreateGroupByActions(this); + group_by_group_ = CreateGroupByActions(saved_groupings_settings_group_, this); group_by_menu_->clear(); group_by_menu_->addActions(group_by_group_->actions()); QObject::connect(group_by_group_, &QActionGroup::triggered, this, &CollectionFilterWidget::GroupByClicked); @@ -239,8 +267,7 @@ void CollectionFilterWidget::UpdateGroupByActions() { } - -QActionGroup *CollectionFilterWidget::CreateGroupByActions(QObject *parent) { +QActionGroup *CollectionFilterWidget::CreateGroupByActions(const QString &saved_groupings_settings_group, QObject *parent) { QActionGroup *ret = new QActionGroup(parent); @@ -267,9 +294,9 @@ QActionGroup *CollectionFilterWidget::CreateGroupByActions(QObject *parent) { sep1->setSeparator(true); ret->addAction(sep1); - // read saved groupings + // Read saved groupings QSettings s; - s.beginGroup(CollectionModel::kSavedGroupingsSettingsGroup); + s.beginGroup(saved_groupings_settings_group); int version = s.value("version").toInt(); if (version == 1) { QStringList saved = s.childKeys(); @@ -316,20 +343,38 @@ QAction *CollectionFilterWidget::CreateGroupByAction(const QString &text, QObjec void CollectionFilterWidget::SaveGroupBy() { - QString text = QInputDialog::getText(this, tr("Grouping Name"), tr("Grouping name:")); - if (!text.isEmpty() && model_) { - model_->SaveGrouping(text); - UpdateGroupByActions(); + if (!model_) return; + + QString name = QInputDialog::getText(this, tr("Grouping Name"), tr("Grouping name:")); + if (name.isEmpty()) return; + + qLog(Debug) << "Saving current grouping to" << name; + + QSettings s; + if (settings_group_.isEmpty() || settings_group_ == CollectionSettingsPage::kSettingsGroup) { + s.beginGroup(SavedGroupingManager::kSavedGroupingsSettingsGroup); } + else { + s.beginGroup(QString(SavedGroupingManager::kSavedGroupingsSettingsGroup) + "_" + settings_group_); + } + QByteArray buffer; + QDataStream datastream(&buffer, QIODevice::WriteOnly); + datastream << model_->GetGroupBy(); + s.setValue("version", "1"); + s.setValue(name, buffer); + s.endGroup(); + + UpdateGroupByActions(); } void CollectionFilterWidget::ShowGroupingManager() { if (!groupings_manager_) { - groupings_manager_ = std::make_unique(); + groupings_manager_ = new SavedGroupingManager(saved_groupings_settings_group_, this); + QObject::connect(groupings_manager_, &SavedGroupingManager::UpdateGroupByActions, this, &CollectionFilterWidget::UpdateGroupByActions); } - groupings_manager_->SetFilter(this); + groupings_manager_->UpdateModel(); groupings_manager_->show(); @@ -362,20 +407,20 @@ void CollectionFilterWidget::GroupByClicked(QAction *action) { } CollectionModel::Grouping g = action->property("group_by").value(); - model_->SetGroupBy(g); + model_->SetGroupBy(g, false); } -void CollectionFilterWidget::GroupingChanged(const CollectionModel::Grouping g) { +void CollectionFilterWidget::GroupingChanged(const CollectionModel::Grouping g, const bool separate_albums_by_grouping) { if (!settings_group_.isEmpty()) { - // Save the settings QSettings s; s.beginGroup(settings_group_); s.setValue(group_by_version(), 1); - s.setValue(group_by(1), static_cast(g[0])); - s.setValue(group_by(2), static_cast(g[1])); - s.setValue(group_by(3), static_cast(g[2])); + s.setValue(group_by_key(1), static_cast(g[0])); + s.setValue(group_by_key(2), static_cast(g[1])); + s.setValue(group_by_key(3), static_cast(g[2])); + s.setValue(separate_albums_by_grouping_key(), separate_albums_by_grouping); s.endGroup(); } @@ -386,6 +431,10 @@ void CollectionFilterWidget::GroupingChanged(const CollectionModel::Grouping g) void CollectionFilterWidget::CheckCurrentGrouping(const CollectionModel::Grouping g) { + if (!group_by_group_) { + UpdateGroupByActions(); + } + for (QAction *action : group_by_group_->actions()) { if (action->property("group_by").isNull()) continue; diff --git a/src/collection/collectionfilterwidget.h b/src/collection/collectionfilterwidget.h index 4e9167cf..422b20ab 100644 --- a/src/collection/collectionfilterwidget.h +++ b/src/collection/collectionfilterwidget.h @@ -2,6 +2,7 @@ * Strawberry Music Player * This file was part of Clementine. * Copyright 2010, David Sansome + * Copyright 2019-2022, Jonas Kvinge * * Strawberry is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -60,9 +61,8 @@ class CollectionFilterWidget : public QWidget { void Init(CollectionModel *model); - static QActionGroup *CreateGroupByActions(QObject *parent); + static QActionGroup *CreateGroupByActions(const QString &saved_groupings_settings_group, QObject *parent); - void UpdateGroupByActions(); void SetFilterHint(const QString &hint); void SetApplyFilterToCollection(bool filter_applies_to_model) { filter_applies_to_model_ = filter_applies_to_model; } void SetDelayBehaviour(DelayBehaviour behaviour) { delay_behaviour_ = behaviour; } @@ -73,12 +73,13 @@ class CollectionFilterWidget : public QWidget { QMenu *menu() const { return collection_menu_; } void AddMenuAction(QAction *action); - void SetSettingsGroup(const QString &group) { settings_group_ = group; } - void SetSettingsPrefix(const QString &prefix) { settings_prefix_ = prefix; } + void SetSettingsGroup(const QString &group); + void SetSettingsPrefix(const QString &prefix); - QString group_by(); - QString group_by_version(); - QString group_by(const int number); + QString group_by_version() const; + QString group_by_key() const; + QString group_by_key(const int number) const; + QString separate_albums_by_grouping_key() const; void ReloadSettings(); @@ -86,6 +87,7 @@ class CollectionFilterWidget : public QWidget { void FocusSearchField(); public slots: + void UpdateGroupByActions(); void SetQueryMode(QueryOptions::QueryMode query_mode); void FocusOnFilter(QKeyEvent *e); @@ -99,7 +101,7 @@ class CollectionFilterWidget : public QWidget { void keyReleaseEvent(QKeyEvent *e) override; private slots: - void GroupingChanged(const CollectionModel::Grouping g); + void GroupingChanged(const CollectionModel::Grouping g, const bool separate_albums_by_grouping); void GroupByClicked(QAction *action); void SaveGroupBy(); void ShowGroupingManager(); @@ -115,8 +117,8 @@ class CollectionFilterWidget : public QWidget { Ui_CollectionFilterWidget *ui_; CollectionModel *model_; - std::unique_ptr group_by_dialog_; - std::unique_ptr groupings_manager_; + GroupByDialog *group_by_dialog_; + SavedGroupingManager *groupings_manager_; QMenu *filter_age_menu_; QMenu *group_by_menu_; @@ -130,6 +132,7 @@ class CollectionFilterWidget : public QWidget { DelayBehaviour delay_behaviour_; QString settings_group_; + QString saved_groupings_settings_group_; QString settings_prefix_; }; diff --git a/src/collection/collectionmodel.cpp b/src/collection/collectionmodel.cpp index 378727ce..ce3aebcf 100644 --- a/src/collection/collectionmodel.cpp +++ b/src/collection/collectionmodel.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -71,7 +72,6 @@ #include "covermanager/albumcoverloaderresult.h" #include "settings/collectionsettingspage.h" -const char *CollectionModel::kSavedGroupingsSettingsGroup = "SavedGroupings"; const int CollectionModel::kPrettyCoverSize = 32; const char *CollectionModel::kPixmapDiskCacheDir = "pixmapcache"; @@ -86,6 +86,7 @@ CollectionModel::CollectionModel(CollectionBackend *backend, Application *app, Q total_song_count_(0), total_artist_count_(0), total_album_count_(0), + separate_albums_by_grouping_(false), artist_icon_(IconLoader::Load("folder-sound")), album_icon_(IconLoader::Load("cdcase")), init_task_id_(-1), @@ -160,22 +161,6 @@ void CollectionModel::set_show_dividers(const bool show_dividers) { } -void CollectionModel::SaveGrouping(const QString &name) { - - qLog(Debug) << "Model, save to: " << name; - - QByteArray buffer; - QDataStream ds(&buffer, QIODevice::WriteOnly); - ds << group_by_; - - QSettings s; - s.beginGroup(kSavedGroupingsSettingsGroup); - s.setValue("version", "1"); - s.setValue(name, buffer); - s.endGroup(); - -} - void CollectionModel::ReloadSettings() { QSettings s; @@ -239,13 +224,13 @@ void CollectionModel::SongsDiscovered(const SongList &songs) { CollectionItem *container = root_; QString key; for (int i = 0; i < 3; ++i) { - GroupBy type = group_by_[i]; - if (type == GroupBy_None) break; + GroupBy group_by = group_by_[i]; + if (group_by == GroupBy_None) break; if (!key.isEmpty()) key.append("-"); // Special case: if the song is a compilation and the current GroupBy level is Artists, then we want the Various Artists node :( - if (IsArtistGroupBy(type) && song.is_compilation()) { + if (IsArtistGroupBy(group_by) && song.is_compilation()) { if (container->compilation_artist_node_ == nullptr) { CreateCompilationArtistNode(true, container); } @@ -254,7 +239,7 @@ void CollectionModel::SongsDiscovered(const SongList &songs) { } else { // Otherwise find the proper container at this level based on the item's key - key.append(ContainerKey(type, song)); + key.append(ContainerKey(group_by, separate_albums_by_grouping_, song)); // Does it exist already? if (container_nodes_[i].contains(key)) { @@ -262,7 +247,7 @@ void CollectionModel::SongsDiscovered(const SongList &songs) { } else { // Create the container - container = ItemFromSong(type, true, i == 0, container, song, i); + container = ItemFromSong(group_by, separate_albums_by_grouping_, true, i == 0, container, song, i); container_nodes_[i].insert(key, container); } @@ -274,7 +259,7 @@ void CollectionModel::SongsDiscovered(const SongList &songs) { if (!container->lazy_loaded && use_lazy_loading_) continue; // We've gone all the way down to the deepest level and everything was already lazy loaded, so now we have to create the song in the container. - song_nodes_.insert(song.id(), ItemFromSong(GroupBy_None, true, false, container, song, -1)); + song_nodes_.insert(song.id(), ItemFromSong(GroupBy_None, separate_albums_by_grouping_, true, false, container, song, -1)); } } @@ -311,11 +296,11 @@ CollectionItem *CollectionModel::CreateCompilationArtistNode(const bool signal, } -QString CollectionModel::ContainerKey(const GroupBy type, const Song &song) { +QString CollectionModel::ContainerKey(const GroupBy group_by, const bool separate_albums_by_grouping, const Song &song) { QString key; - switch (type) { + switch (group_by) { case GroupBy_AlbumArtist: key = TextOrUnknown(song.effective_albumartist()); break; @@ -325,32 +310,32 @@ QString CollectionModel::ContainerKey(const GroupBy type, const Song &song) { case GroupBy_Album: key = TextOrUnknown(song.album()); if (!song.album_id().isEmpty()) key.append("-" + song.album_id()); - if (!song.grouping().isEmpty()) key.append("-" + song.grouping()); + if (separate_albums_by_grouping && !song.grouping().isEmpty()) key.append("-" + song.grouping()); break; case GroupBy_AlbumDisc: key = PrettyAlbumDisc(song.album(), song.disc()); if (!song.album_id().isEmpty()) key.append("-" + song.album_id()); - if (!song.grouping().isEmpty()) key.append("-" + song.grouping()); + if (separate_albums_by_grouping && !song.grouping().isEmpty()) key.append("-" + song.grouping()); break; case GroupBy_YearAlbum: key = PrettyYearAlbum(song.year(), song.album()); if (!song.album_id().isEmpty()) key.append("-" + song.album_id()); - if (!song.grouping().isEmpty()) key.append("-" + song.grouping()); + if (separate_albums_by_grouping && !song.grouping().isEmpty()) key.append("-" + song.grouping()); break; case GroupBy_YearAlbumDisc: key = PrettyYearAlbumDisc(song.year(), song.album(), song.disc()); if (!song.album_id().isEmpty()) key.append("-" + song.album_id()); - if (!song.grouping().isEmpty()) key.append("-" + song.grouping()); + if (separate_albums_by_grouping && !song.grouping().isEmpty()) key.append("-" + song.grouping()); break; case GroupBy_OriginalYearAlbum: key = PrettyYearAlbum(song.effective_originalyear(), song.album()); if (!song.album_id().isEmpty()) key.append("-" + song.album_id()); - if (!song.grouping().isEmpty()) key.append("-" + song.grouping()); + if (separate_albums_by_grouping && !song.grouping().isEmpty()) key.append("-" + song.grouping()); break; case GroupBy_OriginalYearAlbumDisc: key = PrettyYearAlbumDisc(song.effective_originalyear(), song.album(), song.disc()); if (!song.album_id().isEmpty()) key.append("-" + song.album_id()); - if (!song.grouping().isEmpty()) key.append("-" + song.grouping()); + if (separate_albums_by_grouping && !song.grouping().isEmpty()) key.append("-" + song.grouping()); break; case GroupBy_Disc: key = PrettyDisc(song.disc()); @@ -408,13 +393,13 @@ QString CollectionModel::ContainerKey(const GroupBy type, const Song &song) { } -QString CollectionModel::DividerKey(const GroupBy type, CollectionItem *item) { +QString CollectionModel::DividerKey(const GroupBy group_by, CollectionItem *item) { // Items which are to be grouped under the same divider must produce the same divider key. This will only get called for top-level items. if (item->sort_text.isEmpty()) return QString(); - switch (type) { + switch (group_by) { case GroupBy_AlbumArtist: case GroupBy_Artist: case GroupBy_Album: @@ -461,16 +446,16 @@ QString CollectionModel::DividerKey(const GroupBy type, CollectionItem *item) { case GroupByCount: return QString(); } - qLog(Error) << "Unknown GroupBy type" << type << "for item" << item->display_text; + qLog(Error) << "Unknown GroupBy" << group_by << "for item" << item->display_text; return QString(); } -QString CollectionModel::DividerDisplayText(const GroupBy type, const QString &key) { +QString CollectionModel::DividerDisplayText(const GroupBy group_by, const QString &key) { // Pretty display text for the dividers. - switch (type) { + switch (group_by) { case GroupBy_AlbumArtist: case GroupBy_Artist: case GroupBy_Album: @@ -513,7 +498,7 @@ QString CollectionModel::DividerDisplayText(const GroupBy type, const QString &k case GroupByCount: break; } - qLog(Error) << "Unknown GroupBy type" << type << "for divider key" << key; + qLog(Error) << "Unknown GroupBy" << group_by << "for divider key" << key; return QString(); } @@ -726,8 +711,8 @@ QVariant CollectionModel::data(const QModelIndex &idx, const int role) const { if (use_pretty_covers_) { bool is_album_node = false; if (role == Qt::DecorationRole && item->type == CollectionItem::Type_Container) { - GroupBy container_type = group_by_[item->container_level]; - is_album_node = IsAlbumGroupBy(container_type); + GroupBy container_group_by = group_by_[item->container_level]; + is_album_node = IsAlbumGroupBy(container_group_by); } if (is_album_node) { // It has const behaviour some of the time - that's ok right? @@ -741,7 +726,7 @@ QVariant CollectionModel::data(const QModelIndex &idx, const int role) const { QVariant CollectionModel::data(const CollectionItem *item, const int role) const { - GroupBy container_type = item->type == CollectionItem::Type_Container ? group_by_[item->container_level] : GroupBy_None; + GroupBy container_group_by = item->type == CollectionItem::Type_Container ? group_by_[item->container_level] : GroupBy_None; switch (role) { case Qt::DisplayRole: @@ -751,7 +736,7 @@ QVariant CollectionModel::data(const CollectionItem *item, const int role) const case Qt::DecorationRole: switch (item->type) { case CollectionItem::Type_Container: - switch (container_type) { + switch (container_group_by) { case GroupBy_Album: case GroupBy_AlbumDisc: case GroupBy_YearAlbum: @@ -778,7 +763,7 @@ QVariant CollectionModel::data(const CollectionItem *item, const int role) const return item->type == CollectionItem::Type_Divider; case Role_ContainerType: - return container_type; + return container_group_by; case Role_Key: return item->key; @@ -849,26 +834,26 @@ CollectionModel::QueryResult CollectionModel::RunQuery(CollectionItem *parent) { // Information about what we want the children to be int child_level = parent == root_ ? 0 : parent->container_level + 1; - GroupBy child_type = child_level >= 3 ? GroupBy_None : group_by_[child_level]; + GroupBy child_group_by = child_level >= 3 ? GroupBy_None : group_by_[child_level]; - // Initialize the query. child_type says what type of thing we want (artists, songs, etc.) + // Initialize the query. child_group_by says what type of thing we want (artists, songs, etc.) { QMutexLocker l(backend_->db()->Mutex()); QSqlDatabase db(backend_->db()->Connect()); CollectionQuery q(db, backend_->songs_table(), backend_->fts_table(), query_options_); - InitQuery(child_type, &q); + InitQuery(child_group_by, separate_albums_by_grouping_, &q); // Walk up through the item's parents adding filters as necessary CollectionItem *p = parent; while (p && p->type == CollectionItem::Type_Container) { - FilterQuery(group_by_[p->container_level], p, &q); + FilterQuery(group_by_[p->container_level], separate_albums_by_grouping_, p, &q); p = p->parent; } // Artists GroupBy is special - we don't want compilation albums appearing - if (IsArtistGroupBy(child_type)) { + if (IsArtistGroupBy(child_group_by)) { // Add the special Various artists node if (show_various_artists_ && HasCompilations(db, q)) { result.create_va = true; @@ -902,7 +887,7 @@ void CollectionModel::PostQuery(CollectionItem *parent, const CollectionModel::Q // Information about what we want the children to be int child_level = parent == root_ ? 0 : parent->container_level + 1; - GroupBy child_type = child_level >= 3 ? GroupBy_None : group_by_[child_level]; + GroupBy child_group_by = child_level >= 3 ? GroupBy_None : group_by_[child_level]; if (result.create_va && parent->compilation_artist_node_ == nullptr) { CreateCompilationArtistNode(signal, parent); @@ -911,10 +896,10 @@ void CollectionModel::PostQuery(CollectionItem *parent, const CollectionModel::Q // Step through the results for (const SqlRow &row : result.rows) { // Create the item - it will get inserted into the model here - CollectionItem *item = ItemFromQuery(child_type, signal, child_level == 0, parent, row, child_level); + CollectionItem *item = ItemFromQuery(child_group_by, separate_albums_by_grouping_, signal, child_level == 0, parent, row, child_level); // Save a pointer to it for later - if (child_type == GroupBy_None) { + if (child_group_by == GroupBy_None) { song_nodes_.insert(item->metadata.id(), item); } else { @@ -1002,34 +987,52 @@ void CollectionModel::Reset() { } -void CollectionModel::InitQuery(const GroupBy type, CollectionQuery *q) { +void CollectionModel::InitQuery(const GroupBy group_by, const bool separate_albums_by_grouping, CollectionQuery *q) { - // Say what type of thing we want to get back from the database. - switch (type) { + // Say what group_by of thing we want to get back from the database. + switch (group_by) { case GroupBy_AlbumArtist: q->SetColumnSpec("DISTINCT effective_albumartist"); break; case GroupBy_Artist: q->SetColumnSpec("DISTINCT artist"); break; - case GroupBy_Album: - q->SetColumnSpec("DISTINCT album, album_id, grouping"); + case GroupBy_Album:{ + QString query("DISTINCT album, album_id"); + if (separate_albums_by_grouping) query.append(", grouping"); + q->SetColumnSpec(query); break; - case GroupBy_AlbumDisc: - q->SetColumnSpec("DISTINCT album, album_id, disc, grouping"); + } + case GroupBy_AlbumDisc:{ + QString query("DISTINCT album, album_id, disc"); + if (separate_albums_by_grouping) query.append(", grouping"); + q->SetColumnSpec(query); break; - case GroupBy_YearAlbum: - q->SetColumnSpec("DISTINCT year, album, album_id, grouping"); + } + case GroupBy_YearAlbum:{ + QString query("DISTINCT year, album, album_id"); + if (separate_albums_by_grouping) query.append(", grouping"); + q->SetColumnSpec(query); break; - case GroupBy_YearAlbumDisc: - q->SetColumnSpec("DISTINCT year, album, album_id, disc, grouping"); + } + case GroupBy_YearAlbumDisc:{ + QString query("DISTINCT year, album, album_id, disc"); + if (separate_albums_by_grouping) query.append(", grouping"); + q->SetColumnSpec(query); break; - case GroupBy_OriginalYearAlbum: - q->SetColumnSpec("DISTINCT year, originalyear, album, album_id, grouping"); + } + case GroupBy_OriginalYearAlbum:{ + QString query("DISTINCT year, originalyear, album, album_id"); + if (separate_albums_by_grouping) query.append(", grouping"); + q->SetColumnSpec(query); break; - case GroupBy_OriginalYearAlbumDisc: - q->SetColumnSpec("DISTINCT year, originalyear, album, album_id, disc, grouping"); + } + case GroupBy_OriginalYearAlbumDisc:{ + QString query("DISTINCT year, originalyear, album, album_id, disc"); + if (separate_albums_by_grouping) query.append(", grouping"); + q->SetColumnSpec(query); break; + } case GroupBy_Disc: q->SetColumnSpec("DISTINCT disc"); break; @@ -1074,11 +1077,11 @@ void CollectionModel::InitQuery(const GroupBy type, CollectionQuery *q) { } -void CollectionModel::FilterQuery(const GroupBy type, CollectionItem *item, CollectionQuery *q) { +void CollectionModel::FilterQuery(const GroupBy group_by, const bool separate_albums_by_grouping, CollectionItem *item, CollectionQuery *q) { // Say how we want the query to be filtered. This is done once for each parent going up the tree. - switch (type) { + switch (group_by) { case GroupBy_AlbumArtist: if (IsCompilationArtistNode(item)) { q->AddCompilationRequirement(true); @@ -1102,33 +1105,33 @@ void CollectionModel::FilterQuery(const GroupBy type, CollectionItem *item, Coll case GroupBy_Album: q->AddWhere("album", item->metadata.album()); q->AddWhere("album_id", item->metadata.album_id()); - q->AddWhere("grouping", item->metadata.grouping()); + if (separate_albums_by_grouping) q->AddWhere("grouping", item->metadata.grouping()); break; case GroupBy_AlbumDisc: q->AddWhere("album", item->metadata.album()); q->AddWhere("album_id", item->metadata.album_id()); q->AddWhere("disc", item->metadata.disc()); - q->AddWhere("grouping", item->metadata.grouping()); + if (separate_albums_by_grouping) q->AddWhere("grouping", item->metadata.grouping()); break; case GroupBy_YearAlbum: q->AddWhere("year", item->metadata.year()); q->AddWhere("album", item->metadata.album()); q->AddWhere("album_id", item->metadata.album_id()); - q->AddWhere("grouping", item->metadata.grouping()); + if (separate_albums_by_grouping) q->AddWhere("grouping", item->metadata.grouping()); break; case GroupBy_YearAlbumDisc: q->AddWhere("year", item->metadata.year()); q->AddWhere("album", item->metadata.album()); q->AddWhere("album_id", item->metadata.album_id()); q->AddWhere("disc", item->metadata.disc()); - q->AddWhere("grouping", item->metadata.grouping()); + if (separate_albums_by_grouping) q->AddWhere("grouping", item->metadata.grouping()); break; case GroupBy_OriginalYearAlbum: q->AddWhere("year", item->metadata.year()); q->AddWhere("originalyear", item->metadata.originalyear()); q->AddWhere("album", item->metadata.album()); q->AddWhere("album_id", item->metadata.album_id()); - q->AddWhere("grouping", item->metadata.grouping()); + if (separate_albums_by_grouping) q->AddWhere("grouping", item->metadata.grouping()); break; case GroupBy_OriginalYearAlbumDisc: q->AddWhere("year", item->metadata.year()); @@ -1136,7 +1139,7 @@ void CollectionModel::FilterQuery(const GroupBy type, CollectionItem *item, Coll q->AddWhere("album", item->metadata.album()); q->AddWhere("album_id", item->metadata.album_id()); q->AddWhere("disc", item->metadata.disc()); - q->AddWhere("grouping", item->metadata.grouping()); + if (separate_albums_by_grouping) q->AddWhere("grouping", item->metadata.grouping()); break; case GroupBy_Disc: q->AddWhere("disc", item->metadata.disc()); @@ -1178,15 +1181,15 @@ void CollectionModel::FilterQuery(const GroupBy type, CollectionItem *item, Coll break; case GroupBy_None: case GroupByCount: - qLog(Error) << "Unknown GroupBy type" << type << "used in filter"; + qLog(Error) << "Unknown GroupBy" << group_by << "used in filter"; break; } } -CollectionItem *CollectionModel::InitItem(const GroupBy type, const bool signal, CollectionItem *parent, const int container_level) { +CollectionItem *CollectionModel::InitItem(const GroupBy group_by, const bool signal, CollectionItem *parent, const int container_level) { - CollectionItem::Type item_type = type == GroupBy_None ? CollectionItem::Type_Song : CollectionItem::Type_Container; + CollectionItem::Type item_type = group_by == GroupBy_None ? CollectionItem::Type_Song : CollectionItem::Type_Container; if (signal) beginInsertRows(ItemToIndex(parent), static_cast(parent->children.count()), static_cast(parent->children.count())); @@ -1199,25 +1202,25 @@ CollectionItem *CollectionModel::InitItem(const GroupBy type, const bool signal, } -CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool signal, const bool create_divider, CollectionItem *parent, const SqlRow &row, const int container_level) { +CollectionItem *CollectionModel::ItemFromQuery(const GroupBy group_by, const bool separate_albums_by_grouping, const bool signal, const bool create_divider, CollectionItem *parent, const SqlRow &row, const int container_level) { - CollectionItem *item = InitItem(type, signal, parent, container_level); + CollectionItem *item = InitItem(group_by, signal, parent, container_level); if (parent != root_ && !parent->key.isEmpty()) { item->key = parent->key + "-"; } - switch (type) { + switch (group_by) { case GroupBy_AlbumArtist:{ item->metadata.set_albumartist(row.value(0).toString()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); item->display_text = TextOrUnknown(item->metadata.albumartist()); item->sort_text = SortTextForArtist(item->metadata.albumartist()); break; } case GroupBy_Artist:{ item->metadata.set_artist(row.value(0).toString()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); item->display_text = TextOrUnknown(item->metadata.artist()); item->sort_text = SortTextForArtist(item->metadata.artist()); break; @@ -1226,7 +1229,7 @@ CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool si item->metadata.set_album(row.value(0).toString()); item->metadata.set_album_id(row.value(1).toString()); item->metadata.set_grouping(row.value(2).toString()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); item->display_text = TextOrUnknown(item->metadata.album()); item->sort_text = SortTextForArtist(item->metadata.album()); break; @@ -1236,7 +1239,7 @@ CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool si item->metadata.set_album_id(row.value(1).toString()); item->metadata.set_disc(row.value(2).toInt()); item->metadata.set_grouping(row.value(3).toString()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); item->display_text = PrettyAlbumDisc(item->metadata.album(), item->metadata.disc()); item->sort_text = item->metadata.album() + SortTextForNumber(qMax(0, item->metadata.disc())); break; @@ -1246,7 +1249,7 @@ CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool si item->metadata.set_album(row.value(1).toString()); item->metadata.set_album_id(row.value(2).toString()); item->metadata.set_grouping(row.value(3).toString()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); item->display_text = PrettyYearAlbum(item->metadata.year(), item->metadata.album()); item->sort_text = SortTextForNumber(qMax(0, item->metadata.year())) + item->metadata.grouping() + item->metadata.album(); break; @@ -1257,7 +1260,7 @@ CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool si item->metadata.set_album_id(row.value(2).toString()); item->metadata.set_disc(row.value(3).toInt()); item->metadata.set_grouping(row.value(4).toString()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); item->display_text = PrettyYearAlbumDisc(item->metadata.year(), item->metadata.album(), item->metadata.disc()); item->sort_text = SortTextForNumber(qMax(0, item->metadata.year())) + item->metadata.album() + SortTextForNumber(qMax(0, item->metadata.disc())); break; @@ -1268,7 +1271,7 @@ CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool si item->metadata.set_album(row.value(2).toString()); item->metadata.set_album_id(row.value(3).toString()); item->metadata.set_grouping(row.value(4).toString()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); item->display_text = PrettyYearAlbum(item->metadata.effective_originalyear(), item->metadata.album()); item->sort_text = SortTextForNumber(qMax(0, item->metadata.effective_originalyear())) + item->metadata.grouping() + item->metadata.album(); break; @@ -1280,14 +1283,14 @@ CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool si item->metadata.set_album_id(row.value(3).toString()); item->metadata.set_disc(row.value(4).toInt()); item->metadata.set_grouping(row.value(5).toString()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); item->display_text = PrettyYearAlbumDisc(item->metadata.effective_originalyear(), item->metadata.album(), item->metadata.disc()); item->sort_text = SortTextForNumber(qMax(0, item->metadata.effective_originalyear())) + item->metadata.album() + SortTextForNumber(qMax(0, item->metadata.disc())); break; } case GroupBy_Disc:{ item->metadata.set_disc(row.value(0).toInt()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); const int disc = qMax(0, row.value(0).toInt()); item->display_text = PrettyDisc(disc); item->sort_text = SortTextForNumber(disc); @@ -1295,7 +1298,7 @@ CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool si } case GroupBy_Year:{ item->metadata.set_year(row.value(0).toInt()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); const int year = qMax(0, item->metadata.year()); item->display_text = QString::number(year); item->sort_text = SortTextForNumber(year) + " "; @@ -1303,7 +1306,7 @@ CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool si } case GroupBy_OriginalYear:{ item->metadata.set_originalyear(row.value(0).toInt()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); const int year = qMax(0, item->metadata.originalyear()); item->display_text = QString::number(year); item->sort_text = SortTextForNumber(year) + " "; @@ -1311,35 +1314,35 @@ CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool si } case GroupBy_Genre:{ item->metadata.set_genre(row.value(0).toString()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); item->display_text = TextOrUnknown(item->metadata.genre()); item->sort_text = SortTextForArtist(item->metadata.genre()); break; } case GroupBy_Composer:{ item->metadata.set_composer(row.value(0).toString()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); item->display_text = TextOrUnknown(item->metadata.composer()); item->sort_text = SortTextForArtist(item->metadata.composer()); break; } case GroupBy_Performer:{ item->metadata.set_performer(row.value(0).toString()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); item->display_text = TextOrUnknown(item->metadata.performer()); item->sort_text = SortTextForArtist(item->metadata.performer()); break; } case GroupBy_Grouping:{ item->metadata.set_grouping(row.value(0).toString()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); item->display_text = TextOrUnknown(item->metadata.grouping()); item->sort_text = SortTextForArtist(item->metadata.grouping()); break; } case GroupBy_FileType:{ item->metadata.set_filetype(static_cast(row.value(0).toInt())); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); item->display_text = item->metadata.TextForFiletype(); item->sort_text = item->metadata.TextForFiletype(); break; @@ -1348,7 +1351,7 @@ CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool si item->metadata.set_filetype(static_cast(row.value(0).toInt())); item->metadata.set_samplerate(row.value(1).toInt()); item->metadata.set_bitdepth(row.value(2).toInt()); - QString key = ContainerKey(type, item->metadata); + QString key = ContainerKey(group_by, separate_albums_by_grouping, item->metadata); item->key.append(key); item->display_text = key; item->sort_text = key; @@ -1356,7 +1359,7 @@ CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool si } case GroupBy_Samplerate:{ item->metadata.set_samplerate(row.value(0).toInt()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); const int samplerate = qMax(0, item->metadata.samplerate()); item->display_text = QString::number(samplerate); item->sort_text = SortTextForNumber(samplerate) + " "; @@ -1364,7 +1367,7 @@ CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool si } case GroupBy_Bitdepth:{ item->metadata.set_bitdepth(row.value(0).toInt()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); const int bitdepth = qMax(0, item->metadata.bitdepth()); item->display_text = QString::number(bitdepth); item->sort_text = SortTextForNumber(bitdepth) + " "; @@ -1372,7 +1375,7 @@ CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool si } case GroupBy_Bitrate:{ item->metadata.set_bitrate(row.value(0).toInt()); - item->key.append(ContainerKey(type, item->metadata)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, item->metadata)); const int bitrate = qMax(0, item->metadata.bitrate()); item->display_text = QString::number(bitrate); item->sort_text = SortTextForNumber(bitrate) + " "; @@ -1392,31 +1395,31 @@ CollectionItem *CollectionModel::ItemFromQuery(const GroupBy type, const bool si break; } - FinishItem(type, signal, create_divider, parent, item); + FinishItem(group_by, signal, create_divider, parent, item); return item; } -CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool signal, const bool create_divider, CollectionItem *parent, const Song &s, const int container_level) { +CollectionItem *CollectionModel::ItemFromSong(const GroupBy group_by, const bool separate_albums_by_grouping, const bool signal, const bool create_divider, CollectionItem *parent, const Song &s, const int container_level) { - CollectionItem *item = InitItem(type, signal, parent, container_level); + CollectionItem *item = InitItem(group_by, signal, parent, container_level); if (parent != root_ && !parent->key.isEmpty()) { item->key = parent->key + "-"; } - switch (type) { + switch (group_by) { case GroupBy_AlbumArtist:{ item->metadata.set_albumartist(s.effective_albumartist()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); item->display_text = TextOrUnknown(s.effective_albumartist()); item->sort_text = SortTextForArtist(s.effective_albumartist()); break; } case GroupBy_Artist:{ item->metadata.set_artist(s.artist()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); item->display_text = TextOrUnknown(s.artist()); item->sort_text = SortTextForArtist(s.artist()); break; @@ -1425,7 +1428,7 @@ CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool sig item->metadata.set_album(s.album()); item->metadata.set_album_id(s.album_id()); item->metadata.set_grouping(s.grouping()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); item->display_text = TextOrUnknown(s.album()); item->sort_text = SortTextForArtist(s.album()); break; @@ -1435,7 +1438,7 @@ CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool sig item->metadata.set_album_id(s.album_id()); item->metadata.set_disc(s.disc()); item->metadata.set_grouping(s.grouping()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); item->display_text = PrettyAlbumDisc(s.album(), s.disc()); item->sort_text = s.album() + SortTextForNumber(qMax(0, s.disc())); break; @@ -1445,7 +1448,7 @@ CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool sig item->metadata.set_album(s.album()); item->metadata.set_album_id(s.album_id()); item->metadata.set_grouping(s.grouping()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); item->display_text = PrettyYearAlbum(s.year(), s.album()); item->sort_text = SortTextForNumber(qMax(0, s.year())) + s.grouping() + s.album(); break; @@ -1456,7 +1459,7 @@ CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool sig item->metadata.set_album_id(s.album_id()); item->metadata.set_disc(s.disc()); item->metadata.set_grouping(s.grouping()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); item->display_text = PrettyYearAlbumDisc(s.year(), s.album(), s.disc()); item->sort_text = SortTextForNumber(qMax(0, s.year())) + s.album() + SortTextForNumber(qMax(0, s.disc())); break; @@ -1467,7 +1470,7 @@ CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool sig item->metadata.set_album(s.album()); item->metadata.set_album_id(s.album_id()); item->metadata.set_grouping(s.grouping()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); item->display_text = PrettyYearAlbum(s.effective_originalyear(), s.album()); item->sort_text = SortTextForNumber(qMax(0, s.effective_originalyear())) + s.grouping() + s.album(); break; @@ -1479,14 +1482,14 @@ CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool sig item->metadata.set_album_id(s.album_id()); item->metadata.set_disc(s.disc()); item->metadata.set_grouping(s.grouping()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); item->display_text = PrettyYearAlbumDisc(s.effective_originalyear(), s.album(), s.disc()); item->sort_text = SortTextForNumber(qMax(0, s.effective_originalyear())) + s.album() + SortTextForNumber(qMax(0, s.disc())); break; } case GroupBy_Disc:{ item->metadata.set_disc(s.disc()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); const int disc = qMax(0, s.disc()); item->display_text = PrettyDisc(disc); item->sort_text = SortTextForNumber(disc); @@ -1494,7 +1497,7 @@ CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool sig } case GroupBy_Year:{ item->metadata.set_year(s.year()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); const int year = qMax(0, s.year()); item->display_text = QString::number(year); item->sort_text = SortTextForNumber(year) + " "; @@ -1502,7 +1505,7 @@ CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool sig } case GroupBy_OriginalYear:{ item->metadata.set_originalyear(s.effective_originalyear()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); const int year = qMax(0, s.effective_originalyear()); item->display_text = QString::number(year); item->sort_text = SortTextForNumber(year) + " "; @@ -1510,35 +1513,35 @@ CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool sig } case GroupBy_Genre:{ item->metadata.set_genre(s.genre()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); item->display_text = TextOrUnknown(s.genre()); item->sort_text = SortTextForArtist(s.genre()); break; } case GroupBy_Composer:{ item->metadata.set_composer(s.composer()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); item->display_text = TextOrUnknown(s.composer()); item->sort_text = SortTextForArtist(s.composer()); break; } case GroupBy_Performer:{ item->metadata.set_performer(s.performer()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); item->display_text = TextOrUnknown(s.performer()); item->sort_text = SortTextForArtist(s.performer()); break; } case GroupBy_Grouping:{ item->metadata.set_grouping(s.grouping()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); item->display_text = TextOrUnknown(s.grouping()); item->sort_text = SortTextForArtist(s.grouping()); break; } case GroupBy_FileType:{ item->metadata.set_filetype(s.filetype()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); item->display_text = s.TextForFiletype(); item->sort_text = s.TextForFiletype(); break; @@ -1547,7 +1550,7 @@ CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool sig item->metadata.set_filetype(s.filetype()); item->metadata.set_samplerate(s.samplerate()); item->metadata.set_bitdepth(s.bitdepth()); - QString key = ContainerKey(type, s); + QString key = ContainerKey(group_by, separate_albums_by_grouping, s); item->key.append(key); item->display_text = key; item->sort_text = key; @@ -1555,7 +1558,7 @@ CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool sig } case GroupBy_Samplerate:{ item->metadata.set_samplerate(s.samplerate()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); const int samplerate = qMax(0, s.samplerate()); item->display_text = QString::number(samplerate); item->sort_text = SortTextForNumber(samplerate) + " "; @@ -1563,7 +1566,7 @@ CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool sig } case GroupBy_Bitdepth:{ item->metadata.set_bitdepth(s.bitdepth()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); const int bitdepth = qMax(0, s.bitdepth()); item->display_text = QString::number(bitdepth); item->sort_text = SortTextForNumber(bitdepth) + " "; @@ -1571,7 +1574,7 @@ CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool sig } case GroupBy_Bitrate:{ item->metadata.set_bitrate(s.bitrate()); - item->key.append(ContainerKey(type, s)); + item->key.append(ContainerKey(group_by, separate_albums_by_grouping, s)); const int bitrate = qMax(0, s.bitrate()); item->display_text = QString::number(bitrate); item->sort_text = SortTextForNumber(bitrate) + " "; @@ -1592,16 +1595,16 @@ CollectionItem *CollectionModel::ItemFromSong(const GroupBy type, const bool sig } } - FinishItem(type, signal, create_divider, parent, item); + FinishItem(group_by, signal, create_divider, parent, item); if (s.url().scheme() == "cdda") item->lazy_loaded = true; return item; } -void CollectionModel::FinishItem(const GroupBy type, const bool signal, const bool create_divider, CollectionItem *parent, CollectionItem *item) { +void CollectionModel::FinishItem(const GroupBy group_by, const bool signal, const bool create_divider, CollectionItem *parent, CollectionItem *item) { - if (type == GroupBy_None) item->lazy_loaded = true; + if (group_by == GroupBy_None) item->lazy_loaded = true; if (signal) { endInsertRows(); @@ -1609,7 +1612,7 @@ void CollectionModel::FinishItem(const GroupBy type, const bool signal, const bo // Create the divider entry if we're supposed to if (create_divider && show_dividers_) { - QString divider_key = DividerKey(type, item); + QString divider_key = DividerKey(group_by, item); if (!divider_key.isEmpty()) { item->sort_text.prepend(divider_key + " "); } @@ -1621,7 +1624,7 @@ void CollectionModel::FinishItem(const GroupBy type, const bool signal, const bo CollectionItem *divider = new CollectionItem(CollectionItem::Type_Divider, root_); divider->key = divider_key; - divider->display_text = DividerDisplayText(type, divider_key); + divider->display_text = DividerDisplayText(group_by, divider_key); divider->sort_text = divider_key + " "; divider->lazy_loaded = true; @@ -1875,12 +1878,15 @@ bool CollectionModel::canFetchMore(const QModelIndex &parent) const { } -void CollectionModel::SetGroupBy(const Grouping g) { +void CollectionModel::SetGroupBy(const Grouping g, const std::optional separate_albums_by_grouping) { group_by_ = g; + if (separate_albums_by_grouping) { + separate_albums_by_grouping_ = separate_albums_by_grouping.value(); + } ResetAsync(); - emit GroupingChanged(g); + emit GroupingChanged(g, separate_albums_by_grouping_); } diff --git a/src/collection/collectionmodel.h b/src/collection/collectionmodel.h index 22acaa14..3220a82f 100644 --- a/src/collection/collectionmodel.h +++ b/src/collection/collectionmodel.h @@ -24,6 +24,8 @@ #include "config.h" +#include + #include #include #include @@ -64,8 +66,6 @@ class CollectionModel : public SimpleTreeModel { explicit CollectionModel(CollectionBackend *backend, Application *app, QObject *parent = nullptr); ~CollectionModel() override; - static const char *kSavedGroupingsSettingsGroup; - static const int kPrettyCoverSize; static const char *kPixmapDiskCacheDir; @@ -160,9 +160,6 @@ class CollectionModel : public SimpleTreeModel { // Whether or not to show letters heading in the collection view void set_show_dividers(const bool show_dividers); - // Save the current grouping - void SaveGrouping(const QString &name); - // Reload settings. void ReloadSettings(); @@ -195,15 +192,15 @@ class CollectionModel : public SimpleTreeModel { void ExpandAll(CollectionItem *item = nullptr) const; const CollectionModel::Grouping GetGroupBy() const { return group_by_; } - void SetGroupBy(const CollectionModel::Grouping g); + void SetGroupBy(const CollectionModel::Grouping g, const std::optional separate_albums_by_grouping); - static QString ContainerKey(const GroupBy type, const Song &song); + static QString ContainerKey(const GroupBy group_by, const bool separate_albums_by_grouping, const Song &song); signals: void TotalSongCountUpdated(int count); void TotalArtistCountUpdated(int count); void TotalAlbumCountUpdated(int count); - void GroupingChanged(CollectionModel::Grouping g); + void GroupingChanged(CollectionModel::Grouping g, bool separate_albums_by_grouping); public slots: void SetFilterAge(const int age); @@ -247,22 +244,22 @@ class CollectionModel : public SimpleTreeModel { // Functions for working with queries and creating items. // When the model is reset or when a node is lazy-loaded the Collection constructs a database query to populate the items. // Filters are added for each parent item, restricting the songs returned to a particular album or artist for example. - static void InitQuery(const GroupBy type, CollectionQuery *q); - static void FilterQuery(const GroupBy type, CollectionItem *item, CollectionQuery *q); + static void InitQuery(const GroupBy group_by, const bool separate_albums_by_grouping, CollectionQuery *q); + static void FilterQuery(const GroupBy group_by, const bool separate_albums_by_grouping, CollectionItem *item, CollectionQuery *q); // Items can be created either from a query that's been run to populate a node, or by a spontaneous SongsDiscovered emission from the backend. - CollectionItem *ItemFromQuery(const GroupBy type, const bool signal, const bool create_divider, CollectionItem *parent, const SqlRow &row, const int container_level); - CollectionItem *ItemFromSong(const GroupBy type, const bool signal, const bool create_divider, CollectionItem *parent, const Song &s, const int container_level); + CollectionItem *ItemFromQuery(const GroupBy group_by, const bool separate_albums_by_grouping, const bool signal, const bool create_divider, CollectionItem *parent, const SqlRow &row, const int container_level); + CollectionItem *ItemFromSong(const GroupBy group_by, const bool separate_albums_by_grouping, const bool signal, const bool create_divider, CollectionItem *parent, const Song &s, const int container_level); // The "Various Artists" node is an annoying special case. CollectionItem *CreateCompilationArtistNode(const bool signal, CollectionItem *parent); // Helpers for ItemFromQuery and ItemFromSong - CollectionItem *InitItem(const GroupBy type, const bool signal, CollectionItem *parent, const int container_level); - void FinishItem(const GroupBy type, const bool signal, const bool create_divider, CollectionItem *parent, CollectionItem *item); + CollectionItem *InitItem(const GroupBy group_by, const bool signal, CollectionItem *parent, const int container_level); + void FinishItem(const GroupBy group_by, const bool signal, const bool create_divider, CollectionItem *parent, CollectionItem *item); - static QString DividerKey(const GroupBy type, CollectionItem *item); - static QString DividerDisplayText(const GroupBy type, const QString &key); + static QString DividerKey(const GroupBy group_by, CollectionItem *item); + static QString DividerDisplayText(const GroupBy group_by, const QString &key); // Helpers static bool IsCompilationArtistNode(const CollectionItem *node) { return node == node->parent->compilation_artist_node_; } @@ -284,6 +281,7 @@ class CollectionModel : public SimpleTreeModel { QueryOptions query_options_; Grouping group_by_; + bool separate_albums_by_grouping_; // Keyed on database ID QMap song_nodes_; diff --git a/src/collection/groupbydialog.cpp b/src/collection/groupbydialog.cpp index 38c3ae9a..e538e5e9 100644 --- a/src/collection/groupbydialog.cpp +++ b/src/collection/groupbydialog.cpp @@ -108,22 +108,31 @@ GroupByDialog::GroupByDialog(QWidget *parent) : QDialog(parent), ui_(new Ui_Grou GroupByDialog::~GroupByDialog() = default; void GroupByDialog::Reset() { + ui_->combobox_first->setCurrentIndex(2); // Album Artist - ui_->combobox_second->setCurrentIndex(3); // Album + ui_->combobox_second->setCurrentIndex(4); // Album Disc ui_->combobox_third->setCurrentIndex(0); // None + ui_->checkbox_separate_albums_by_grouping->setChecked(false); + } void GroupByDialog::accept() { + emit Accepted(CollectionModel::Grouping( p_->mapping_.get().find(ui_->combobox_first->currentIndex())->group_by, p_->mapping_.get().find(ui_->combobox_second->currentIndex())->group_by, - p_->mapping_.get().find(ui_->combobox_third->currentIndex())->group_by) + p_->mapping_.get().find(ui_->combobox_third->currentIndex())->group_by), + ui_->checkbox_separate_albums_by_grouping->isChecked() ); QDialog::accept(); + } -void GroupByDialog::CollectionGroupingChanged(const CollectionModel::Grouping g) { +void GroupByDialog::CollectionGroupingChanged(const CollectionModel::Grouping g, const bool separate_albums_by_grouping) { + ui_->combobox_first->setCurrentIndex(p_->mapping_.get().find(g[0])->combo_box_index); ui_->combobox_second->setCurrentIndex(p_->mapping_.get().find(g[1])->combo_box_index); ui_->combobox_third->setCurrentIndex(p_->mapping_.get().find(g[2])->combo_box_index); + ui_->checkbox_separate_albums_by_grouping->setChecked(separate_albums_by_grouping); + } diff --git a/src/collection/groupbydialog.h b/src/collection/groupbydialog.h index 3a0d7428..983c2e7b 100644 --- a/src/collection/groupbydialog.h +++ b/src/collection/groupbydialog.h @@ -45,11 +45,11 @@ class GroupByDialog : public QDialog { ~GroupByDialog() override; public slots: - void CollectionGroupingChanged(const CollectionModel::Grouping g); + void CollectionGroupingChanged(const CollectionModel::Grouping g, const bool separate_albums_by_grouping); void accept() override; signals: - void Accepted(CollectionModel::Grouping g); + void Accepted(CollectionModel::Grouping g, bool separate_albums_by_grouping); private slots: void Reset(); diff --git a/src/collection/groupbydialog.ui b/src/collection/groupbydialog.ui index aae23dfa..e64cff1b 100644 --- a/src/collection/groupbydialog.ui +++ b/src/collection/groupbydialog.ui @@ -6,8 +6,8 @@ 0 0 - 354 - 246 + 394 + 273 @@ -370,6 +370,13 @@ + + + + Separate albums by grouping tag + + + diff --git a/src/collection/savedgroupingmanager.cpp b/src/collection/savedgroupingmanager.cpp index 081c926d..a17f76a7 100644 --- a/src/collection/savedgroupingmanager.cpp +++ b/src/collection/savedgroupingmanager.cpp @@ -2,7 +2,7 @@ * Strawberry Music Player * This file was part of Clementine. * Copyright 2015, Nick Lanham - * Copyright 2019-2021, Jonas Kvinge + * Copyright 2019-2022, Jonas Kvinge * * Strawberry is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,35 +22,31 @@ #include "config.h" #include -#include #include #include -#include -#include -#include -#include #include -#include +#include #include #include +#include +#include #include #include -#include #include -#include #include "core/logging.h" #include "core/iconloader.h" -#include "collectionfilterwidget.h" #include "collectionmodel.h" #include "savedgroupingmanager.h" #include "ui_savedgroupingmanager.h" -SavedGroupingManager::SavedGroupingManager(QWidget *parent) +const char *SavedGroupingManager::kSavedGroupingsSettingsGroup = "SavedGroupings"; + +SavedGroupingManager::SavedGroupingManager(const QString &saved_groupings_settings_group, QWidget *parent) : QDialog(parent), ui_(new Ui_SavedGroupingManager), model_(new QStandardItemModel(0, 4, this)), - filter_(nullptr) { + saved_groupings_settings_group_(saved_groupings_settings_group) { ui_->setupUi(this); @@ -71,7 +67,17 @@ SavedGroupingManager::SavedGroupingManager(QWidget *parent) SavedGroupingManager::~SavedGroupingManager() { delete ui_; - delete model_; +} + +QString SavedGroupingManager::GetSavedGroupingsSettingsGroup(const QString &settings_group) { + + if (settings_group.isEmpty() || settings_group == CollectionSettingsPage::kSettingsGroup) { + return kSavedGroupingsSettingsGroup; + } + else { + return QString(kSavedGroupingsSettingsGroup) + "_" + settings_group; + } + } QString SavedGroupingManager::GroupByToString(const CollectionModel::GroupBy g) { @@ -151,7 +157,7 @@ void SavedGroupingManager::UpdateModel() { model_->setRowCount(0); // don't use clear, it deletes headers QSettings s; - s.beginGroup(CollectionModel::kSavedGroupingsSettingsGroup); + s.beginGroup(saved_groupings_settings_group_); int version = s.value("version").toInt(); if (version == 1) { QStringList saved = s.childKeys(); @@ -186,7 +192,7 @@ void SavedGroupingManager::Remove() { if (ui_->list->selectionModel()->hasSelection()) { QSettings s; - s.beginGroup(CollectionModel::kSavedGroupingsSettingsGroup); + s.beginGroup(saved_groupings_settings_group_); for (const QModelIndex &idx : ui_->list->selectionModel()->selectedRows()) { if (idx.isValid()) { qLog(Debug) << "Remove saved grouping: " << model_->item(idx.row(), 0)->text(); @@ -196,7 +202,8 @@ void SavedGroupingManager::Remove() { s.endGroup(); } UpdateModel(); - filter_->UpdateGroupByActions(); + + emit UpdateGroupByActions(); } diff --git a/src/collection/savedgroupingmanager.h b/src/collection/savedgroupingmanager.h index 3da213f8..4a3397a5 100644 --- a/src/collection/savedgroupingmanager.h +++ b/src/collection/savedgroupingmanager.h @@ -2,7 +2,7 @@ * Strawberry Music Player * This file was part of Clementine. * Copyright 2015, Nick Lanham - * Copyright 2019-2021, Jonas Kvinge + * Copyright 2019-2022, Jonas Kvinge * * Strawberry is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,14 +40,20 @@ class SavedGroupingManager : public QDialog { Q_OBJECT public: - explicit SavedGroupingManager(QWidget *parent = nullptr); + explicit SavedGroupingManager(const QString &saved_groupings_settings_group, QWidget *parent = nullptr); ~SavedGroupingManager() override; + static const char *kSavedGroupingsSettingsGroup; + + static QString GetSavedGroupingsSettingsGroup(const QString &settings_group); + void UpdateModel(); - void SetFilter(CollectionFilterWidget *filter) { filter_ = filter; } static QString GroupByToString(const CollectionModel::GroupBy g); + signals: + void UpdateGroupByActions(); + private slots: void UpdateButtonState(); void Remove(); @@ -55,7 +61,7 @@ class SavedGroupingManager : public QDialog { private: Ui_SavedGroupingManager *ui_; QStandardItemModel *model_; - CollectionFilterWidget *filter_; + QString saved_groupings_settings_group_; }; #endif // SAVEDGROUPINGMANAGER_H diff --git a/src/internet/internetsearchview.cpp b/src/internet/internetsearchview.cpp index 208789c4..3ed2fe2c 100644 --- a/src/internet/internetsearchview.cpp +++ b/src/internet/internetsearchview.cpp @@ -76,6 +76,7 @@ #include "collection/collectionfilterwidget.h" #include "collection/collectionmodel.h" #include "collection/groupbydialog.h" +#include "collection/savedgroupingmanager.h" #include "covermanager/albumcoverloader.h" #include "covermanager/albumcoverloaderoptions.h" #include "covermanager/albumcoverloaderresult.h" @@ -179,7 +180,7 @@ void InternetSearchView::Init(Application *app, InternetService *service) { back_proxy_->sort(0); // Add actions to the settings menu - group_by_actions_ = CollectionFilterWidget::CreateGroupByActions(this); + group_by_actions_ = CollectionFilterWidget::CreateGroupByActions(SavedGroupingManager::GetSavedGroupingsSettingsGroup(service_->settings_group()), this); QMenu *settings_menu = new QMenu(this); settings_menu->addActions(group_by_actions_->actions()); settings_menu->addSeparator();