From d92b96b9adb5fb1beb4f65d84585c360da820f55 Mon Sep 17 00:00:00 2001 From: Pratyaksh Gautam <pratyakshgautam11@gmail.com> Date: Tue, 25 Oct 2022 20:01:34 +0000 Subject: [PATCH] List View: Add file count to size column for directories (Issue #61) - File count for each directory will be counted in a separate job - Configurable in the 'Configure columns ..' menu - Options are 'Never' / 'Only for local files' / 'Always' --- thunar/thunar-column-editor.c | 48 +++ thunar/thunar-enum-types.c | 58 ++++ thunar/thunar-enum-types.h | 35 +++ thunar/thunar-file.c | 104 ++++++- thunar/thunar-file.h | 7 + thunar/thunar-io-jobs.c | 70 ++++- thunar/thunar-io-jobs.h | 47 +-- thunar/thunar-list-model.c | 477 +++++++++++++++++++---------- thunar/thunar-preferences-dialog.c | 39 --- thunar/thunar-preferences.c | 14 + thunar/thunar-standard-view.c | 1 + 11 files changed, 670 insertions(+), 230 deletions(-) diff --git a/thunar/thunar-column-editor.c b/thunar/thunar-column-editor.c index d01e4ee50..e920f507a 100644 --- a/thunar/thunar-column-editor.c +++ b/thunar/thunar-column-editor.c @@ -128,12 +128,14 @@ thunar_column_editor_init (ThunarColumnEditor *column_editor) GtkTreeIter iter; GtkWidget *separator; GtkWidget *button; + GtkWidget *combo; GtkWidget *frame; GtkWidget *image; GtkWidget *label; GtkWidget *grid; GtkWidget *vbox; GtkWidget *swin; + GEnumClass *enum_class; gint row = 0; /* grab a reference on the preferences */ @@ -334,6 +336,52 @@ thunar_column_editor_init (ThunarColumnEditor *column_editor) thunar_gtk_label_set_a11y_relation (GTK_LABEL (label), button); gtk_widget_show (button); + frame = g_object_new (GTK_TYPE_FRAME, "border-width", 0, "shadow-type", GTK_SHADOW_NONE, NULL); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, TRUE, 0); + gtk_widget_show (frame); + + label = gtk_label_new (_("Size Column of Folders")); + gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ()); + gtk_frame_set_label_widget (GTK_FRAME (frame), label); + gtk_widget_show (label); + + grid = gtk_grid_new (); + gtk_grid_set_column_spacing (GTK_GRID (grid), 6); + gtk_grid_set_row_spacing (GTK_GRID (grid), 6); + gtk_container_set_border_width (GTK_CONTAINER (grid), 12); + gtk_container_add (GTK_CONTAINER (frame), grid); + gtk_widget_show (grid); + + /* explain what it does */ + label = gtk_label_new (_("Show number of containing items")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0f); + gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1); + gtk_widget_show (label); + + combo = gtk_combo_box_text_new (); + enum_class = g_type_class_ref (THUNAR_TYPE_FOLDER_ITEM_COUNT); + + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), + g_enum_get_value (enum_class, THUNAR_FOLDER_ITEM_COUNT_NEVER)->value_nick); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), + g_enum_get_value (enum_class, THUNAR_FOLDER_ITEM_COUNT_ONLY_LOCAL)->value_nick); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), + g_enum_get_value (enum_class, THUNAR_FOLDER_ITEM_COUNT_ALWAYS)->value_nick); + g_type_class_unref (enum_class); + + g_object_bind_property_full (G_OBJECT (column_editor->preferences), "misc-folder-item-count", + G_OBJECT (combo), "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + transform_enum_value_to_index, + transform_index_to_enum_value, + (gpointer) thunar_folder_item_count_get_type, NULL); + + gtk_widget_set_hexpand (combo, TRUE); + gtk_grid_attach (GTK_GRID (grid), combo, 1, row - 1, 1, 1); + thunar_gtk_label_set_a11y_relation (GTK_LABEL (label), combo); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); + gtk_widget_show (combo); + /* setup the tree selection */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (column_editor->tree_view)); g_signal_connect_swapped (G_OBJECT (selection), "changed", G_CALLBACK (thunar_column_editor_update_buttons), column_editor); diff --git a/thunar/thunar-enum-types.c b/thunar/thunar-enum-types.c index 9a7e2343c..5e989dee5 100644 --- a/thunar/thunar-enum-types.c +++ b/thunar/thunar-enum-types.c @@ -37,6 +37,45 @@ static ThunarThumbnailSize thunar_icon_size_to_thumbnail_size (ThunarIconSize +gboolean +transform_enum_value_to_index (GBinding *binding, + const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + GEnumClass *klass; + GType (*type_func)() = user_data; + guint n; + + klass = g_type_class_ref (type_func ()); + for (n = 0; n < klass->n_values; ++n) + if (klass->values[n].value == g_value_get_enum (src_value)) + g_value_set_int (dst_value, n); + g_type_class_unref (klass); + + return TRUE; +} + + + +gboolean +transform_index_to_enum_value (GBinding *binding, + const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + GEnumClass *klass; + GType (*type_func)() = user_data; + + klass = g_type_class_ref (type_func ()); + g_value_set_enum (dst_value, klass->values[g_value_get_int (src_value)].value); + g_type_class_unref (klass); + + return TRUE; +} + + + GType thunar_renamer_mode_get_type (void) { @@ -712,3 +751,22 @@ thunar_image_preview_mode_get_type (void) return type; } +GType thunar_folder_item_count_get_type (void) +{ + static GType type = G_TYPE_INVALID; + + if (G_UNLIKELY (type == G_TYPE_INVALID)) + { + static const GEnumValue values[] = + { + { THUNAR_FOLDER_ITEM_COUNT_NEVER, "THUNAR_FOLDER_ITEM_COUNT_NEVER", N_("Never") }, + { THUNAR_FOLDER_ITEM_COUNT_ONLY_LOCAL, "THUNAR_FOLDER_ITEM_COUNT_ONLY_LOCAL", N_("Only for local files") }, + { THUNAR_FOLDER_ITEM_COUNT_ALWAYS, "THUNAR_FOLDER_ITEM_COUNT_ALWAYS", N_("Always") }, + { 0, NULL, NULL } + }; + + type = g_enum_register_static ("ThunarFolderItemCount", values); + } + + return type; +} diff --git a/thunar/thunar-enum-types.h b/thunar/thunar-enum-types.h index f63092beb..528badeb1 100644 --- a/thunar/thunar-enum-types.h +++ b/thunar/thunar-enum-types.h @@ -25,6 +25,17 @@ G_BEGIN_DECLS; + +gboolean transform_enum_value_to_index (GBinding *binding, + const GValue *src_value, + GValue *dst_value, + gpointer user_data); + +gboolean transform_index_to_enum_value (GBinding *binding, + const GValue *src_value, + GValue *dst_value, + gpointer user_data); + #define THUNAR_TYPE_RENAMER_MODE (thunar_renamer_mode_get_type ()) /** @@ -480,6 +491,30 @@ typedef enum GType thunar_image_preview_mode_get_type (void) G_GNUC_CONST; +/** + * ThunarFolderItemCount + * + * Specify when the size column on a folder + * should instead show the item count of the folder + **/ + +#define THUNAR_TYPE_FOLDER_ITEM_COUNT (thunar_folder_item_count_get_type ()) + +/** + * ThunarFolderItemCount: + * @THUNAR_FOLDER_ITEM_COUNT_NEVER, : never show number of items as the size of the folder + * @THUNAR_FOLDER_ITEM_COUNT_ONLY_LOCAL, : only show number of items as size of folder for local folders + * @THUNAR_FOLDER_ITEM_COUNT_ALWAYS, : always show the number of items as the size of the folder + **/ +typedef enum +{ + THUNAR_FOLDER_ITEM_COUNT_NEVER, + THUNAR_FOLDER_ITEM_COUNT_ONLY_LOCAL, + THUNAR_FOLDER_ITEM_COUNT_ALWAYS, +} ThunarFolderItemCount; + +GType thunar_folder_item_count_get_type (void) G_GNUC_CONST; + G_END_DECLS; #endif /* !__THUNAR_ENUM_TYPES_H__ */ diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c index c2d598d9e..78aed85a6 100644 --- a/thunar/thunar-file.c +++ b/thunar/thunar-file.c @@ -58,16 +58,17 @@ #include <thunar/thunar-application.h> #include <thunar/thunar-chooser-dialog.h> -#include <thunar/thunar-file.h> +#include <thunar/thunar-dialogs.h> #include <thunar/thunar-file-monitor.h> +#include <thunar/thunar-file.h> #include <thunar/thunar-gio-extensions.h> #include <thunar/thunar-gobject-extensions.h> -#include <thunar/thunar-private.h> +#include <thunar/thunar-icon-factory.h> +#include <thunar/thunar-io-jobs.h> #include <thunar/thunar-preferences.h> +#include <thunar/thunar-private.h> #include <thunar/thunar-user.h> #include <thunar/thunar-util.h> -#include <thunar/thunar-dialogs.h> -#include <thunar/thunar-icon-factory.h> @@ -186,6 +187,13 @@ struct _ThunarFile /* tells whether the file watch is not set */ gboolean no_file_watch; + + /* Number of files in this directory (only used if this #Thunarfile is a directory) */ + /* Note that this feature was added into #ThunarFile on purpose, because having inside #ThunarFolder caused lag when + * there were > 10.000 files in a folder (Creation of #ThunarFolder seems to be slow) */ + guint file_count; + guint64 file_count_timestamp; + }; typedef struct @@ -382,6 +390,8 @@ thunar_file_class_init (ThunarFileClass *klass) static void thunar_file_init (ThunarFile *file) { + file->file_count = 0; + file->file_count_timestamp = 0; } @@ -3485,6 +3495,92 @@ thunar_file_can_be_trashed (const ThunarFile *file) +/** + * thunar_file_get_file_count + * @file : a #ThunarFile instance. + * @callback: a #GCallback to be executed after the file count + * @data : a #gpointer containing user data to pass to the callback + * + * Returns the number of items in the directory + * Counts the number of files in the directory as fast as possible. + * Will use cached data to do calculations only when the file has + * been modified since the last time its contents were counted. + * + * Return value: Number of files in a folder + **/ +guint +thunar_file_get_file_count (ThunarFile *file, + GCallback callback, + gpointer data) +{ + GError *err = NULL; + ThunarJob *job; + GFileInfo *info; + guint64 last_modified; + + _thunar_return_val_if_fail (thunar_file_is_directory (file), 0); + + /* Forcefully get cached values by passing NULL as the second argument */ + if (file == NULL) + return file->file_count; + + info = g_file_query_info (thunar_file_get_file (file), + G_FILE_ATTRIBUTE_TIME_MODIFIED, + G_FILE_QUERY_INFO_NONE, + NULL, + &err); + + if (err != NULL) + { + g_warning ("An error occured while trying to get file counts."); + return file->file_count; + } + + last_modified = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED); + g_object_unref (info); + + /* return a cached value if last time that the file count was computed is later + * than the last time the file was modified */ + if (G_LIKELY (last_modified < file->file_count_timestamp)) + return file->file_count; + + /* put the timestamp calculation at the *start* of the process to prevent another call to + * thunar_file_get_count starting another job on the same folder before one has ended. + * Divide by 1e6 to convert from microseconds to seconds */ + file->file_count_timestamp = g_get_real_time () / (guint64) 1e6; + + /* set up a job to actually enumerate over the folder's contents and get its file count */ + job = thunar_io_jobs_count_files (file); + + /* set up the signal on finish to call the callback */ + if (callback != NULL) + g_signal_connect (job, "finished", G_CALLBACK (callback), data); + + exo_job_launch (EXO_JOB (job)); + + return file->file_count; +} + + + +/** + * thunar_file_set_file_count + * @file: A #ThunarFileInstance + * @count: The value to set the file's count to + * + * Set @file's count to the given number if it is a directory. + * Does *NOT* update the file's count timestamp. + **/ +void +thunar_file_set_file_count (ThunarFile *file, + const guint count) +{ + _thunar_return_if_fail (thunar_file_is_directory (file)); + + file->file_count = count; +} + + /** * thunar_file_get_emblem_names: * @file : a #ThunarFile instance. diff --git a/thunar/thunar-file.h b/thunar/thunar-file.h index 7e163b16a..8b9a3e405 100644 --- a/thunar/thunar-file.h +++ b/thunar/thunar-file.h @@ -235,6 +235,13 @@ gboolean thunar_file_is_chmodable (const ThunarFile gboolean thunar_file_is_renameable (const ThunarFile *file); gboolean thunar_file_can_be_trashed (const ThunarFile *file); + +guint thunar_file_get_file_count (ThunarFile *file, + GCallback callback, + gpointer data); +void thunar_file_set_file_count (ThunarFile *file, + const guint count); + GList *thunar_file_get_emblem_names (ThunarFile *file); void thunar_file_set_emblem_names (ThunarFile *file, GList *emblem_names); diff --git a/thunar/thunar-io-jobs.c b/thunar/thunar-io-jobs.c index 7d28833da..75a06f1d6 100644 --- a/thunar/thunar-io-jobs.c +++ b/thunar/thunar-io-jobs.c @@ -31,10 +31,11 @@ #include <thunar/thunar-application.h> #include <thunar/thunar-enum-types.h> +#include <thunar/thunar-file.h> #include <thunar/thunar-gio-extensions.h> -#include <thunar/thunar-io-scan-directory.h> -#include <thunar/thunar-io-jobs.h> #include <thunar/thunar-io-jobs-util.h> +#include <thunar/thunar-io-jobs.h> +#include <thunar/thunar-io-scan-directory.h> #include <thunar/thunar-job.h> #include <thunar/thunar-private.h> #include <thunar/thunar-simple-job.h> @@ -1451,3 +1452,68 @@ thunar_io_jobs_rename_file (ThunarFile *file, G_TYPE_STRING, display_name, THUNAR_TYPE_OPERATION_LOG_MODE, log_mode); } + + + +static gboolean +_thunar_io_jobs_count (ThunarJob *job, + GArray *param_values, + GError **error) +{ + GError *err = NULL; + ThunarFile *file; + GFileEnumerator *enumerator; + guint count; + + _thunar_return_val_if_fail (THUNAR_IS_JOB (job), FALSE); + _thunar_return_val_if_fail (param_values != NULL, FALSE); + _thunar_return_val_if_fail (param_values->len == 1, FALSE); + _thunar_return_val_if_fail (G_VALUE_HOLDS (&g_array_index (param_values, GValue, 0), THUNAR_TYPE_FILE), FALSE); + _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + file = THUNAR_FILE (g_value_get_object (&g_array_index (param_values, GValue, 0))); + + if (file == NULL) + return FALSE; + + enumerator = g_file_enumerate_children (thunar_file_get_file (file), NULL, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, &err); + if (err != NULL) + { + g_propagate_error (error, err); + return FALSE; + } + + count = 0; + for (GFileInfo *child_info = g_file_enumerator_next_file (enumerator, NULL, &err); + child_info != NULL; + child_info = g_file_enumerator_next_file (enumerator, NULL, &err)) + { + if (err != NULL) + { + g_propagate_error (error, err); + return FALSE; + } + + count++; + g_object_unref (child_info); + } + + thunar_file_set_file_count (file, count); + + g_object_unref (enumerator); + + return TRUE; +} + + + +ThunarJob * +thunar_io_jobs_count_files (ThunarFile *file) +{ + _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); + + return thunar_simple_job_new (_thunar_io_jobs_count, 1, + THUNAR_TYPE_FILE, file); +} diff --git a/thunar/thunar-io-jobs.h b/thunar/thunar-io-jobs.h index da7955d8d..a377b8c61 100644 --- a/thunar/thunar-io-jobs.h +++ b/thunar/thunar-io-jobs.h @@ -26,32 +26,33 @@ G_BEGIN_DECLS -ThunarJob *thunar_io_jobs_create_files (GList *file_list, - GFile *template_file) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; -ThunarJob *thunar_io_jobs_make_directories (GList *file_list) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; -ThunarJob *thunar_io_jobs_unlink_files (GList *file_list) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; -ThunarJob *thunar_io_jobs_move_files (GList *source_file_list, - GList *target_file_list) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; -ThunarJob *thunar_io_jobs_copy_files (GList *source_file_list, - GList *target_file_list) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; -ThunarJob *thunar_io_jobs_link_files (GList *source_file_list, - GList *target_file_list) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; -ThunarJob *thunar_io_jobs_trash_files (GList *file_list) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; -ThunarJob *thunar_io_jobs_restore_files (GList *source_file_list, - GList *target_file_list) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; -ThunarJob *thunar_io_jobs_change_group (GList *files, - guint32 gid, - gboolean recursive) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; -ThunarJob *thunar_io_jobs_change_mode (GList *files, - ThunarFileMode dir_mask, - ThunarFileMode dir_mode, - ThunarFileMode file_mask, - ThunarFileMode file_mode, - gboolean recursive) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; -ThunarJob *thunar_io_jobs_list_directory (GFile *directory) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; +ThunarJob *thunar_io_jobs_create_files (GList *file_list, + GFile *template_file) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; +ThunarJob *thunar_io_jobs_make_directories (GList *file_list) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; +ThunarJob *thunar_io_jobs_unlink_files (GList *file_list) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; +ThunarJob *thunar_io_jobs_move_files (GList *source_file_list, + GList *target_file_list) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; +ThunarJob *thunar_io_jobs_copy_files (GList *source_file_list, + GList *target_file_list) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; +ThunarJob *thunar_io_jobs_link_files (GList *source_file_list, + GList *target_file_list) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; +ThunarJob *thunar_io_jobs_trash_files (GList *file_list) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; +ThunarJob *thunar_io_jobs_restore_files (GList *source_file_list, + GList *target_file_list) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; +ThunarJob *thunar_io_jobs_change_group (GList *files, + guint32 gid, + gboolean recursive) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; +ThunarJob *thunar_io_jobs_change_mode (GList *files, + ThunarFileMode dir_mask, + ThunarFileMode dir_mode, + ThunarFileMode file_mask, + ThunarFileMode file_mode, + gboolean recursive) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; +ThunarJob *thunar_io_jobs_list_directory (GFile *directory) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; ThunarJob *thunar_io_jobs_rename_file (ThunarFile *file, const gchar *display_name, ThunarOperationLogMode log_mode) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; +ThunarJob *thunar_io_jobs_count_files (ThunarFile *file); G_END_DECLS diff --git a/thunar/thunar-list-model.c b/thunar/thunar-list-model.c index 75aa77f1a..7c069f38e 100644 --- a/thunar/thunar-list-model.c +++ b/thunar/thunar-list-model.c @@ -33,6 +33,7 @@ #include <libxfce4util/libxfce4util.h> #include <thunar/thunar-application.h> +#include <thunar/thunar-file.h> #include <thunar/thunar-file-monitor.h> #include <thunar/thunar-gobject-extensions.h> #include <thunar/thunar-list-model.h> @@ -55,6 +56,7 @@ enum PROP_FOLDERS_FIRST, PROP_NUM_FILES, PROP_SHOW_HIDDEN, + PROP_FOLDER_ITEM_COUNT, PROP_FILE_SIZE_BINARY, N_PROPERTIES }; @@ -73,155 +75,163 @@ typedef gint (*ThunarSortFunc) (const ThunarFile *a, const ThunarFile *b, gboolean case_sensitive); -static void thunar_list_model_tree_model_init (GtkTreeModelIface *iface); -static void thunar_list_model_drag_dest_init (GtkTreeDragDestIface *iface); -static void thunar_list_model_sortable_init (GtkTreeSortableIface *iface); -static void thunar_list_model_dispose (GObject *object); -static void thunar_list_model_finalize (GObject *object); -static void thunar_list_model_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static void thunar_list_model_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static GtkTreeModelFlags thunar_list_model_get_flags (GtkTreeModel *model); -static gint thunar_list_model_get_n_columns (GtkTreeModel *model); -static GType thunar_list_model_get_column_type (GtkTreeModel *model, - gint idx); -static gboolean thunar_list_model_get_iter (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreePath *path); -static GtkTreePath *thunar_list_model_get_path (GtkTreeModel *model, - GtkTreeIter *iter); -static void thunar_list_model_get_value (GtkTreeModel *model, - GtkTreeIter *iter, - gint column, - GValue *value); -static gboolean thunar_list_model_iter_next (GtkTreeModel *model, - GtkTreeIter *iter); -static gboolean thunar_list_model_iter_children (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeIter *parent); -static gboolean thunar_list_model_iter_has_child (GtkTreeModel *model, - GtkTreeIter *iter); -static gint thunar_list_model_iter_n_children (GtkTreeModel *model, - GtkTreeIter *iter); -static gboolean thunar_list_model_iter_nth_child (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n); -static gboolean thunar_list_model_iter_parent (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeIter *child); -static gboolean thunar_list_model_drag_data_received (GtkTreeDragDest *dest, - GtkTreePath *path, - GtkSelectionData *data); -static gboolean thunar_list_model_row_drop_possible (GtkTreeDragDest *dest, - GtkTreePath *path, - GtkSelectionData *data); -static gboolean thunar_list_model_get_sort_column_id (GtkTreeSortable *sortable, - gint *sort_column_id, - GtkSortType *order); -static void thunar_list_model_set_sort_column_id (GtkTreeSortable *sortable, - gint sort_column_id, - GtkSortType order); -static void thunar_list_model_set_default_sort_func (GtkTreeSortable *sortable, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy); -static void thunar_list_model_set_sort_func (GtkTreeSortable *sortable, - gint sort_column_id, - GtkTreeIterCompareFunc func, - gpointer data, - GDestroyNotify destroy); -static gboolean thunar_list_model_has_default_sort_func (GtkTreeSortable *sortable); -static gint thunar_list_model_cmp_func (gconstpointer a, - gconstpointer b, - gpointer user_data); -static void thunar_list_model_sort (ThunarListModel *store); -static void thunar_list_model_file_changed (ThunarFileMonitor *file_monitor, - ThunarFile *file, - ThunarListModel *store); -static void thunar_list_model_folder_destroy (ThunarFolder *folder, - ThunarListModel *store); -static void thunar_list_model_folder_error (ThunarFolder *folder, - const GError *error, - ThunarListModel *store); -static void thunar_list_model_files_added (ThunarFolder *folder, - GList *files, - ThunarListModel *store); -static void thunar_list_model_files_removed (ThunarFolder *folder, - GList *files, - ThunarListModel *store); -static gint sort_by_date (const ThunarFile *a, - const ThunarFile *b, - gboolean case_sensitive, - gint type); -static gint sort_by_date_created (const ThunarFile *a, - const ThunarFile *b, - gboolean case_sensitive); -static gint sort_by_date_accessed (const ThunarFile *a, - const ThunarFile *b, - gboolean case_sensitive); -static gint sort_by_date_modified (const ThunarFile *a, - const ThunarFile *b, - gboolean case_sensitive); -static gint sort_by_date_deleted (const ThunarFile *a, - const ThunarFile *b, - gboolean case_sensitive); -static gint sort_by_recency (const ThunarFile *a, - const ThunarFile *b, - gboolean case_sensitive); -static gint sort_by_location (const ThunarFile *a, - const ThunarFile *b, - gboolean case_sensitive); -static gint sort_by_group (const ThunarFile *a, - const ThunarFile *b, - gboolean case_sensitive); -static gint sort_by_mime_type (const ThunarFile *a, - const ThunarFile *b, - gboolean case_sensitive); -static gint sort_by_owner (const ThunarFile *a, - const ThunarFile *b, - gboolean case_sensitive); -static gint sort_by_permissions (const ThunarFile *a, - const ThunarFile *b, - gboolean case_sensitive); -static gint sort_by_size (const ThunarFile *a, - const ThunarFile *b, - gboolean case_sensitive); -static gint sort_by_size_in_bytes (const ThunarFile *a, - const ThunarFile *b, - gboolean case_sensitive); -static gint sort_by_type (const ThunarFile *a, - const ThunarFile *b, - gboolean case_sensitive); - -static gboolean thunar_list_model_get_case_sensitive (ThunarListModel *store); -static void thunar_list_model_set_case_sensitive (ThunarListModel *store, - gboolean case_sensitive); -static ThunarDateStyle thunar_list_model_get_date_style (ThunarListModel *store); -static void thunar_list_model_set_date_style (ThunarListModel *store, - ThunarDateStyle date_style); -static const char* thunar_list_model_get_date_custom_style (ThunarListModel *store); -static void thunar_list_model_set_date_custom_style (ThunarListModel *store, - const char *date_custom_style); -static gint thunar_list_model_get_num_files (ThunarListModel *store); -static gboolean thunar_list_model_get_folders_first (ThunarListModel *store); -static ThunarJob* thunar_list_model_job_search_directory (ThunarListModel *model, - const gchar *search_query_c, - ThunarFile *directory); -static void thunar_list_model_search_folder (ThunarListModel *model, - ThunarJob *job, - gchar *uri, - gchar **search_query_c_terms, - enum ThunarListModelSearch search_type, - gboolean show_hidden); -static void thunar_list_model_cancel_search_job (ThunarListModel *model); - - +static void thunar_list_model_tree_model_init (GtkTreeModelIface *iface); +static void thunar_list_model_drag_dest_init (GtkTreeDragDestIface *iface); +static void thunar_list_model_sortable_init (GtkTreeSortableIface *iface); +static void thunar_list_model_dispose (GObject *object); +static void thunar_list_model_finalize (GObject *object); +static void thunar_list_model_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void thunar_list_model_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static GtkTreeModelFlags thunar_list_model_get_flags (GtkTreeModel *model); +static gint thunar_list_model_get_n_columns (GtkTreeModel *model); +static GType thunar_list_model_get_column_type (GtkTreeModel *model, + gint idx); +static gboolean thunar_list_model_get_iter (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreePath *path); +static GtkTreePath *thunar_list_model_get_path (GtkTreeModel *model, + GtkTreeIter *iter); +static void thunar_list_model_get_value (GtkTreeModel *model, + GtkTreeIter *iter, + gint column, + GValue *value); +static gboolean thunar_list_model_iter_next (GtkTreeModel *model, + GtkTreeIter *iter); +static gboolean thunar_list_model_iter_children (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *parent); +static gboolean thunar_list_model_iter_has_child (GtkTreeModel *model, + GtkTreeIter *iter); +static gint thunar_list_model_iter_n_children (GtkTreeModel *model, + GtkTreeIter *iter); +static gboolean thunar_list_model_iter_nth_child (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n); +static gboolean thunar_list_model_iter_parent (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *child); +static gboolean thunar_list_model_drag_data_received (GtkTreeDragDest *dest, + GtkTreePath *path, + GtkSelectionData *data); +static gboolean thunar_list_model_row_drop_possible (GtkTreeDragDest *dest, + GtkTreePath *path, + GtkSelectionData *data); +static gboolean thunar_list_model_get_sort_column_id (GtkTreeSortable *sortable, + gint *sort_column_id, + GtkSortType *order); +static void thunar_list_model_set_sort_column_id (GtkTreeSortable *sortable, + gint sort_column_id, + GtkSortType order); +static void thunar_list_model_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); +static void thunar_list_model_set_sort_func (GtkTreeSortable *sortable, + gint sort_column_id, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy); +static gboolean thunar_list_model_has_default_sort_func (GtkTreeSortable *sortable); +static gint thunar_list_model_cmp_func (gconstpointer a, + gconstpointer b, + gpointer user_data); +static void thunar_list_model_sort (ThunarListModel *store); +static void thunar_list_model_file_changed (ThunarFileMonitor *file_monitor, + ThunarFile *file, + ThunarListModel *store); +static void thunar_list_model_folder_destroy (ThunarFolder *folder, + ThunarListModel *store); +static void thunar_list_model_folder_error (ThunarFolder *folder, + const GError *error, + ThunarListModel *store); +static void thunar_list_model_files_added (ThunarFolder *folder, + GList *files, + ThunarListModel *store); +static void thunar_list_model_files_removed (ThunarFolder *folder, + GList *files, + ThunarListModel *store); +static gint sort_by_date (const ThunarFile *a, + const ThunarFile *b, + gboolean case_sensitive, + gint type); +static gint sort_by_date_created (const ThunarFile *a, + const ThunarFile *b, + gboolean case_sensitive); +static gint sort_by_date_accessed (const ThunarFile *a, + const ThunarFile *b, + gboolean case_sensitive); +static gint sort_by_date_modified (const ThunarFile *a, + const ThunarFile *b, + gboolean case_sensitive); +static gint sort_by_date_deleted (const ThunarFile *a, + const ThunarFile *b, + gboolean case_sensitive); +static gint sort_by_recency (const ThunarFile *a, + const ThunarFile *b, + gboolean case_sensitive); +static gint sort_by_location (const ThunarFile *a, + const ThunarFile *b, + gboolean case_sensitive); +static gint sort_by_group (const ThunarFile *a, + const ThunarFile *b, + gboolean case_sensitive); +static gint sort_by_mime_type (const ThunarFile *a, + const ThunarFile *b, + gboolean case_sensitive); +static gint sort_by_owner (const ThunarFile *a, + const ThunarFile *b, + gboolean case_sensitive); +static gint sort_by_permissions (const ThunarFile *a, + const ThunarFile *b, + gboolean case_sensitive); +static gint sort_by_size (const ThunarFile *a, + const ThunarFile *b, + gboolean case_sensitive); +static gint sort_by_size_in_bytes (const ThunarFile *a, + const ThunarFile *b, + gboolean case_sensitive); +static gint sort_by_size_and_items_count (ThunarFile *a, + ThunarFile *b, + gboolean case_sensitive); +static gint sort_by_type (const ThunarFile *a, + const ThunarFile *b, + gboolean case_sensitive); + +static gboolean thunar_list_model_get_case_sensitive (ThunarListModel *store); +static void thunar_list_model_set_case_sensitive (ThunarListModel *store, + gboolean case_sensitive); +static ThunarDateStyle thunar_list_model_get_date_style (ThunarListModel *store); +static void thunar_list_model_set_date_style (ThunarListModel *store, + ThunarDateStyle date_style); +static const char* thunar_list_model_get_date_custom_style (ThunarListModel *store); +static void thunar_list_model_set_date_custom_style (ThunarListModel *store, + const char *date_custom_style); +static gint thunar_list_model_get_num_files (ThunarListModel *store); +static gboolean thunar_list_model_get_folders_first (ThunarListModel *store); +static ThunarJob* thunar_list_model_job_search_directory (ThunarListModel *model, + const gchar *search_query_c, + ThunarFile *directory); +static void thunar_list_model_search_folder (ThunarListModel *model, + ThunarJob *job, + gchar *uri, + gchar **search_query_c_terms, + enum ThunarListModelSearch search_type, + gboolean show_hidden); +static void thunar_list_model_cancel_search_job (ThunarListModel *model); + +static gint thunar_list_model_get_folder_item_count (ThunarListModel *store); +static void thunar_list_model_set_folder_item_count (ThunarListModel *store, + ThunarFolderItemCount count_as_dir_size); + +static void thunar_list_model_file_count_callback (ExoJob *job, + gpointer model); struct _ThunarListModelClass { @@ -245,13 +255,14 @@ struct _ThunarListModel gint stamp; #endif - GSequence *rows; - GSList *hidden; - ThunarFolder *folder; - gboolean show_hidden : 1; - gboolean file_size_binary : 1; - ThunarDateStyle date_style; - char *date_custom_style; + GSequence *rows; + GSList *hidden; + ThunarFolder *folder; + gboolean show_hidden : 1; + ThunarFolderItemCount folder_item_count; + gboolean file_size_binary : 1; + ThunarDateStyle date_style; + char *date_custom_style; /* Use the shared ThunarFileMonitor instance, so we * do not need to connect "changed" handler to every @@ -405,6 +416,19 @@ thunar_list_model_class_init (ThunarListModelClass *klass) TRUE, EXO_PARAM_READWRITE); + /** + * ThunarListModel:folder-item-count: + * + * Tells when the size column of folders should show the number of containing files + **/ + list_model_props[PROP_FOLDER_ITEM_COUNT] = + g_param_spec_enum ("folder-item-count", + "folder-item-count", + "folder-item-count", + THUNAR_TYPE_FOLDER_ITEM_COUNT, + TRUE, + EXO_PARAM_READWRITE); + /* install properties */ g_object_class_install_properties (gobject_class, N_PROPERTIES, list_model_props); @@ -594,6 +618,10 @@ thunar_list_model_get_property (GObject *object, g_value_set_boolean (value, thunar_list_model_get_file_size_binary (store)); break; + case PROP_FOLDER_ITEM_COUNT: + g_value_set_enum (value, thunar_list_model_get_folder_item_count (store)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -640,6 +668,10 @@ thunar_list_model_set_property (GObject *object, thunar_list_model_set_file_size_binary (store, g_value_get_boolean (value)); break; + case PROP_FOLDER_ITEM_COUNT: + thunar_list_model_set_folder_item_count (store, g_value_get_enum (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -783,9 +815,10 @@ thunar_list_model_get_value (GtkTreeModel *model, ThunarUser *user; ThunarFile *file; ThunarFolder *folder; + gchar *str; + guint32 item_count; GFile *g_file; GFile *g_file_parent; - gchar *str; _thunar_return_if_fail (THUNAR_IS_LIST_MODEL (model)); _thunar_return_if_fail (iter->stamp == (THUNAR_LIST_MODEL (model))->stamp); @@ -930,6 +963,7 @@ thunar_list_model_get_value (GtkTreeModel *model, case THUNAR_COLUMN_SIZE: g_value_init (value, G_TYPE_STRING); + if (thunar_file_is_mountable (file)) { g_file = thunar_file_get_target_location (file); @@ -939,8 +973,38 @@ thunar_list_model_get_value (GtkTreeModel *model, g_object_unref (g_file); break; } - if (!thunar_file_is_directory (file)) - g_value_take_string (value, thunar_file_get_size_string_formatted (file, THUNAR_LIST_MODEL (model)->file_size_binary)); + else if (thunar_file_is_directory (file)) + { + /* If the option is set to never show folder sizes as item counts, then just give the folder's binary size */ + if (THUNAR_LIST_MODEL (model)->folder_item_count == THUNAR_FOLDER_ITEM_COUNT_NEVER) + g_value_take_string (value, thunar_file_get_size_string_formatted (file, THUNAR_LIST_MODEL (model)->file_size_binary)); + + /* If the option is set to always show folder sizes as item counts, then give the folder's item count */ + else if (THUNAR_LIST_MODEL (model)->folder_item_count == THUNAR_FOLDER_ITEM_COUNT_ALWAYS) + { + item_count = thunar_file_get_file_count (file, G_CALLBACK (thunar_list_model_file_count_callback), model); + g_value_take_string (value, g_strdup_printf (ngettext ("%u item", "%u items", item_count), item_count)); + } + + /* If the option is set to always show folder sizes as item counts only for local files, + * check if the files is local or not, and act accordingly */ + else if (THUNAR_LIST_MODEL (model)->folder_item_count == THUNAR_FOLDER_ITEM_COUNT_ONLY_LOCAL) + { + if (thunar_file_is_local (file)) + { + item_count = thunar_file_get_file_count (file, G_CALLBACK (thunar_list_model_file_count_callback), model); + g_value_take_string (value, g_strdup_printf (ngettext ("%u item", "%u items", item_count), item_count)); + } + else + g_value_take_string (value, thunar_file_get_size_string_formatted (file, THUNAR_LIST_MODEL (model)->file_size_binary)); + } + else + g_warning ("Error, unknown enum value for folder_item_count in the list model"); + } + else + { + g_value_take_string (value, thunar_file_get_size_string_formatted (file, THUNAR_LIST_MODEL (model)->file_size_binary)); + } break; case THUNAR_COLUMN_SIZE_IN_BYTES: @@ -1104,7 +1168,7 @@ thunar_list_model_get_sort_column_id (GtkTreeSortable *sortable, *sort_column_id = THUNAR_COLUMN_NAME; else if (store->sort_func == sort_by_permissions) *sort_column_id = THUNAR_COLUMN_PERMISSIONS; - else if (store->sort_func == sort_by_size) + else if (store->sort_func == sort_by_size || store->sort_func == (ThunarSortFunc) sort_by_size_and_items_count) *sort_column_id = THUNAR_COLUMN_SIZE; else if (store->sort_func == sort_by_size_in_bytes) *sort_column_id = THUNAR_COLUMN_SIZE_IN_BYTES; @@ -1199,7 +1263,7 @@ thunar_list_model_set_sort_column_id (GtkTreeSortable *sortable, break; case THUNAR_COLUMN_SIZE: - store->sort_func = sort_by_size; + store->sort_func = (store->folder_item_count != THUNAR_FOLDER_ITEM_COUNT_NEVER) ? (ThunarSortFunc) sort_by_size_and_items_count : sort_by_size; break; case THUNAR_COLUMN_SIZE_IN_BYTES: @@ -1355,7 +1419,7 @@ thunar_list_model_file_changed (ThunarFileMonitor *file_monitor, GtkTreePath *path; GtkTreeIter iter; - _thunar_return_if_fail (THUNAR_IS_FILE_MONITOR (file_monitor)); + _thunar_return_if_fail (THUNAR_IS_FILE_MONITOR (file_monitor) || file_monitor == NULL); _thunar_return_if_fail (THUNAR_IS_LIST_MODEL (store)); _thunar_return_if_fail (THUNAR_IS_FILE (file)); @@ -1858,6 +1922,32 @@ sort_by_size_in_bytes (const ThunarFile *a, +static gint +sort_by_size_and_items_count (ThunarFile *a, + ThunarFile *b, + gboolean case_sensitive) +{ + guint32 count_a; + guint32 count_b; + + if (thunar_file_is_directory (a) && thunar_file_is_directory (b)) + { + count_a = thunar_file_get_file_count (a, NULL, NULL); + count_b = thunar_file_get_file_count (b, NULL, NULL); + + if (count_a < count_b) + return -1; + else if (count_a > count_b) + return 1; + else + return thunar_file_compare_by_name (a, b, case_sensitive); + } + + return sort_by_size(a, b, case_sensitive); +} + + + static gint sort_by_type (const ThunarFile *a, const ThunarFile *b, @@ -2691,6 +2781,51 @@ thunar_list_model_set_file_size_binary (ThunarListModel *store, +/** + * thunar_list_model_get_folder_item_count: + * @store : a #ThunarListModel. + * + * Return value: A value of the enum #ThunarFolderItemCount + **/ +static gint +thunar_list_model_get_folder_item_count (ThunarListModel *store) +{ + _thunar_return_val_if_fail (THUNAR_IS_LIST_MODEL (store), FALSE); + return store->folder_item_count; +} + + + +/** + * thunar_list_model_set_folder_item_count: + * @store : a #ThunarListModel. + * @count_as_dir_size : a value of the enum #ThunarFolderItemCount + **/ +static void +thunar_list_model_set_folder_item_count (ThunarListModel *store, + ThunarFolderItemCount count_as_dir_size) +{ + _thunar_return_if_fail (THUNAR_IS_LIST_MODEL (store)); + + /* check if the new setting differs */ + if (store->folder_item_count == count_as_dir_size) + return; + + store->folder_item_count = count_as_dir_size; + g_object_notify_by_pspec (G_OBJECT (store), list_model_props[PROP_FOLDER_ITEM_COUNT]); + + gtk_tree_model_foreach (GTK_TREE_MODEL (store), (GtkTreeModelForeachFunc) (void (*)(void)) gtk_tree_model_row_changed, NULL); + + /* re-sorting the store if needed */ + if (store->sort_func == sort_by_size || store->sort_func == (ThunarSortFunc) sort_by_size_and_items_count) + { + store->sort_func = (store->folder_item_count != THUNAR_FOLDER_ITEM_COUNT_NEVER) ? (ThunarSortFunc) sort_by_size_and_items_count : sort_by_size; + thunar_list_model_sort (store); + } +} + + + /** * thunar_list_model_get_file: * @store : a #ThunarListModel. @@ -3156,3 +3291,21 @@ thunar_list_model_get_statusbar_text (ThunarListModel *store, g_object_unref (preferences); return text; } + + + +static void +thunar_list_model_file_count_callback (ExoJob *job, + gpointer model) +{ + GArray *param_values; + ThunarFile *file; + + param_values = thunar_simple_job_get_param_values (THUNAR_SIMPLE_JOB (job)); + file = THUNAR_FILE (g_value_get_object (&g_array_index (param_values, GValue, 0))); + + if (file == NULL) + return; + + thunar_list_model_file_changed (NULL, file, THUNAR_LIST_MODEL (model)); +} diff --git a/thunar/thunar-preferences-dialog.c b/thunar/thunar-preferences-dialog.c index 04ffa239e..05f527235 100644 --- a/thunar/thunar-preferences-dialog.c +++ b/thunar/thunar-preferences-dialog.c @@ -164,45 +164,6 @@ transform_view_index_to_string (GBinding *binding, -static gboolean -transform_enum_value_to_index (GBinding *binding, - const GValue *src_value, - GValue *dst_value, - gpointer user_data) -{ - GEnumClass *klass; - GType (*type_func)() = user_data; - guint n; - - klass = g_type_class_ref (type_func ()); - for (n = 0; n < klass->n_values; ++n) - if (klass->values[n].value == g_value_get_enum (src_value)) - g_value_set_int (dst_value, n); - g_type_class_unref (klass); - - return TRUE; -} - - - -static gboolean -transform_index_to_enum_value (GBinding *binding, - const GValue *src_value, - GValue *dst_value, - gpointer user_data) -{ - GEnumClass *klass; - GType (*type_func)() = user_data; - - klass = g_type_class_ref (type_func ()); - g_value_set_enum (dst_value, klass->values[g_value_get_int (src_value)].value); - g_type_class_unref (klass); - - return TRUE; -} - - - static gboolean transform_string_to_uint64 (GBinding *binding, const GValue *src_value, diff --git a/thunar/thunar-preferences.c b/thunar/thunar-preferences.c index 2a5d6d4b8..eb581a6c8 100644 --- a/thunar/thunar-preferences.c +++ b/thunar/thunar-preferences.c @@ -89,6 +89,7 @@ enum PROP_MISC_DATE_CUSTOM_STYLE, PROP_EXEC_SHELL_SCRIPTS_BY_DEFAULT, PROP_MISC_FOLDERS_FIRST, + PROP_MISC_FOLDER_ITEM_COUNT, PROP_MISC_FULL_PATH_IN_TAB_TITLE, PROP_MISC_FULL_PATH_IN_WINDOW_TITLE, PROP_MISC_HORIZONTAL_WHEEL_NAVIGATES, @@ -687,6 +688,19 @@ thunar_preferences_class_init (ThunarPreferencesClass *klass) TRUE, EXO_PARAM_READWRITE); + /** + * ThunarPreferences:misc-folder-item-count + * + * Tells when the size column of folders should show the number of containing files + **/ + preferences_props[PROP_MISC_FOLDER_ITEM_COUNT] = + g_param_spec_enum ("misc-folder-item-count", + "MiscFolderItemCount", + NULL, + THUNAR_TYPE_FOLDER_ITEM_COUNT, + THUNAR_FOLDER_ITEM_COUNT_NEVER, + EXO_PARAM_READWRITE); + /** * ThunarPreferences:misc-full-path-in-tab-title: * diff --git a/thunar/thunar-standard-view.c b/thunar/thunar-standard-view.c index 852dbaea4..d6ef4a2ba 100644 --- a/thunar/thunar-standard-view.c +++ b/thunar/thunar-standard-view.c @@ -831,6 +831,7 @@ thunar_standard_view_init (ThunarStandardView *standard_view) g_object_bind_property (G_OBJECT (standard_view->preferences), "misc-date-custom-style", G_OBJECT (standard_view->model), "date-custom-style", G_BINDING_SYNC_CREATE); g_object_bind_property (G_OBJECT (standard_view->preferences), "misc-folders-first", G_OBJECT (standard_view->model), "folders-first", G_BINDING_SYNC_CREATE); g_object_bind_property (G_OBJECT (standard_view->preferences), "misc-file-size-binary", G_OBJECT (standard_view->model), "file-size-binary", G_BINDING_SYNC_CREATE); + g_object_bind_property (G_OBJECT (standard_view->preferences), "misc-folder-item-count", G_OBJECT (standard_view->model), "folder-item-count", G_BINDING_SYNC_CREATE); /* setup the icon renderer */ standard_view->icon_renderer = thunar_icon_renderer_new (); -- GitLab