From 5abf6a719f6e1326d3259d92aff1849e00433eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Wed, 5 Jan 2022 08:26:28 +0100 Subject: [PATCH] Add UI for sort order settings --- fiv-io.c | 76 ++++++++++++++++++++++++++++++++++++++-------------- fiv-io.h | 9 +++++++ fiv.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 145 insertions(+), 21 deletions(-) diff --git a/fiv-io.c b/fiv-io.c index 518da32..012202e 100644 --- a/fiv-io.c +++ b/fiv-io.c @@ -2464,11 +2464,6 @@ model_entry_finalize(ModelEntry *entry) g_free(entry->uri); } -typedef enum _FivIoModelSort { - FIV_IO_MODEL_SORT_NAME, - FIV_IO_MODEL_SORT_MTIME, -} FivIoModelSort; - struct _FivIoModel { GObject parent_instance; gchar **supported_globs; @@ -2478,7 +2473,7 @@ struct _FivIoModel { GArray *subdirs; ///< "directory" contents GArray *files; ///< "directory" contents - FivIoModelSort sort; ///< How to sort + FivIoModelSort sort_field; ///< How to sort gboolean sort_descending; ///< Whether to sort in reverse gboolean filtering; ///< Only show non-hidden, supported }; @@ -2487,6 +2482,8 @@ G_DEFINE_TYPE(FivIoModel, fiv_io_model, G_TYPE_OBJECT) enum { PROP_FILTERING = 1, + PROP_SORT_FIELD, + PROP_SORT_DESCENDING, N_PROPERTIES }; @@ -2546,13 +2543,17 @@ model_compare_entries(FivIoModel *self, const ModelEntry *entry1, GFile *file1, return -1; int result = 0; - switch (self->sort) { - case FIV_IO_MODEL_SORT_NAME: - result = model_compare_name(file1, file2); - break; + switch (self->sort_field) { case FIV_IO_MODEL_SORT_MTIME: result -= entry1->mtime_msec < entry2->mtime_msec; result += entry1->mtime_msec > entry2->mtime_msec; + if (result != 0) + break; + + // Fall-through + case FIV_IO_MODEL_SORT_NAME: + case FIV_IO_MODEL_SORT_COUNT: + result = model_compare_name(file1, file2); } return self->sort_descending ? -result : +result; } @@ -2570,6 +2571,16 @@ model_compare(gconstpointer a, gconstpointer b, gpointer user_data) return result; } +static void +model_resort(FivIoModel *self) +{ + g_array_sort_with_data(self->subdirs, model_compare, self); + g_array_sort_with_data(self->files, model_compare, self); + + g_signal_emit(self, model_signals[FILES_CHANGED], 0); + g_signal_emit(self, model_signals[SUBDIRECTORIES_CHANGED], 0); +} + static gboolean model_reload(FivIoModel *self, GError **error) { @@ -2613,11 +2624,9 @@ model_reload(FivIoModel *self, GError **error) } } g_object_unref(enumerator); - g_array_sort_with_data(self->subdirs, model_compare, self); - g_array_sort_with_data(self->files, model_compare, self); - g_signal_emit(self, model_signals[FILES_CHANGED], 0); - g_signal_emit(self, model_signals[SUBDIRECTORIES_CHANGED], 0); + // We also emit change signals there, indirectly. + model_resort(self); return TRUE; } @@ -2644,6 +2653,12 @@ fiv_io_model_get_property( case PROP_FILTERING: g_value_set_boolean(value, self->filtering); break; + case PROP_SORT_FIELD: + g_value_set_int(value, self->sort_field); + break; + case PROP_SORT_DESCENDING: + g_value_set_boolean(value, self->sort_descending); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } @@ -2656,12 +2671,25 @@ fiv_io_model_set_property( FivIoModel *self = FIV_IO_MODEL(object); switch (property_id) { case PROP_FILTERING: - if (self->filtering == g_value_get_boolean(value)) - return; - - self->filtering = !self->filtering; - g_object_notify_by_pspec(object, model_properties[PROP_FILTERING]); - (void) model_reload(self, NULL /* error */); + if (self->filtering != g_value_get_boolean(value)) { + self->filtering = !self->filtering; + g_object_notify_by_pspec(object, model_properties[property_id]); + (void) model_reload(self, NULL /* error */); + } + break; + case PROP_SORT_FIELD: + if ((int) self->sort_field != g_value_get_int(value)) { + self->sort_field = g_value_get_int(value); + g_object_notify_by_pspec(object, model_properties[property_id]); + model_resort(self); + } + break; + case PROP_SORT_DESCENDING: + if (self->sort_descending != g_value_get_boolean(value)) { + self->sort_descending = !self->sort_descending; + g_object_notify_by_pspec(object, model_properties[property_id]); + model_resort(self); + } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); @@ -2679,6 +2707,14 @@ fiv_io_model_class_init(FivIoModelClass *klass) model_properties[PROP_FILTERING] = g_param_spec_boolean( "filtering", "Filtering", "Only show non-hidden, supported entries", TRUE, G_PARAM_READWRITE); + // TODO(p): GObject enumerations are annoying, but this should be one. + model_properties[PROP_SORT_FIELD] = g_param_spec_int( + "sort-field", "Sort field", "Sort order", + FIV_IO_MODEL_SORT_MIN, FIV_IO_MODEL_SORT_MAX, + FIV_IO_MODEL_SORT_NAME, G_PARAM_READWRITE); + model_properties[PROP_SORT_DESCENDING] = g_param_spec_boolean( + "sort-descending", "Sort descending", "Use reverse sort order", + FALSE, G_PARAM_READWRITE); g_object_class_install_properties( object_class, N_PROPERTIES, model_properties); diff --git a/fiv-io.h b/fiv-io.h index e3deac5..f8f3118 100644 --- a/fiv-io.h +++ b/fiv-io.h @@ -79,6 +79,15 @@ cairo_surface_t *fiv_io_open_from_data(const char *data, size_t len, // --- Filesystem -------------------------------------------------------------- +typedef enum _FivIoModelSort { + FIV_IO_MODEL_SORT_NAME, + FIV_IO_MODEL_SORT_MTIME, + FIV_IO_MODEL_SORT_COUNT, + + FIV_IO_MODEL_SORT_MIN = 0, + FIV_IO_MODEL_SORT_MAX = FIV_IO_MODEL_SORT_COUNT - 1 +} FivIoModelSort; + #define FIV_TYPE_IO_MODEL (fiv_io_model_get_type()) G_DECLARE_FINAL_TYPE(FivIoModel, fiv_io_model, FIV, IO_MODEL, GObject) diff --git a/fiv.c b/fiv.c index 9f66c29..f8ddb10 100644 --- a/fiv.c +++ b/fiv.c @@ -26,6 +26,7 @@ #include #include "config.h" + #include "fiv-browser.h" #include "fiv-io.h" #include "fiv-sidebar.h" @@ -273,6 +274,8 @@ struct { GtkWidget *browser_sidebar; GtkWidget *plus; GtkWidget *minus; + GtkWidget *sort_field[FIV_IO_MODEL_SORT_COUNT]; + GtkWidget *sort_direction[2]; GtkWidget *browser_scroller; GtkWidget *browser; @@ -436,6 +439,18 @@ on_filtering_toggled(GtkToggleButton *button, G_GNUC_UNUSED gpointer user_data) g.model, "filtering", gtk_toggle_button_get_active(button), NULL); } +static void +on_sort_field(G_GNUC_UNUSED GtkMenuItem *item, gpointer data) +{ + g_object_set(g.model, "sort-field", (gint) (intptr_t) data, NULL); +} + +static void +on_sort_direction(G_GNUC_UNUSED GtkMenuItem *item, gpointer data) +{ + g_object_set(g.model, "sort-descending", (gboolean) (intptr_t) data, NULL); +} + static void open(const gchar *uri) { @@ -657,6 +672,26 @@ on_notify_filtering( gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(user_data), b); } +static void +on_notify_sort_field( + GObject *object, GParamSpec *param_spec, G_GNUC_UNUSED gpointer user_data) +{ + gint field = -1; + g_object_get(object, g_param_spec_get_name(param_spec), &field, NULL); + gtk_check_menu_item_set_active( + GTK_CHECK_MENU_ITEM(g.sort_field[field]), TRUE); +} + +static void +on_notify_sort_descending( + GObject *object, GParamSpec *param_spec, G_GNUC_UNUSED gpointer user_data) +{ + gboolean b = FALSE; + g_object_get(object, g_param_spec_get_name(param_spec), &b, NULL); + gtk_check_menu_item_set_active( + GTK_CHECK_MENU_ITEM(g.sort_direction[b]), TRUE); +} + static void toggle_fullscreen(void) { @@ -1180,17 +1215,61 @@ make_browser_sidebar(FivIoModel *model) g_signal_connect(funnel, "toggled", G_CALLBACK(on_filtering_toggled), NULL); + GtkWidget *menu = gtk_menu_new(); + g.sort_field[0] = gtk_radio_menu_item_new_with_mnemonic(NULL, "By _Name"); + g.sort_field[1] = gtk_radio_menu_item_new_with_mnemonic( + gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(g.sort_field[0])), + "By _Modification Time"); + for (int i = FIV_IO_MODEL_SORT_MIN; i <= FIV_IO_MODEL_SORT_MAX; i++) { + g_signal_connect(g.sort_field[i], "activate", + G_CALLBACK(on_sort_field), (void *) (intptr_t) i); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), g.sort_field[i]); + } + + g.sort_direction[0] = + gtk_radio_menu_item_new_with_mnemonic(NULL, "_Ascending"); + g.sort_direction[1] = gtk_radio_menu_item_new_with_mnemonic( + gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(g.sort_direction[0])), + "_Descending"); + g_signal_connect(g.sort_direction[0], "activate", + G_CALLBACK(on_sort_direction), (void *) 0); + g_signal_connect(g.sort_direction[1], "activate", + G_CALLBACK(on_sort_direction), (void *) 1); + + gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new()); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), g.sort_direction[0]); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), g.sort_direction[1]); + gtk_widget_show_all(menu); + + GtkWidget *sort = gtk_menu_button_new(); + gtk_widget_set_tooltip_text(sort, "Sort order"); + gtk_button_set_image(GTK_BUTTON(sort), + gtk_image_new_from_icon_name( + "view-sort-ascending-symbolic", GTK_ICON_SIZE_BUTTON)); + gtk_menu_button_set_popup(GTK_MENU_BUTTON(sort), menu); + + GtkWidget *model_group = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_style_context_add_class( + gtk_widget_get_style_context(model_group), GTK_STYLE_CLASS_LINKED); + gtk_box_pack_start(GTK_BOX(model_group), funnel, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(model_group), sort, FALSE, FALSE, 0); + GtkBox *toolbar = fiv_sidebar_get_toolbar(FIV_SIDEBAR(sidebar)); gtk_box_pack_start(toolbar, zoom_group, FALSE, FALSE, 0); - gtk_box_pack_start(toolbar, funnel, FALSE, FALSE, 0); + gtk_box_pack_start(toolbar, model_group, FALSE, FALSE, 0); gtk_widget_set_halign(GTK_WIDGET(toolbar), GTK_ALIGN_CENTER); g_signal_connect(g.browser, "notify::thumbnail-size", G_CALLBACK(on_notify_thumbnail_size), NULL); g_signal_connect(model, "notify::filtering", G_CALLBACK(on_notify_filtering), funnel); + g_signal_connect(model, "notify::sort-field", + G_CALLBACK(on_notify_sort_field), NULL); + g_signal_connect(model, "notify::sort-descending", + G_CALLBACK(on_notify_sort_descending), NULL); on_toolbar_zoom(NULL, (gpointer) 0); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(funnel), TRUE); + // TODO(p): Invoke sort configuration notifications explicitly. return sidebar; }