diff options
| -rw-r--r-- | fiv-io.c | 76 | ||||
| -rw-r--r-- | fiv-io.h | 9 | ||||
| -rw-r--r-- | fiv.c | 81 | 
3 files changed, 145 insertions, 21 deletions
@@ -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); @@ -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) @@ -26,6 +26,7 @@  #include <stdlib.h>  #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; @@ -437,6 +440,18 @@ on_filtering_toggled(GtkToggleButton *button, G_GNUC_UNUSED gpointer user_data)  }  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)  {  	GFile *file = g_file_new_for_uri(uri); @@ -658,6 +673,26 @@ on_notify_filtering(  }  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)  {  	if (gdk_window_get_state(gtk_widget_get_window(g.window)) & @@ -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;  }  | 
