filechooser: Bring back sorting

Add a sort model between the filter model and the
file system model, and set it up to sort according
to the circumstances.
This commit is contained in:
Matthias Clasen 2022-10-11 11:02:07 -04:00
parent d0d0409f9d
commit c0c3d75062

View File

@ -95,6 +95,8 @@
#include "gtkstringlist.h"
#include "gtkfilterlistmodel.h"
#include "gtkcustomfilter.h"
#include "gtkcustomsorter.h"
#include "gtkmultisorter.h"
#ifndef G_OS_WIN32
#include "gopenuriportal.h"
@ -200,6 +202,7 @@ struct _GtkFileChooserWidget
* selection model.
*/
GtkSelectionModel *selection_model;
GtkSortListModel *sort_model;
GtkFilterListModel *filter_model;
/* The file browsing widgets */
@ -284,6 +287,7 @@ struct _GtkFileChooserWidget
GFile *current_folder;
GFile *renamed_file;
GtkColumnViewColumn *column_view_name_column;
GtkColumnViewColumn *column_view_location_column;
GtkColumnViewColumn *column_view_size_column;
GtkColumnViewColumn *column_view_time_column;
@ -600,6 +604,7 @@ gtk_file_chooser_widget_finalize (GObject *object)
g_clear_object (&impl->model_for_search);
g_clear_object (&impl->selection_model);
g_clear_object (&impl->sort_model);
g_clear_object (&impl->filter_model);
/* stopping the load above should have cleared this */
@ -621,6 +626,8 @@ get_toplevel (GtkWidget *widget)
return NULL;
}
static void setup_sorting (GtkFileChooserWidget *impl);
static GListModel *
get_current_model (GtkFileChooserWidget *self)
{
@ -634,6 +641,7 @@ set_current_model (GtkFileChooserWidget *self,
gtk_filter_list_model_set_model (self->filter_model, model);
gtk_filter_changed (gtk_filter_list_model_get_filter (self->filter_model),
GTK_FILTER_CHANGE_DIFFERENT);
setup_sorting (self);
}
/* Extracts the parent folders out of the supplied list of GtkRecentInfo* items, and returns
@ -1555,6 +1563,9 @@ change_sort_directories_first_state (GSimpleAction *action,
g_simple_action_set_state (action, state);
impl->sort_directories_first = g_variant_get_boolean (state);
gtk_sorter_changed (gtk_sort_list_model_get_sorter (impl->sort_model),
GTK_SORTER_CHANGE_DIFFERENT);
}
static void
@ -2049,7 +2060,8 @@ column_view_get_file_date (GtkListItem *item,
impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (gtk_list_item_get_child (item),
GTK_TYPE_FILE_CHOOSER_WIDGET));
g_assert (impl != NULL);
if (!impl)
return NULL;
if (impl->operation_mode == OPERATION_MODE_RECENT)
time = (glong) g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
@ -2084,7 +2096,8 @@ column_view_get_file_time (GtkListItem *item,
impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (gtk_list_item_get_child (item),
GTK_TYPE_FILE_CHOOSER_WIDGET));
g_assert (impl != NULL);
if (!impl)
return NULL;
if (impl->operation_mode == OPERATION_MODE_RECENT)
time = (glong) g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
@ -2108,16 +2121,16 @@ column_view_get_file_type (GtkListItem *item,
impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (gtk_list_item_get_child (item),
GTK_TYPE_FILE_CHOOSER_WIDGET));
g_assert (impl != NULL);
if (!impl)
return NULL;
return get_type_information (impl, info);
}
static char *
column_view_get_location (GtkListItem *list_item,
GFileInfo *info)
file_chooser_get_location (GtkFileChooserWidget *impl,
GFileInfo *info)
{
GtkFileChooserWidget *impl;
GFile *home_location;
GFile *dir_location;
GFile *file;
@ -2145,11 +2158,6 @@ column_view_get_location (GtkListItem *list_item,
dir_location = g_file_get_parent (target);
g_clear_object (&target);
}
impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (gtk_list_item_get_child (list_item),
GTK_TYPE_FILE_CHOOSER_WIDGET));
g_assert (impl != NULL);
if (!dir_location)
location = g_strdup ("/");
else if (impl->current_folder && g_file_equal (impl->current_folder, dir_location))
@ -2174,6 +2182,20 @@ column_view_get_location (GtkListItem *list_item,
return g_steal_pointer (&location);
}
static char *
column_view_get_location (GtkListItem *list_item,
GFileInfo *info)
{
GtkFileChooserWidget *impl;
impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (gtk_list_item_get_child (list_item),
GTK_TYPE_FILE_CHOOSER_WIDGET));
if (!impl)
return NULL;
return file_chooser_get_location (impl, info);
}
static char *
column_view_get_size (GtkListItem *item,
GFileInfo *info)
@ -2191,7 +2213,8 @@ column_view_get_time_visible (GtkListItem *item)
impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (gtk_list_item_get_child (item),
GTK_TYPE_FILE_CHOOSER_WIDGET));
g_assert (impl != NULL);
if (!impl)
return FALSE;
return impl->show_time;
}
@ -2682,7 +2705,7 @@ set_select_multiple (GtkFileChooserWidget *impl,
gtk_column_view_set_enable_rubberband (GTK_COLUMN_VIEW (impl->browse_files_column_view),
select_multiple);
model = g_object_ref (G_LIST_MODEL (impl->filter_model));
model = g_object_ref (G_LIST_MODEL (impl->sort_model));
g_clear_object (&impl->selection_model);
@ -2696,11 +2719,6 @@ set_select_multiple (GtkFileChooserWidget *impl,
g_signal_connect (impl->selection_model, "items-changed",
G_CALLBACK (list_items_changed), impl);
g_signal_connect (impl->selection_model,
"items-changed",
G_CALLBACK (list_items_changed),
impl);
gtk_column_view_set_model (GTK_COLUMN_VIEW (impl->browse_files_column_view),
GTK_SELECTION_MODEL (impl->selection_model));
@ -7024,6 +7042,7 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class)
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_new_folder_button);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_path_bar_size_group);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_path_bar);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, column_view_name_column);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, column_view_location_column);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, column_view_size_column);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, column_view_time_column);
@ -7217,14 +7236,218 @@ match_func (gpointer item, gpointer user_data)
return g_file_info_get_attribute_boolean (G_FILE_INFO (item), "filechooser::visible");
}
static GtkOrdering
directory_sort_func (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
GtkFileChooserWidget *impl = user_data;
if (impl->sort_directories_first)
{
gboolean adir = _gtk_file_info_consider_as_directory ((GFileInfo *)a);
gboolean bdir = _gtk_file_info_consider_as_directory ((GFileInfo *)b);
if (adir && !bdir)
return GTK_ORDERING_SMALLER;
if (!adir && bdir)
return GTK_ORDERING_LARGER;
}
return GTK_ORDERING_EQUAL;
}
static GtkOrdering
name_sort_func (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
char *key_a, *key_b;
GtkOrdering result;
/* FIXME: use sortkeys for these */
key_a = g_utf8_collate_key_for_filename (g_file_info_get_display_name ((GFileInfo *)a), -1);
key_b = g_utf8_collate_key_for_filename (g_file_info_get_display_name ((GFileInfo *)b), -1);
result = g_strcmp0 (key_a, key_b);
g_free (key_a);
g_free (key_b);
return result;
}
static GtkOrdering
location_sort_func (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
GtkFileChooserWidget *impl = user_data;
char *key_a, *key_b;
GtkOrdering result;
/* FIXME: use sortkeys for these */
key_a = g_utf8_collate_key_for_filename (file_chooser_get_location (impl, (GFileInfo *)a), -1);
key_b = g_utf8_collate_key_for_filename (file_chooser_get_location (impl, (GFileInfo *)b), -1);
result = g_strcmp0 (key_a, key_b);
g_free (key_a);
g_free (key_b);
return result;
}
static GtkOrdering
size_sort_func (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
gint64 size_a, size_b;
size_a = g_file_info_get_size ((GFileInfo *)a);
size_b = g_file_info_get_size ((GFileInfo *)b);
if (size_a < size_b)
return GTK_ORDERING_SMALLER;
else if (size_a > size_b)
return GTK_ORDERING_LARGER;
else
return GTK_ORDERING_EQUAL;
}
static GtkOrdering
type_sort_func (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
GtkFileChooserWidget *impl = user_data;
char *key_a, *key_b;
GtkOrdering result;
/* FIXME: use sortkeys for these */
key_a = get_type_information (impl, (GFileInfo *)a);
key_b = get_type_information (impl, (GFileInfo *)b);
result = g_strcmp0 (key_a, key_b);
g_free (key_a);
g_free (key_b);
return result;
}
static GtkOrdering
time_sort_func (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
GtkFileChooserWidget *impl = user_data;
glong time_a, time_b;
if (impl->operation_mode == OPERATION_MODE_RECENT)
time_a = (glong) g_file_info_get_attribute_uint64 ((GFileInfo *)a, G_FILE_ATTRIBUTE_TIME_ACCESS);
else
time_b = (glong) g_file_info_get_attribute_uint64 ((GFileInfo *)b, G_FILE_ATTRIBUTE_TIME_MODIFIED);
if (time_a < time_b)
return GTK_ORDERING_SMALLER;
else if (time_a > time_b)
return GTK_ORDERING_LARGER;
else
return GTK_ORDERING_EQUAL;
}
static GtkOrdering
recent_sort_func (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
GtkOrdering result;
result = time_sort_func (a, b, user_data);
if (result == GTK_ORDERING_EQUAL)
result = name_sort_func (a, b, user_data);
if (result == GTK_ORDERING_EQUAL)
result = location_sort_func (a, b, user_data);
return result;
}
static GtkOrdering
search_sort_func (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
GtkOrdering result;
result = location_sort_func (a, b, user_data);
if (result == GTK_ORDERING_EQUAL)
result = name_sort_func (a, b, user_data);
if (result == GTK_ORDERING_EQUAL)
result = time_sort_func (a, b, user_data);
return result;
}
static void
setup_sorting (GtkFileChooserWidget *impl)
{
GtkFileSystemModel *fsmodel;
GtkSorter *sorter = NULL;
fsmodel = GTK_FILE_SYSTEM_MODEL (get_current_model (impl));
gtk_column_view_column_set_sorter (impl->column_view_name_column, NULL);
gtk_column_view_column_set_sorter (impl->column_view_location_column, NULL);
gtk_column_view_column_set_sorter (impl->column_view_size_column, NULL);
gtk_column_view_column_set_sorter (impl->column_view_type_column, NULL);
gtk_column_view_column_set_sorter (impl->column_view_time_column, NULL);
if (fsmodel == impl->browse_files_model)
{
gtk_column_view_column_set_sorter (impl->column_view_name_column, GTK_SORTER (gtk_custom_sorter_new (name_sort_func, impl, NULL)));
gtk_column_view_column_set_sorter (impl->column_view_location_column, GTK_SORTER (gtk_custom_sorter_new (location_sort_func, impl, NULL)));
gtk_column_view_column_set_sorter (impl->column_view_size_column, GTK_SORTER (gtk_custom_sorter_new (size_sort_func, impl, NULL)));
gtk_column_view_column_set_sorter (impl->column_view_type_column, GTK_SORTER (gtk_custom_sorter_new (type_sort_func, impl, NULL)));
gtk_column_view_column_set_sorter (impl->column_view_time_column, GTK_SORTER (gtk_custom_sorter_new (time_sort_func, impl, NULL)));
sorter = GTK_SORTER (gtk_multi_sorter_new ());
gtk_multi_sorter_append (GTK_MULTI_SORTER (sorter), GTK_SORTER (gtk_custom_sorter_new (directory_sort_func, impl, NULL)));
gtk_multi_sorter_append (GTK_MULTI_SORTER (sorter), g_object_ref (gtk_column_view_get_sorter (GTK_COLUMN_VIEW (impl->browse_files_column_view))));
}
else if (fsmodel == impl->recent_model)
{
sorter = GTK_SORTER (gtk_custom_sorter_new (recent_sort_func, impl, NULL));
}
else if (fsmodel == impl->search_model)
{
sorter = GTK_SORTER (gtk_custom_sorter_new (search_sort_func, impl, NULL));
}
gtk_sort_list_model_set_sorter (impl->sort_model, sorter);
g_clear_object (&sorter);
}
static void
gtk_file_chooser_widget_init (GtkFileChooserWidget *impl)
{
GtkExpression *expression;
impl->selection_model = GTK_SELECTION_MODEL (gtk_single_selection_new (NULL));
impl->filter_model = gtk_filter_list_model_new (NULL, GTK_FILTER (gtk_custom_filter_new (match_func, NULL, NULL)));
/* Ensure private types used by the template
* definition before calling gtk_widget_init_template()
*/
g_type_ensure (GTK_TYPE_PATH_BAR);
g_type_ensure (GTK_TYPE_PLACES_VIEW);
g_type_ensure (GTK_TYPE_PLACES_SIDEBAR);
g_type_ensure (GTK_TYPE_FILE_CHOOSER_ERROR_STACK);
impl->select_multiple = FALSE;
impl->sort_directories_first = FALSE;
impl->show_hidden = FALSE;
impl->show_size_column = TRUE;
impl->show_type_column = TRUE;
@ -7239,25 +7462,21 @@ gtk_file_chooser_widget_init (GtkFileChooserWidget *impl)
impl->auto_selecting_first_row = FALSE;
impl->renamed_file = NULL;
gtk_single_selection_set_model (GTK_SINGLE_SELECTION (impl->selection_model), G_LIST_MODEL (impl->filter_model));
gtk_widget_init_template (GTK_WIDGET (impl));
impl->selection_model = GTK_SELECTION_MODEL (gtk_single_selection_new (NULL));
impl->filter_model = gtk_filter_list_model_new (NULL, GTK_FILTER (gtk_custom_filter_new (match_func, NULL, NULL)));
impl->sort_model = gtk_sort_list_model_new (NULL, NULL);
g_signal_connect (impl->selection_model, "selection-changed",
G_CALLBACK (list_selection_changed), impl);
g_signal_connect (impl->selection_model, "items-changed",
G_CALLBACK (list_items_changed), impl);
/* Ensure private types used by the template
* definition before calling gtk_widget_init_template()
*/
g_type_ensure (GTK_TYPE_PATH_BAR);
g_type_ensure (GTK_TYPE_PLACES_VIEW);
g_type_ensure (GTK_TYPE_PLACES_SIDEBAR);
g_type_ensure (GTK_TYPE_FILE_CHOOSER_ERROR_STACK);
gtk_single_selection_set_model (GTK_SINGLE_SELECTION (impl->selection_model), G_LIST_MODEL (impl->sort_model));
gtk_sort_list_model_set_model (impl->sort_model, G_LIST_MODEL (impl->filter_model));
gtk_widget_init_template (GTK_WIDGET (impl));
gtk_column_view_set_model (GTK_COLUMN_VIEW (impl->browse_files_column_view),
impl->selection_model);
gtk_column_view_set_model (GTK_COLUMN_VIEW (impl->browse_files_column_view), impl->selection_model);
g_signal_connect (impl, "notify::display,", G_CALLBACK (display_changed_cb), impl);
check_icon_theme (impl);