diff --git a/gui/ocarina.c b/gui/ocarina.c index c85dbe62..86200889 100644 --- a/gui/ocarina.c +++ b/gui/ocarina.c @@ -125,6 +125,7 @@ static void __ocarina_shutdown(GApplication *application, gpointer data) gui_idle_disable(); core_deinit(); + gui_treeview_deinit(); gui_filter_deinit(); gui_model_deinit(); gui_window_deinit(); diff --git a/gui/treeview.c b/gui/treeview.c index e28b408d..f16dd5d6 100644 --- a/gui/treeview.c +++ b/gui/treeview.c @@ -2,10 +2,169 @@ * Copyright 2016 (c) Anna Schumaker. */ #include +#include #include +#include + +static const enum compare_t GUI_COL_MAP[GUI_MODEL_N_COLUMNS] = { + [GUI_MODEL_TRACK_NR] = COMPARE_TRACK, + [GUI_MODEL_TITLE] = COMPARE_TITLE, + [GUI_MODEL_LENGTH] = COMPARE_LENGTH, + [GUI_MODEL_ARTIST] = COMPARE_ARTIST, + [GUI_MODEL_ALBUM] = COMPARE_ALBUM, + [GUI_MODEL_YEAR] = COMPARE_YEAR, + [GUI_MODEL_GENRE] = COMPARE_GENRE, + [GUI_MODEL_COUNT] = COMPARE_COUNT, + [GUI_MODEL_LAST_PLAY] = COMPARE_PLAYED, +}; + +static unsigned int sort_count = 0; +static gchar *sort_text = NULL; + +static int __gui_treeview_colum_match_sort(enum compare_t compare) +{ + struct playlist *playlist = gui_model_get_playlist(); + GSList *cur = playlist ? playlist->pl_queue.q_sort : NULL; + + while (cur) { + int field = GPOINTER_TO_INT(cur->data); + if (abs(field) == compare) + return field; + cur = g_slist_next(cur); + } + + return 0; +} + +static void __gui_treeview_set_sort_indicators() +{ + GtkTreeViewColumn *col; + unsigned int i, order; + int field; + + for (i = 0; i < GUI_MODEL_N_COLUMNS; i++) { + col = gtk_tree_view_get_column(gui_treeview(), i); + if (!col) + continue; + + field = __gui_treeview_colum_match_sort(GUI_COL_MAP[i]); + order = (field > 0) ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING; + + gtk_tree_view_column_set_sort_indicator(col, field != 0); + gtk_tree_view_column_set_sort_order(col, order); + } +} + +static void __gui_treeview_clear_sorting() +{ + sort_count = 0; + + if (sort_text) { + g_free(sort_text); + sort_text = NULL; + gtk_label_set_text(gui_sorting(), ""); + } +} + +static void __gui_treeview_set_sorting(gchar *text) +{ + gchar *formatted; + + __gui_treeview_clear_sorting(); + if (!text) + return; + + sort_text = g_strdup(text); + formatted = g_strdup_printf("Sorting: {%s}", text); + gtk_label_set_text(gui_sorting(), formatted); + g_free(formatted); +} + +static int __gui_treeview_dec_sort(gpointer data) +{ + if (sort_count > 0) + sort_count--; + if (sort_count == 0) + __gui_treeview_clear_sorting(); + return FALSE; +} + +static gchar *__gui_treeview_sort_text_append(GtkTreeViewColumn *col) +{ + const gchar *title = gtk_tree_view_column_get_title(col); + gchar *text, **split; + unsigned int i; + + if (!sort_text) + return g_strdup(title); + if (gtk_tree_view_column_get_sort_order(col) == GTK_SORT_ASCENDING) + return g_strdup_printf("%s, %s", sort_text, title); + + /* Find the column and prefix it with a minus sign */ + split = g_strsplit(sort_text, ", ", 0); + for (i = 0; split[i] != NULL; i++) { + if (g_strcmp0(split[i], title) == 0) + break; + } + + g_free(split[i]); + split[i] = g_strdup_printf("-%s", title); + text = g_strjoinv(", ", split); + g_strfreev(split); + return text; +} + +static void __gui_treeview_column_clicked(GtkTreeViewColumn *col, + gpointer data) +{ + struct playlist *playlist = gui_model_get_playlist(); + enum compare_t compare = GPOINTER_TO_UINT(data); + bool reset = (sort_count == 0); + gchar *text; + + if (!playlist) + return; + + playlist_sort(playlist->pl_type, playlist->pl_name, compare, reset); + if (!playlist->pl_queue.q_sort) + return; + __gui_treeview_set_sort_indicators(); + + text = __gui_treeview_sort_text_append(col); + __gui_treeview_set_sorting(text); + g_free(text); + + sort_count++; + g_timeout_add_seconds(3, __gui_treeview_dec_sort, NULL); +} void gui_treeview_init() { + GtkTreeViewColumn *col; + unsigned int i; + gtk_tree_view_set_model(gui_treeview(), GTK_TREE_MODEL(gui_filter_get())); + + for (i = 0; i < GUI_MODEL_N_COLUMNS; i++) { + col = gtk_tree_view_get_column(gui_treeview(), i); + if (col) { + g_signal_connect(col, "clicked", + G_CALLBACK(__gui_treeview_column_clicked), + GUINT_TO_POINTER(GUI_COL_MAP[i])); + } + } +} + +void gui_treeview_deinit() +{ + __gui_treeview_clear_sorting(); +} + +void gui_treeview_set_playlist(struct playlist *playlist) +{ + gui_filter_set_playlist(playlist); + + __gui_treeview_clear_sorting(); + __gui_treeview_set_sort_indicators(); } diff --git a/gui/view.c b/gui/view.c index 16a85924..b1e3b765 100644 --- a/gui/view.c +++ b/gui/view.c @@ -24,20 +24,7 @@ static const gchar *QUEUE_SETTINGS[GUI_MODEL_N_COLUMNS] = { [GUI_MODEL_COUNT] = "gui.queue.count", }; -static const enum compare_t QUEUE_SORT[GUI_MODEL_N_COLUMNS] = { - [GUI_MODEL_TRACK_NR] = COMPARE_TRACK, - [GUI_MODEL_TITLE] = COMPARE_TITLE, - [GUI_MODEL_LENGTH] = COMPARE_LENGTH, - [GUI_MODEL_ARTIST] = COMPARE_ARTIST, - [GUI_MODEL_ALBUM] = COMPARE_ALBUM, - [GUI_MODEL_YEAR] = COMPARE_YEAR, - [GUI_MODEL_GENRE] = COMPARE_GENRE, - [GUI_MODEL_COUNT] = COMPARE_COUNT, - [GUI_MODEL_LAST_PLAY] = COMPARE_PLAYED, -}; - static GtkTreeView *view_treeview = NULL; -static unsigned int view_sort_count = 0; static bool view_no_scroll = false; static unsigned int __view_get_column_index(GtkTreeViewColumn *col) @@ -55,58 +42,6 @@ static unsigned int __view_get_column_index(GtkTreeViewColumn *col) return GUI_MODEL_N_COLUMNS; } -static inline void __view_display_sorting(gchar *text) -{ - gtk_label_set_text(GTK_LABEL(gui_builder_widget("o_sorting")), text); -} - -static int __view_dec_sort(gpointer data) -{ - if (view_sort_count > 0) - view_sort_count--; - if (view_sort_count == 0) - __view_display_sorting(""); - return FALSE; -} - -static void __view_set_column_sort_indicator(GtkTreeViewColumn *col, - unsigned int index) -{ - struct playlist *playlist = gui_model_get_playlist(); - GSList *cur = playlist ? playlist->pl_queue.q_sort : NULL; - unsigned int order = GTK_SORT_ASCENDING; - bool show = false; - int field; - - while (cur) { - order = GTK_SORT_ASCENDING; - field = GPOINTER_TO_INT(cur->data); - - if (abs(field) == QUEUE_SORT[index]) { - show = true; - if (field < 0) - order = GTK_SORT_DESCENDING; - break; - } - - cur = g_slist_next(cur); - } - - gtk_tree_view_column_set_sort_indicator(col, show); - gtk_tree_view_column_set_sort_order(col, order); -} - -static void __view_set_sort_indicators() -{ - GtkTreeViewColumn *col; - - for (unsigned int i = 0; i < GUI_MODEL_N_COLUMNS; i++) { - col = gtk_tree_view_get_column(view_treeview, i); - if (col) - __view_set_column_sort_indicator(col, i); - } -} - void __view_row_activated(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data) { @@ -125,35 +60,6 @@ void __view_column_resized(GtkTreeViewColumn *col, GParamSpec *pspec, settings_set(QUEUE_SETTINGS[index], gtk_tree_view_column_get_width(col)); } -void __view_column_clicked(GtkTreeViewColumn *col, gpointer data) -{ - struct playlist *playlist = gui_model_get_playlist(); - struct queue *queue = playlist ? &playlist->pl_queue : NULL; - unsigned int index = __view_get_column_index(col); - bool reset = view_sort_count == 0; - gchar *text; - - if (!queue || queue_has_flag(queue, Q_NO_SORT)) - return; - - if (playlist_get_queue(PL_SYSTEM, gui_queue(queue)->gq_text)) - playlist_sort(PL_SYSTEM, gui_queue(queue)->gq_text, QUEUE_SORT[index], reset); - else - queue_sort(queue, QUEUE_SORT[index], reset); - - if (view_sort_count == 0) { - text = g_strdup_printf("Sorting within %s", - gtk_tree_view_column_get_title(col)); - __view_display_sorting(text); - g_free(text); - } - - __view_set_sort_indicators(); - - view_sort_count++; - g_timeout_add_seconds(3, __view_dec_sort, NULL); -} - struct view_add_data { enum playlist_type_t vad_type; const gchar *vad_name; @@ -395,11 +301,6 @@ void gui_view_init() void gui_view_set_playlist(struct playlist *playlist) { gui_filter_set_playlist(playlist); - - view_sort_count = 0; - __view_display_sorting(""); - __view_set_sort_indicators(); - gui_view_scroll(); } diff --git a/include/gui/treeview.h b/include/gui/treeview.h index 03bbd863..55bc6a95 100644 --- a/include/gui/treeview.h +++ b/include/gui/treeview.h @@ -8,10 +8,22 @@ /* Called to initialize the treeview widget. */ void gui_treeview_init(); +/* Called to deinitialize the treeview layer. */ +void gui_treeview_deinit(); + +/* Called to set the current playlist. */ +void gui_treeview_set_playlist(struct playlist *); + /* Called to access the treeview widget. */ static inline GtkTreeView *gui_treeview() { return GTK_TREE_VIEW(gui_builder_widget("treeview")); } +/* Called to access the sorting display widget. */ +static inline GtkLabel *gui_sorting() +{ + return GTK_LABEL(gui_builder_widget("sorting")); +} + #endif /* OCARINA_GUI_TREEVIEW_H */ diff --git a/share/ocarina/ocarina.ui b/share/ocarina/ocarina.ui index d1a12527..1d290fa7 100644 --- a/share/ocarina/ocarina.ui +++ b/share/ocarina/ocarina.ui @@ -898,7 +898,6 @@ audio-volume-medium 20 # True - @@ -916,7 +915,6 @@ audio-volume-medium 300 Title True - @@ -934,7 +932,6 @@ audio-volume-medium 60 Length True - @@ -952,7 +949,6 @@ audio-volume-medium 100 Artist True - @@ -970,7 +966,6 @@ audio-volume-medium 100 Album True - @@ -988,7 +983,6 @@ audio-volume-medium 45 Year True - @@ -1006,7 +1000,6 @@ audio-volume-medium 100 Genre True - @@ -1024,7 +1017,6 @@ audio-volume-medium 60 Count True - @@ -1042,7 +1034,6 @@ audio-volume-medium 1 Played True - @@ -1076,7 +1067,7 @@ audio-volume-medium - + True False center diff --git a/tests/gui/treeview.c b/tests/gui/treeview.c index b7cae247..3e5dc8f2 100644 --- a/tests/gui/treeview.c +++ b/tests/gui/treeview.c @@ -10,15 +10,105 @@ struct core_init_data init_data = {}; -void test_treeview() +void test_treeview_init() { g_assert_nonnull(gui_treeview()); g_assert_true(GTK_IS_TREE_VIEW(gui_treeview())); - g_assert(gtk_tree_view_get_model(gui_treeview()) == GTK_TREE_MODEL(gui_filter_get())); } +void test_treeview_sort() +{ + GtkTreeViewColumn *col; + unsigned int i, sort; + + /* Switch to collection playlist and check sort arrows. */ + gui_treeview_set_playlist(playlist_get(PL_SYSTEM, "Collection")); + g_assert(gui_model_get_playlist() == + playlist_get(PL_SYSTEM, "Collection")); + + for (i = 0; i < GUI_MODEL_N_COLUMNS; i++) { + col = gtk_tree_view_get_column(gui_treeview(), i); + switch (i) { + case GUI_MODEL_FILE_PATH: + case GUI_MODEL_FONT: + g_assert_null(col); + break; + case GUI_MODEL_ARTIST: + case GUI_MODEL_YEAR: + case GUI_MODEL_TRACK_NR: + g_assert_true( + gtk_tree_view_column_get_sort_indicator(col)); + g_assert_cmpuint( + gtk_tree_view_column_get_sort_order(col), ==, + GTK_SORT_ASCENDING); + break; + default: + g_assert_false( + gtk_tree_view_column_get_sort_indicator(col)); + } + } + + /* Click some columns and check that indicators change. */ + col = gtk_tree_view_get_column(gui_treeview(), GUI_MODEL_YEAR); + gtk_tree_view_column_clicked(col); + g_assert_cmpstr(gtk_label_get_text(gui_sorting()), ==, + "Sorting: {Year}"); + + col = gtk_tree_view_get_column(gui_treeview(), GUI_MODEL_LENGTH); + gtk_tree_view_column_clicked(col); + g_assert_cmpstr(gtk_label_get_text(gui_sorting()), ==, + "Sorting: {Year, Length}"); + + col = gtk_tree_view_get_column(gui_treeview(), GUI_MODEL_TRACK_NR); + gtk_tree_view_column_clicked(col); + g_assert_cmpstr(gtk_label_get_text(gui_sorting()), ==, + "Sorting: {Year, Length, #}"); + + col = gtk_tree_view_get_column(gui_treeview(), GUI_MODEL_LENGTH); + gtk_tree_view_column_clicked(col); + g_assert_cmpstr(gtk_label_get_text(gui_sorting()), ==, + "Sorting: {Year, -Length, #}"); + + for (i = 0; i < GUI_MODEL_N_COLUMNS; i++) { + sort = GTK_SORT_ASCENDING; + col = gtk_tree_view_get_column(gui_treeview(), i); + switch (i) { + case GUI_MODEL_FILE_PATH: + case GUI_MODEL_FONT: + g_assert_null(col); + break; + case GUI_MODEL_LENGTH: + sort = GTK_SORT_DESCENDING; + case GUI_MODEL_YEAR: + case GUI_MODEL_TRACK_NR: + g_assert_true( + gtk_tree_view_column_get_sort_indicator(col)); + g_assert_cmpuint( + gtk_tree_view_column_get_sort_order(col), ==, + sort); + break; + default: + g_assert_false( + gtk_tree_view_column_get_sort_indicator(col)); + } + } + + /* History playlist should not have any sort arrows. */ + gui_treeview_set_playlist(playlist_get(PL_SYSTEM, "History")); + g_assert(gui_model_get_playlist() == + playlist_get(PL_SYSTEM, "History")); + g_assert_cmpstr(gtk_label_get_text(gui_sorting()), ==, ""); + + for (i = 0; i < GUI_MODEL_N_COLUMNS; i++) { + col = gtk_tree_view_get_column(gui_treeview(), i); + if (!col) + break; + g_assert_false(gtk_tree_view_column_get_sort_indicator(col)); + } +} + int main(int argc, char **argv) { int ret; @@ -32,10 +122,12 @@ int main(int argc, char **argv) while (idle_run_task()) {} g_test_init(&argc, &argv, NULL); - g_test_add_func("/Gui/Treeview", test_treeview); + g_test_add_func("/Gui/Treeview/Init", test_treeview_init); + g_test_add_func("/Gui/Treeview/Sort", test_treeview_sort); ret = g_test_run(); core_deinit(); + gui_treeview_deinit(); gui_filter_deinit(); gui_model_deinit(); return ret;