diff options
author | Matthias Clasen <mclasen@redhat.com> | 2022-01-12 02:58:25 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2022-01-12 02:58:25 +0000 |
commit | 21d40fc0386794a5c4991ce8f5eeba96cdf23884 (patch) | |
tree | a8697357e51a50f268ad6ee3dcd28f4a3172bb28 | |
parent | 93ff65c685c0f512270718a4990a01ef8872485e (diff) | |
parent | dcdcc659ef0b4640bd53e407f8bf880235c116b5 (diff) | |
download | gtk+-21d40fc0386794a5c4991ce8f5eeba96cdf23884.tar.gz |
Merge branch 'fix-directory-list' into 'main'
directorylist: Fix several issues
Closes #3784
See merge request GNOME/gtk!4367
-rw-r--r-- | gtk/gtkdirectorylist.c | 238 |
1 files changed, 180 insertions, 58 deletions
diff --git a/gtk/gtkdirectorylist.c b/gtk/gtkdirectorylist.c index a164eac8ef..3b8908aefd 100644 --- a/gtk/gtkdirectorylist.c +++ b/gtk/gtkdirectorylist.c @@ -65,6 +65,25 @@ enum { NUM_PROPERTIES }; +typedef struct _QueuedEvent QueuedEvent; +struct _QueuedEvent +{ + GtkDirectoryList *list; + GFile *file; + GFileInfo *info; + GFileMonitorEvent event; +}; + +static void +free_queued_event (gpointer data) +{ + QueuedEvent *event = data; + + g_clear_object (&event->file); + g_clear_object (&event->info); + g_free (event); +} + struct _GtkDirectoryList { GObject parent_instance; @@ -78,6 +97,7 @@ struct _GtkDirectoryList GCancellable *cancellable; GError *error; /* Error while loading */ GSequence *items; /* Use GPtrArray or GListStore here? */ + GQueue events; }; struct _GtkDirectoryListClass @@ -140,7 +160,6 @@ gtk_directory_list_set_property (GObject *object, case PROP_ATTRIBUTES: gtk_directory_list_set_attributes (self, g_value_get_string (value)); break; - case PROP_FILE: gtk_directory_list_set_file (self, g_value_get_object (value)); break; @@ -238,6 +257,9 @@ gtk_directory_list_dispose (GObject *object) g_clear_error (&self->error); g_clear_pointer (&self->items, g_sequence_free); + g_queue_foreach (&self->events, (GFunc) free_queued_event, NULL); + g_queue_clear (&self->events); + G_OBJECT_CLASS (gtk_directory_list_parent_class)->dispose (object); } @@ -331,6 +353,7 @@ gtk_directory_list_init (GtkDirectoryList *self) self->items = g_sequence_new (g_object_unref); self->io_priority = G_PRIORITY_DEFAULT; self->monitored = TRUE; + g_queue_init (&self->events); } /** @@ -519,78 +542,136 @@ gtk_directory_list_start_loading (GtkDirectoryList *self) g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]); } -static void -got_new_file_info_cb (GObject *source, - GAsyncResult *res, - gpointer data) +static GSequenceIter * +find_file (GSequence *sequence, + GFile *file) { - GFile *file = G_FILE (source); - GtkDirectoryList *self = GTK_DIRECTORY_LIST (data); - GFileInfo *info; - guint position; + GSequenceIter *iter; - info = g_file_query_info_finish (file, res, NULL); - if (!info) - return; + for (iter = g_sequence_get_begin_iter (sequence); + !g_sequence_iter_is_end (iter); + iter = g_sequence_iter_next (iter)) + { + GFileInfo *item = G_FILE_INFO (g_sequence_get (iter)); + GFile *f = G_FILE (g_file_info_get_attribute_object (item, "standard::file")); - g_file_info_set_attribute_object (info, "standard::file", G_OBJECT (file)); - position = g_sequence_get_length (self->items); - g_sequence_append (self->items, info); - g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1); + if (g_file_equal (f, file)) + return iter; + } + + return NULL; } -static void -got_existing_file_info_cb (GObject *source, - GAsyncResult *res, - gpointer data) +static gboolean +handle_event (QueuedEvent *event) { - GFile *file = G_FILE (source); - GtkDirectoryList *self = GTK_DIRECTORY_LIST (data); - GFileInfo *info; + GtkDirectoryList *self = event->list; + GFile *file = event->file; + GFileInfo *info = event->info; GSequenceIter *iter; + unsigned int position; - info = g_file_query_info_finish (file, res, NULL); - if (!info) - return; + switch ((int)event->event) + { + case G_FILE_MONITOR_EVENT_MOVED_IN: + case G_FILE_MONITOR_EVENT_CREATED: + if (!info) + return FALSE; - g_file_info_set_attribute_object (info, "standard::file", G_OBJECT (file)); + g_file_info_set_attribute_object (info, "standard::file", G_OBJECT (file)); - for (iter = g_sequence_get_begin_iter (self->items); - !g_sequence_iter_is_end (iter); - iter = g_sequence_iter_next (iter)) - { - GFileInfo *item = g_sequence_get (iter); - GFile *f = G_FILE (g_file_info_get_attribute_object (item, "standard::file")); - if (g_file_equal (f, file)) + iter = find_file (self->items, file); + if (iter) { - guint position = g_sequence_iter_get_position (iter); + position = g_sequence_iter_get_position (iter); g_sequence_set (iter, g_object_ref (info)); g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 1); - break; } + else + { + position = g_sequence_get_length (self->items); + g_sequence_append (self->items, g_object_ref (info)); + g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1); + } + break; + + case G_FILE_MONITOR_EVENT_MOVED_OUT: + case G_FILE_MONITOR_EVENT_DELETED: + iter = find_file (self->items, file); + if (iter) + { + position = g_sequence_iter_get_position (iter); + g_sequence_remove (iter); + g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0); + } + break; + + case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: + if (!info) + return FALSE; + + g_file_info_set_attribute_object (info, "standard::file", G_OBJECT (file)); + + iter = find_file (self->items, file); + if (iter) + { + position = g_sequence_iter_get_position (iter); + g_sequence_set (iter, g_object_ref (info)); + g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 1); + } + break; + + default: + g_assert_not_reached (); } + + return TRUE; } static void -gtk_directory_list_remove_file (GtkDirectoryList *self, - GFile *file) +handle_events (GtkDirectoryList *self) { - GSequenceIter *iter; + QueuedEvent *event; - for (iter = g_sequence_get_begin_iter (self->items); - !g_sequence_iter_is_end (iter); - iter = g_sequence_iter_next (iter)) + do { - GFileInfo *item = g_sequence_get (iter); - GFile *f = G_FILE (g_file_info_get_attribute_object (item, "standard::file")); - if (g_file_equal (f, file)) - { - guint position = g_sequence_iter_get_position (iter); - g_sequence_remove (iter); - g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0); - break; - } + event = g_queue_peek_tail (&self->events); + if (!event) + return; + + if (!handle_event (event)) + return; + + event = g_queue_pop_tail (&self->events); + free_queued_event (event); } + while (TRUE); +} + +static void +got_new_file_info_cb (GObject *source, + GAsyncResult *res, + gpointer data) +{ + QueuedEvent *event = data; + GtkDirectoryList *self = event->list; + GFile *file = event->file; + + event->info = g_file_query_info_finish (file, res, NULL); + handle_events (self); +} + +static void +got_existing_file_info_cb (GObject *source, + GAsyncResult *res, + gpointer data) +{ + QueuedEvent *event = data; + GtkDirectoryList *self = event->list; + GFile *file = event->file; + + event->info = g_file_query_info_finish (file, res, NULL); + handle_events (self); } static void @@ -601,30 +682,74 @@ directory_changed (GFileMonitor *monitor, gpointer data) { GtkDirectoryList *self = GTK_DIRECTORY_LIST (data); + QueuedEvent *ev; + switch (event) { + case G_FILE_MONITOR_EVENT_MOVED_IN: case G_FILE_MONITOR_EVENT_CREATED: + ev = g_new0 (QueuedEvent, 1); + ev->list = self; + ev->event = event; + ev->file = g_object_ref (file); + g_queue_push_head (&self->events, ev); + g_file_query_info_async (file, self->attributes, G_FILE_QUERY_INFO_NONE, self->io_priority, self->cancellable, got_new_file_info_cb, - self); + ev); break; + case G_FILE_MONITOR_EVENT_MOVED_OUT: case G_FILE_MONITOR_EVENT_DELETED: - gtk_directory_list_remove_file (self, file); + ev = g_new0 (QueuedEvent, 1); + ev->list = self; + ev->event = event; + ev->file = g_object_ref (file); + g_queue_push_head (&self->events, ev); + + handle_events (self); break; case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: + ev = g_new0 (QueuedEvent, 1); + ev->list = self; + ev->event = event; + ev->file = g_object_ref (file); + g_queue_push_head (&self->events, ev); + g_file_query_info_async (file, self->attributes, G_FILE_QUERY_INFO_NONE, self->io_priority, self->cancellable, got_existing_file_info_cb, - self); + ev); + break; + + case G_FILE_MONITOR_EVENT_RENAMED: + ev = g_new0 (QueuedEvent, 1); + ev->list = self; + ev->event = G_FILE_MONITOR_EVENT_DELETED; + ev->file = g_object_ref (file); + g_queue_push_head (&self->events, ev); + + ev = g_new0 (QueuedEvent, 1); + ev->list = self; + ev->event = G_FILE_MONITOR_EVENT_CREATED; + ev->file = g_object_ref (other_file); + g_queue_push_head (&self->events, ev); + + g_file_query_info_async (other_file, + self->attributes, + G_FILE_QUERY_INFO_NONE, + self->io_priority, + self->cancellable, + got_existing_file_info_cb, + ev); break; case G_FILE_MONITOR_EVENT_CHANGED: @@ -632,9 +757,6 @@ directory_changed (GFileMonitor *monitor, case G_FILE_MONITOR_EVENT_PRE_UNMOUNT: case G_FILE_MONITOR_EVENT_UNMOUNTED: case G_FILE_MONITOR_EVENT_MOVED: - case G_FILE_MONITOR_EVENT_RENAMED: - case G_FILE_MONITOR_EVENT_MOVED_IN: - case G_FILE_MONITOR_EVENT_MOVED_OUT: default: break; } @@ -644,7 +766,7 @@ static void gtk_directory_list_start_monitoring (GtkDirectoryList *self) { g_assert (self->monitor == NULL); - self->monitor = g_file_monitor_directory (self->file, G_FILE_MONITOR_NONE, NULL, NULL); + self->monitor = g_file_monitor_directory (self->file, G_FILE_MONITOR_WATCH_MOVES, NULL, NULL); g_signal_connect (self->monitor, "changed", G_CALLBACK (directory_changed), self); } |