summaryrefslogtreecommitdiff
path: root/libcaja-private/caja-file-changes-queue.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcaja-private/caja-file-changes-queue.c')
-rw-r--r--libcaja-private/caja-file-changes-queue.c475
1 files changed, 475 insertions, 0 deletions
diff --git a/libcaja-private/caja-file-changes-queue.c b/libcaja-private/caja-file-changes-queue.c
new file mode 100644
index 00000000..0be95302
--- /dev/null
+++ b/libcaja-private/caja-file-changes-queue.c
@@ -0,0 +1,475 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ Copyright (C) 1999, 2000, 2001 Eazel, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Pavel Cisler <[email protected]>
+*/
+
+#include <config.h>
+#include "caja-file-changes-queue.h"
+
+#include "caja-directory-notify.h"
+#include <eel/eel-glib-extensions.h>
+
+#ifdef G_THREADS_ENABLED
+#define MUTEX_LOCK(a) if ((a) != NULL) g_mutex_lock (a)
+#define MUTEX_UNLOCK(a) if ((a) != NULL) g_mutex_unlock (a)
+#else
+#define MUTEX_LOCK(a)
+#define MUTEX_UNLOCK(a)
+#endif
+
+typedef enum
+{
+ CHANGE_FILE_INITIAL,
+ CHANGE_FILE_ADDED,
+ CHANGE_FILE_CHANGED,
+ CHANGE_FILE_REMOVED,
+ CHANGE_FILE_MOVED,
+ CHANGE_POSITION_SET,
+ CHANGE_POSITION_REMOVE
+} CajaFileChangeKind;
+
+typedef struct
+{
+ CajaFileChangeKind kind;
+ GFile *from;
+ GFile *to;
+ GdkPoint point;
+ int screen;
+} CajaFileChange;
+
+typedef struct
+{
+ GList *head;
+ GList *tail;
+#ifdef G_THREADS_ENABLED
+ GMutex *mutex;
+#endif
+} CajaFileChangesQueue;
+
+static CajaFileChangesQueue *
+caja_file_changes_queue_new (void)
+{
+ CajaFileChangesQueue *result;
+
+ result = g_new0 (CajaFileChangesQueue, 1);
+
+#ifdef G_THREADS_ENABLED
+ result->mutex = g_mutex_new ();
+#endif
+ return result;
+}
+
+static CajaFileChangesQueue *
+caja_file_changes_queue_get (void)
+{
+ static CajaFileChangesQueue *file_changes_queue;
+
+ if (file_changes_queue == NULL)
+ {
+ file_changes_queue = caja_file_changes_queue_new ();
+ }
+
+ return file_changes_queue;
+}
+
+#if 0 /* no public free call yet */
+
+static void
+caja_file_change_free (CajaFileChange *change)
+{
+ if (change->from)
+ {
+ g_object_unref (change->from);
+ }
+ if (change->to)
+ {
+ g_object_unref (change->to);
+ }
+}
+
+void
+caja_file_changes_queue_free (CajaFileChangesQueue *queue)
+{
+ GList *p;
+ if (queue == NULL)
+ {
+ return;
+ }
+
+#ifdef G_THREADS_ENABLED
+ /* if lock on a defunct mutex were defined (returning a failure)
+ * we would lock here
+ */
+#endif
+
+ for (p = queue->head; p != NULL; p = p->next)
+ {
+ caja_file_change_free (p->data);
+ }
+ g_list_free (queue->head);
+
+#ifdef G_THREADS_ENABLED
+ g_mutex_free (queue->mutex);
+#endif
+ g_free (queue);
+}
+
+#endif /* no public free call yet */
+
+static void
+caja_file_changes_queue_add_common (CajaFileChangesQueue *queue,
+ CajaFileChange *new_item)
+{
+ /* enqueue the new queue item while locking down the list */
+ MUTEX_LOCK (queue->mutex);
+
+ queue->head = g_list_prepend (queue->head, new_item);
+ if (queue->tail == NULL)
+ queue->tail = queue->head;
+
+ MUTEX_UNLOCK (queue->mutex);
+}
+
+void
+caja_file_changes_queue_file_added (GFile *location)
+{
+ CajaFileChange *new_item;
+ CajaFileChangesQueue *queue;
+
+ queue = caja_file_changes_queue_get();
+
+ new_item = g_new0 (CajaFileChange, 1);
+ new_item->kind = CHANGE_FILE_ADDED;
+ new_item->from = g_object_ref (location);
+ caja_file_changes_queue_add_common (queue, new_item);
+}
+
+void
+caja_file_changes_queue_file_changed (GFile *location)
+{
+ CajaFileChange *new_item;
+ CajaFileChangesQueue *queue;
+
+ queue = caja_file_changes_queue_get();
+
+ new_item = g_new0 (CajaFileChange, 1);
+ new_item->kind = CHANGE_FILE_CHANGED;
+ new_item->from = g_object_ref (location);
+ caja_file_changes_queue_add_common (queue, new_item);
+}
+
+void
+caja_file_changes_queue_file_removed (GFile *location)
+{
+ CajaFileChange *new_item;
+ CajaFileChangesQueue *queue;
+
+ queue = caja_file_changes_queue_get();
+
+ new_item = g_new0 (CajaFileChange, 1);
+ new_item->kind = CHANGE_FILE_REMOVED;
+ new_item->from = g_object_ref (location);
+ caja_file_changes_queue_add_common (queue, new_item);
+}
+
+void
+caja_file_changes_queue_file_moved (GFile *from,
+ GFile *to)
+{
+ CajaFileChange *new_item;
+ CajaFileChangesQueue *queue;
+
+ queue = caja_file_changes_queue_get ();
+
+ new_item = g_new (CajaFileChange, 1);
+ new_item->kind = CHANGE_FILE_MOVED;
+ new_item->from = g_object_ref (from);
+ new_item->to = g_object_ref (to);
+ caja_file_changes_queue_add_common (queue, new_item);
+}
+
+void
+caja_file_changes_queue_schedule_position_set (GFile *location,
+ GdkPoint point,
+ int screen)
+{
+ CajaFileChange *new_item;
+ CajaFileChangesQueue *queue;
+
+ queue = caja_file_changes_queue_get ();
+
+ new_item = g_new (CajaFileChange, 1);
+ new_item->kind = CHANGE_POSITION_SET;
+ new_item->from = g_object_ref (location);
+ new_item->point = point;
+ new_item->screen = screen;
+ caja_file_changes_queue_add_common (queue, new_item);
+}
+
+void
+caja_file_changes_queue_schedule_position_remove (GFile *location)
+{
+ CajaFileChange *new_item;
+ CajaFileChangesQueue *queue;
+
+ queue = caja_file_changes_queue_get ();
+
+ new_item = g_new (CajaFileChange, 1);
+ new_item->kind = CHANGE_POSITION_REMOVE;
+ new_item->from = g_object_ref (location);
+ caja_file_changes_queue_add_common (queue, new_item);
+}
+
+static CajaFileChange *
+caja_file_changes_queue_get_change (CajaFileChangesQueue *queue)
+{
+ GList *new_tail;
+ CajaFileChange *result;
+
+ g_assert (queue != NULL);
+
+ /* dequeue the tail item while locking down the list */
+ MUTEX_LOCK (queue->mutex);
+
+ if (queue->tail == NULL)
+ {
+ result = NULL;
+ }
+ else
+ {
+ new_tail = queue->tail->prev;
+ result = queue->tail->data;
+ queue->head = g_list_remove_link (queue->head,
+ queue->tail);
+ g_list_free_1 (queue->tail);
+ queue->tail = new_tail;
+ }
+
+ MUTEX_UNLOCK (queue->mutex);
+
+ return result;
+}
+
+enum
+{
+ CONSUME_CHANGES_MAX_CHUNK = 20
+};
+
+static void
+pairs_list_free (GList *pairs)
+{
+ GList *p;
+ GFilePair *pair;
+
+ /* deep delete the list of pairs */
+
+ for (p = pairs; p != NULL; p = p->next)
+ {
+ /* delete the strings in each pair */
+ pair = p->data;
+ g_object_unref (pair->from);
+ g_object_unref (pair->to);
+ }
+
+ /* delete the list and the now empty pair structs */
+ eel_g_list_free_deep (pairs);
+}
+
+static void
+position_set_list_free (GList *list)
+{
+ GList *p;
+ CajaFileChangesQueuePosition *item;
+
+ for (p = list; p != NULL; p = p->next)
+ {
+ item = p->data;
+ g_object_unref (item->location);
+ }
+ /* delete the list and the now empty structs */
+ eel_g_list_free_deep (list);
+}
+
+/* go through changes in the change queue, send ones with the same kind
+ * in a list to the different caja_directory_notify calls
+ */
+void
+caja_file_changes_consume_changes (gboolean consume_all)
+{
+ CajaFileChange *change;
+ GList *additions, *changes, *deletions, *moves;
+ GList *position_set_requests;
+ GFilePair *pair;
+ CajaFileChangesQueuePosition *position_set;
+ guint chunk_count;
+ CajaFileChangesQueue *queue;
+ gboolean flush_needed;
+
+
+ additions = NULL;
+ changes = NULL;
+ deletions = NULL;
+ moves = NULL;
+ position_set_requests = NULL;
+
+ queue = caja_file_changes_queue_get();
+
+ /* Consume changes from the queue, stuffing them into one of three lists,
+ * keep doing it while the changes are of the same kind, then send them off.
+ * This is to ensure that the changes get sent off in the same order that they
+ * arrived.
+ */
+ for (chunk_count = 0; ; chunk_count++)
+ {
+ change = caja_file_changes_queue_get_change (queue);
+
+ /* figure out if we need to flush the pending changes that we collected sofar */
+
+ if (change == NULL)
+ {
+ flush_needed = TRUE;
+ /* no changes left, flush everything */
+ }
+ else
+ {
+ flush_needed = additions != NULL
+ && change->kind != CHANGE_FILE_ADDED
+ && change->kind != CHANGE_POSITION_SET
+ && change->kind != CHANGE_POSITION_REMOVE;
+
+ flush_needed |= changes != NULL
+ && change->kind != CHANGE_FILE_CHANGED;
+
+ flush_needed |= moves != NULL
+ && change->kind != CHANGE_FILE_MOVED
+ && change->kind != CHANGE_POSITION_SET
+ && change->kind != CHANGE_POSITION_REMOVE;
+
+ flush_needed |= deletions != NULL
+ && change->kind != CHANGE_FILE_REMOVED;
+
+ flush_needed |= position_set_requests != NULL
+ && change->kind != CHANGE_POSITION_SET
+ && change->kind != CHANGE_POSITION_REMOVE
+ && change->kind != CHANGE_FILE_ADDED
+ && change->kind != CHANGE_FILE_MOVED;
+
+ flush_needed |= !consume_all && chunk_count >= CONSUME_CHANGES_MAX_CHUNK;
+ /* we have reached the chunk maximum */
+ }
+
+ if (flush_needed)
+ {
+ /* Send changes we collected off.
+ * At one time we may only have one of the lists
+ * contain changes.
+ */
+
+ if (deletions != NULL)
+ {
+ deletions = g_list_reverse (deletions);
+ caja_directory_notify_files_removed (deletions);
+ eel_g_object_list_free (deletions);
+ deletions = NULL;
+ }
+ if (moves != NULL)
+ {
+ moves = g_list_reverse (moves);
+ caja_directory_notify_files_moved (moves);
+ pairs_list_free (moves);
+ moves = NULL;
+ }
+ if (additions != NULL)
+ {
+ additions = g_list_reverse (additions);
+ caja_directory_notify_files_added (additions);
+ eel_g_object_list_free (additions);
+ additions = NULL;
+ }
+ if (changes != NULL)
+ {
+ changes = g_list_reverse (changes);
+ caja_directory_notify_files_changed (changes);
+ eel_g_object_list_free (changes);
+ changes = NULL;
+ }
+ if (position_set_requests != NULL)
+ {
+ position_set_requests = g_list_reverse (position_set_requests);
+ caja_directory_schedule_position_set (position_set_requests);
+ position_set_list_free (position_set_requests);
+ position_set_requests = NULL;
+ }
+ }
+
+ if (change == NULL)
+ {
+ /* we are done */
+ return;
+ }
+
+ /* add the new change to the list */
+ switch (change->kind)
+ {
+ case CHANGE_FILE_ADDED:
+ additions = g_list_prepend (additions, change->from);
+ break;
+
+ case CHANGE_FILE_CHANGED:
+ changes = g_list_prepend (changes, change->from);
+ break;
+
+ case CHANGE_FILE_REMOVED:
+ deletions = g_list_prepend (deletions, change->from);
+ break;
+
+ case CHANGE_FILE_MOVED:
+ pair = g_new (GFilePair, 1);
+ pair->from = change->from;
+ pair->to = change->to;
+ moves = g_list_prepend (moves, pair);
+ break;
+
+ case CHANGE_POSITION_SET:
+ position_set = g_new (CajaFileChangesQueuePosition, 1);
+ position_set->location = change->from;
+ position_set->set = TRUE;
+ position_set->point = change->point;
+ position_set->screen = change->screen;
+ position_set_requests = g_list_prepend (position_set_requests,
+ position_set);
+ break;
+
+ case CHANGE_POSITION_REMOVE:
+ position_set = g_new (CajaFileChangesQueuePosition, 1);
+ position_set->location = change->from;
+ position_set->set = FALSE;
+ position_set_requests = g_list_prepend (position_set_requests,
+ position_set);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ g_free (change);
+ }
+}