Add sort columns to playlists

Increment playlist state version from 1 to 2 to get sort columns next to
their "original" column. Discard old stored playlist state in config file.
This commit is contained in:
Mark
2025-07-26 12:44:20 +02:00
committed by Jonas Kvinge
parent e7fc4b1706
commit 0bfc2ee198
5 changed files with 111 additions and 19 deletions

View File

@@ -219,11 +219,17 @@ bool Playlist::column_is_editable(const Playlist::Column column) {
switch (column) {
case Column::Title:
case Column::TitleSort:
case Column::Artist:
case Column::ArtistSort:
case Column::Album:
case Column::AlbumSort:
case Column::AlbumArtist:
case Column::AlbumArtistSort:
case Column::Composer:
case Column::ComposerSort:
case Column::Performer:
case Column::PerformerSort:
case Column::Grouping:
case Column::Track:
case Column::Disc:
@@ -247,21 +253,39 @@ bool Playlist::set_column_value(Song &song, const Playlist::Column column, const
case Column::Title:
song.set_title(value.toString());
break;
case Column::TitleSort:
song.set_titlesort(value.toString());
break;
case Column::Artist:
song.set_artist(value.toString());
break;
case Column::ArtistSort:
song.set_artistsort(value.toString());
break;
case Column::Album:
song.set_album(value.toString());
break;
case Column::AlbumSort:
song.set_albumsort(value.toString());
break;
case Column::AlbumArtist:
song.set_albumartist(value.toString());
break;
case Column::AlbumArtistSort:
song.set_albumartistsort(value.toString());
break;
case Column::Composer:
song.set_composer(value.toString());
break;
case Column::ComposerSort:
song.set_composersort(value.toString());
break;
case Column::Performer:
song.set_performer(value.toString());
break;
case Column::PerformerSort:
song.set_performersort(value.toString());
break;
case Column::Grouping:
song.set_grouping(value.toString());
break;
@@ -319,8 +343,11 @@ QVariant Playlist::data(const QModelIndex &idx, const int role) const {
// Don't forget to change Playlist::CompareItems when adding new columns
switch (static_cast<Column>(idx.column())) {
case Column::Title: return song.PrettyTitle();
case Column::TitleSort: return song.titlesort();
case Column::Artist: return song.artist();
case Column::ArtistSort: return song.artistsort();
case Column::Album: return song.album();
case Column::AlbumSort: return song.albumsort();
case Column::Length: return song.length_nanosec();
case Column::Track: return song.track();
case Column::Disc: return song.disc();
@@ -328,8 +355,11 @@ QVariant Playlist::data(const QModelIndex &idx, const int role) const {
case Column::OriginalYear: return song.effective_originalyear();
case Column::Genre: return song.genre();
case Column::AlbumArtist: return song.playlist_albumartist();
case Column::AlbumArtistSort: return song.albumartistsort();
case Column::Composer: return song.composer();
case Column::ComposerSort: return song.composersort();
case Column::Performer: return song.performer();
case Column::PerformerSort: return song.performersort();
case Column::Grouping: return song.grouping();
case Column::PlayCount: return song.playcount();
@@ -1326,8 +1356,11 @@ bool Playlist::CompareItems(const Column column, const Qt::SortOrder order, Play
switch (column) {
case Column::Title: strcmp(title_sortable);
case Column::TitleSort: strcmp(titlesort);
case Column::Artist: strcmp(artist_sortable);
case Column::ArtistSort: strcmp(artistsort);
case Column::Album: strcmp(album_sortable);
case Column::AlbumSort: strcmp(albumsort);
case Column::Length: cmp(length_nanosec);
case Column::Track: cmp(track);
case Column::Disc: cmp(disc);
@@ -1335,8 +1368,11 @@ bool Playlist::CompareItems(const Column column, const Qt::SortOrder order, Play
case Column::OriginalYear: cmp(effective_originalyear);
case Column::Genre: strcmp(genre);
case Column::AlbumArtist: strcmp(playlist_albumartist_sortable);
case Column::AlbumArtistSort: strcmp(albumartistsort);
case Column::Composer: strcmp(composer);
case Column::ComposerSort: strcmp(composersort);
case Column::Performer: strcmp(performer);
case Column::PerformerSort: strcmp(performersort);
case Column::Grouping: strcmp(grouping);
case Column::PlayCount: cmp(playcount);
@@ -1380,8 +1416,11 @@ QString Playlist::column_name(const Column column) {
switch (column) {
case Column::Title: return tr("Title");
case Column::TitleSort: return tr("Title Sort");
case Column::Artist: return tr("Artist");
case Column::ArtistSort: return tr("Artist Sort");
case Column::Album: return tr("Album");
case Column::AlbumSort: return tr("Album Sort");
case Column::Track: return tr("Track");
case Column::Disc: return tr("Disc");
case Column::Length: return tr("Length");
@@ -1389,8 +1428,11 @@ QString Playlist::column_name(const Column column) {
case Column::OriginalYear: return tr("Original Year");
case Column::Genre: return tr("Genre");
case Column::AlbumArtist: return tr("Album Artist");
case Column::AlbumArtistSort: return tr("Album Artist Sort");
case Column::Composer: return tr("Composer");
case Column::ComposerSort: return tr("Composer Sort");
case Column::Performer: return tr("Performer");
case Column::PerformerSort: return tr("Performer Sort");
case Column::Grouping: return tr("Grouping");
case Column::PlayCount: return tr("Play Count");
@@ -2109,21 +2151,39 @@ Playlist::Columns Playlist::ChangedColumns(const Song &metadata1, const Song &me
if (metadata1.title() != metadata2.title()) {
columns << Column::Title;
}
if (metadata1.titlesort() != metadata2.titlesort()) {
columns << Column::TitleSort;
}
if (metadata1.artist() != metadata2.artist()) {
columns << Column::Artist;
}
if (metadata1.artistsort() != metadata2.artistsort()) {
columns << Column::ArtistSort;
}
if (metadata1.album() != metadata2.album()) {
columns << Column::Album;
}
if (metadata1.albumsort() != metadata2.albumsort()) {
columns << Column::AlbumSort;
}
if (metadata1.effective_albumartist() != metadata2.effective_albumartist()) {
columns << Column::AlbumArtist;
}
if (metadata1.albumartistsort() != metadata2.albumartistsort()) {
columns << Column::AlbumArtistSort;
}
if (metadata1.performer() != metadata2.performer()) {
columns << Column::Performer;
}
if (metadata1.performersort() != metadata2.performersort()) {
columns << Column::PerformerSort;
}
if (metadata1.composer() != metadata2.composer()) {
columns << Column::Composer;
}
if (metadata1.composersort() != metadata2.composersort()) {
columns << Column::ComposerSort;
}
if (metadata1.year() != metadata2.year()) {
columns << Column::Year;
}

View File

@@ -101,11 +101,17 @@ class Playlist : public QAbstractListModel {
// Always add new columns to the end of this enum - the values are persisted
enum class Column {
Title = 0,
TitleSort,
Artist,
ArtistSort,
Album,
AlbumSort,
AlbumArtist,
AlbumArtistSort,
Performer,
PerformerSort,
Composer,
ComposerSort,
Year,
OriginalYear,
Track,

View File

@@ -387,13 +387,19 @@ TagCompletionModel::TagCompletionModel(SharedPtr<CollectionBackend> backend, con
QString TagCompletionModel::database_column(const Playlist::Column column) {
switch (column) {
case Playlist::Column::Artist: return u"artist"_s;
case Playlist::Column::Album: return u"album"_s;
case Playlist::Column::AlbumArtist: return u"albumartist"_s;
case Playlist::Column::Composer: return u"composer"_s;
case Playlist::Column::Performer: return u"performer"_s;
case Playlist::Column::Grouping: return u"grouping"_s;
case Playlist::Column::Genre: return u"genre"_s;
case Playlist::Column::Artist: return u"artist"_s;
case Playlist::Column::ArtistSort: return u"artistsort"_s;
case Playlist::Column::Album: return u"album"_s;
case Playlist::Column::AlbumSort: return u"albumsort"_s;
case Playlist::Column::AlbumArtist: return u"albumartist"_s;
case Playlist::Column::AlbumArtistSort: return u"albumartistsort"_s;
case Playlist::Column::Composer: return u"composer"_s;
case Playlist::Column::ComposerSort: return u"composersort"_s;
case Playlist::Column::Performer: return u"performer"_s;
case Playlist::Column::PerformerSort: return u"performersort"_s;
case Playlist::Column::Grouping: return u"grouping"_s;
case Playlist::Column::Genre: return u"genre"_s;
case Playlist::Column::TitleSort: return u"titlesort"_s;
default:
qLog(Warning) << "Unknown column" << static_cast<int>(column);
return QString();

View File

@@ -87,6 +87,7 @@ constexpr int kGlowIntensitySteps = 24;
constexpr int kAutoscrollGraceTimeout = 30; // seconds
constexpr int kDropIndicatorWidth = 2;
constexpr int kDropIndicatorGradientWidth = 5;
constexpr int kHeaderStateVersion = 2;
} // namespace
PlaylistView::PlaylistView(QWidget *parent)
@@ -131,7 +132,6 @@ PlaylistView::PlaylistView(QWidget *parent)
cached_current_row_row_(-1),
drop_indicator_row_(-1),
drag_over_(false),
header_state_version_(1),
column_alignment_(DefaultColumnAlignment()),
rating_locked_(false),
dynamic_controls_(new DynamicPlaylistControls(this)),
@@ -217,12 +217,18 @@ void PlaylistView::SetItemDelegates() {
setItemDelegate(new PlaylistDelegateBase(this));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::Title), new TextItemDelegate(this));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::TitleSort), new TagCompletionItemDelegate(this, collection_backend_, Playlist::Column::TitleSort));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::Album), new TagCompletionItemDelegate(this, collection_backend_, Playlist::Column::Album));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::AlbumSort), new TagCompletionItemDelegate(this, collection_backend_, Playlist::Column::AlbumSort));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::Artist), new TagCompletionItemDelegate(this, collection_backend_, Playlist::Column::Artist));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::ArtistSort), new TagCompletionItemDelegate(this, collection_backend_, Playlist::Column::ArtistSort));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::AlbumArtist), new TagCompletionItemDelegate(this, collection_backend_, Playlist::Column::AlbumArtist));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::AlbumArtistSort), new TagCompletionItemDelegate(this, collection_backend_, Playlist::Column::AlbumArtistSort));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::Genre), new TagCompletionItemDelegate(this, collection_backend_, Playlist::Column::Genre));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::Composer), new TagCompletionItemDelegate(this, collection_backend_, Playlist::Column::Composer));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::ComposerSort), new TagCompletionItemDelegate(this, collection_backend_, Playlist::Column::ComposerSort));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::Performer), new TagCompletionItemDelegate(this, collection_backend_, Playlist::Column::Performer));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::PerformerSort), new TagCompletionItemDelegate(this, collection_backend_, Playlist::Column::PerformerSort));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::Grouping), new TagCompletionItemDelegate(this, collection_backend_, Playlist::Column::Grouping));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::Length), new LengthItemDelegate(this));
setItemDelegateForColumn(static_cast<int>(Playlist::Column::Filesize), new SizeItemDelegate(this));
@@ -304,11 +310,26 @@ void PlaylistView::LoadHeaderState() {
Settings s;
s.beginGroup(PlaylistSettings::kSettingsGroup);
// Since we use serialized internal data structures, we cannot read anything but the current version
const int header_state_version = s.value(PlaylistSettings::kStateVersion, 0).toInt();
if (s.contains(PlaylistSettings::kState)) {
header_state_version_ = s.value(PlaylistSettings::kStateVersion, 0).toInt();
header_state_ = s.value(PlaylistSettings::kState).toByteArray();
if (header_state_version == kHeaderStateVersion) {
header_state_ = s.value(PlaylistSettings::kState).toByteArray();
}
else {
// Force header state reset since column indices may have changed between versions
header_state_.clear();
}
}
if (s.contains(PlaylistSettings::kColumnAlignments)) {
if (header_state_version == kHeaderStateVersion) {
column_alignment_ = s.value(PlaylistSettings::kColumnAlignments).value<ColumnAlignmentMap>();
}
else {
// Force column alignment reset since column indices may have changed between versions
column_alignment_.clear();
}
}
if (s.contains(PlaylistSettings::kColumnAlignments)) column_alignment_ = s.value(PlaylistSettings::kColumnAlignments).value<ColumnAlignmentMap>();
s.endGroup();
if (column_alignment_.isEmpty()) {
@@ -329,7 +350,6 @@ void PlaylistView::SetHeaderState() {
void PlaylistView::ResetHeaderState() {
set_initial_header_layout_ = true;
header_state_version_ = 1;
header_state_ = header_->ResetState();
RestoreHeaderState();
@@ -347,9 +367,15 @@ void PlaylistView::RestoreHeaderState() {
header_->SetStretchEnabled(true);
header_->HideSection(static_cast<int>(Playlist::Column::TitleSort));
header_->HideSection(static_cast<int>(Playlist::Column::ArtistSort));
header_->HideSection(static_cast<int>(Playlist::Column::AlbumSort));
header_->HideSection(static_cast<int>(Playlist::Column::AlbumArtist));
header_->HideSection(static_cast<int>(Playlist::Column::AlbumArtistSort));
header_->HideSection(static_cast<int>(Playlist::Column::Performer));
header_->HideSection(static_cast<int>(Playlist::Column::PerformerSort));
header_->HideSection(static_cast<int>(Playlist::Column::Composer));
header_->HideSection(static_cast<int>(Playlist::Column::ComposerSort));
header_->HideSection(static_cast<int>(Playlist::Column::Year));
header_->HideSection(static_cast<int>(Playlist::Column::OriginalYear));
header_->HideSection(static_cast<int>(Playlist::Column::Disc));
@@ -400,11 +426,6 @@ void PlaylistView::RestoreHeaderState() {
}
if (header_state_version_ < 1) {
header_->HideSection(static_cast<int>(Playlist::Column::Rating));
header_state_version_ = 1;
}
// Make sure at least one column is visible
bool all_hidden = true;
for (int i = 0; i < header_->count(); ++i) {
@@ -1305,7 +1326,7 @@ void PlaylistView::SaveSettings() {
Settings s;
s.beginGroup(PlaylistSettings::kSettingsGroup);
s.setValue(PlaylistSettings::kStateVersion, header_state_version_);
s.setValue(PlaylistSettings::kStateVersion, kHeaderStateVersion);
s.setValue(PlaylistSettings::kState, header_->SaveState());
s.setValue(PlaylistSettings::kColumnAlignments, QVariant::fromValue<ColumnAlignmentMap>(column_alignment_));
s.setValue(PlaylistSettings::kRatingLocked, rating_locked_);

View File

@@ -284,7 +284,6 @@ class PlaylistView : public QTreeView {
int drop_indicator_row_;
bool drag_over_;
int header_state_version_;
QByteArray header_state_;
ColumnAlignmentMap column_alignment_;
bool rating_locked_;