[gnome-music/wip/jfelder/improve-song-album-sorting: 4/5] Use normalized strings to sort album, artists and songs names
- From: Jean Felder <jfelder src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/jfelder/improve-song-album-sorting: 4/5] Use normalized strings to sort album, artists and songs names
- Date: Mon, 6 Jan 2020 11:58:11 +0000 (UTC)
commit 4efbfcbd93575219e3607a0c5a3f4cf619121ec0
Author: Jean Felder <jfelder src gnome org>
Date: Tue Dec 17 19:49:07 2019 +0100
Use normalized strings to sort album, artists and songs names
If an album, artist or song name contains an accent, it won't be
sorted correctly in somes cases.
For example, if an album is named "Éléor", it will be visible at the
end of the AlbumsView while it would make more sense to insert it
before an album starting with the letter "f".
The main issue is that direct string comparisons do not handle well
string which contains accents. Instead a caseless comparison needs to
be done: the strings need to be normalized before the comparison.
This issue is fixed by introducing a new sort function named
sort_names which normalizes the string. This sort function is set for
all the relevant models.
gnomemusic/coremodel.py | 12 +++++-------
gnomemusic/songliststore.py | 20 +++++++++++---------
gnomemusic/utils.py | 22 ++++++++++++++++++++++
3 files changed, 38 insertions(+), 16 deletions(-)
---
diff --git a/gnomemusic/coremodel.py b/gnomemusic/coremodel.py
index 13a8bc0e..c795c2bc 100644
--- a/gnomemusic/coremodel.py
+++ b/gnomemusic/coremodel.py
@@ -36,6 +36,7 @@ from gnomemusic.grilowrappers.grltrackerplaylists import Playlist
from gnomemusic.player import PlayerPlaylist
from gnomemusic.songliststore import SongListStore
from gnomemusic.widgets.songwidget import SongWidget
+import gnomemusic.utils as utils
class CoreModel(GObject.GObject):
@@ -159,20 +160,17 @@ class CoreModel(GObject.GObject):
return coresong.props.selected
def _albums_sort(self, album_a, album_b):
- return album_b.props.title.casefold() < album_a.props.title.casefold()
+ return utils.sort_names(album_a.props.title, album_b.props.title)
def _artist_sort(self, artist_a, artist_b):
- name_a = artist_a.props.artist.casefold()
- name_b = artist_b.props.artist.casefold()
- return name_a > name_b
+ return utils.sort_names(artist_a.props.artist, artist_b.props.artist)
def _playlists_sort(self, playlist_a, playlist_b):
if playlist_a.props.is_smart:
if not playlist_b.props.is_smart:
return -1
- title_a = playlist_a.props.title.casefold()
- title_b = playlist_b.props.title.casefold()
- return title_a > title_b
+ return utils.sort_names(
+ playlist_a.props.title, playlist_b.props.title)
if playlist_b.props.is_smart:
return 1
diff --git a/gnomemusic/songliststore.py b/gnomemusic/songliststore.py
index 8ced678e..4a8e13b0 100644
--- a/gnomemusic/songliststore.py
+++ b/gnomemusic/songliststore.py
@@ -66,19 +66,21 @@ class SongListStore(Gtk.ListStore):
return wrap
def _songs_sort(self, song_a, song_b):
- title_a = song_a.props.title.casefold()
- title_b = song_b.props.title.casefold()
- song_cmp = title_a == title_b
+ title_a = song_a.props.title
+ title_b = song_b.props.title
+ song_cmp = (utils.normalize_caseless(title_a)
+ == utils.normalize_caseless(title_b))
if not song_cmp:
- return title_a > title_b
+ return utils.sort_names(title_a, title_b)
- artist_a = song_a.props.artist.casefold()
- artist_b = song_b.props.artist.casefold()
- artist_cmp = artist_a == artist_b
+ artist_a = song_a.props.artist
+ artist_b = song_b.props.artist
+ artist_cmp = (utils.normalize_caseless(artist_a)
+ == utils.normalize_caseless(artist_b))
if not artist_cmp:
- return artist_a > artist_b
+ return utils.sort_names(artist_a, artist_b)
- return song_a.props.album.casefold() > song_b.props.album.casefold()
+ return utils.sort_names(song_a.props.album, song_b.props.album)
def _on_items_changed(self, model, position, removed, added):
if removed > 0:
diff --git a/gnomemusic/utils.py b/gnomemusic/utils.py
index 76310cb6..405a41e4 100644
--- a/gnomemusic/utils.py
+++ b/gnomemusic/utils.py
@@ -23,6 +23,7 @@
# delete this exception statement from your version.
from enum import Enum, IntEnum
+import unicodedata
from gettext import gettext as _
from gi.repository import Gio
@@ -134,3 +135,24 @@ def seconds_to_string(duration):
seconds %= 60
return '{:d}:{:02d}'.format(minutes, seconds)
+
+
+def normalize_caseless(text):
+ """Get a normalized casefolded version of a string.
+
+ :param str text: string to normalize
+ :returns: normalized casefolded string
+ :rtype: str
+ """
+ return unicodedata.normalize("NFKD", text.casefold())
+
+
+def sort_names(name_a, name_b):
+ """Caseless comparison of two strings.
+
+ :param str name_a: first string to compare
+ :param str name_b: second string to compare
+ :returns: False if name_a should be before name_b. True otherwise.
+ :rtype: boolean
+ """
+ return normalize_caseless(name_b) < normalize_caseless(name_a)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]