diff options
Diffstat (limited to 'src/nautilus-file-changes-queue.c')
-rw-r--r-- | src/nautilus-file-changes-queue.c | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/src/nautilus-file-changes-queue.c b/src/nautilus-file-changes-queue.c new file mode 100644 index 0000000..d1bdc46 --- /dev/null +++ b/src/nautilus-file-changes-queue.c @@ -0,0 +1,346 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + * + * Author: Pavel Cisler <pavel@eazel.com> + */ + +#include <config.h> +#include "nautilus-file-changes-queue.h" + +#include "nautilus-directory-notify.h" +#include "nautilus-tag-manager.h" + +typedef enum +{ + CHANGE_FILE_INITIAL, + CHANGE_FILE_ADDED, + CHANGE_FILE_CHANGED, + CHANGE_FILE_REMOVED, + CHANGE_FILE_MOVED, +} NautilusFileChangeKind; + +typedef struct +{ + NautilusFileChangeKind kind; + GFile *from; + GFile *to; + GdkPoint point; + int screen; +} NautilusFileChange; + +typedef struct +{ + GList *head; + GList *tail; + GMutex mutex; +} NautilusFileChangesQueue; + +static NautilusFileChangesQueue * +nautilus_file_changes_queue_new (void) +{ + NautilusFileChangesQueue *result; + + result = g_new0 (NautilusFileChangesQueue, 1); + g_mutex_init (&result->mutex); + + return result; +} + +static NautilusFileChangesQueue * +nautilus_file_changes_queue_get (void) +{ + static NautilusFileChangesQueue *file_changes_queue; + + if (file_changes_queue == NULL) + { + file_changes_queue = nautilus_file_changes_queue_new (); + } + + return file_changes_queue; +} + +static void +nautilus_file_changes_queue_add_common (NautilusFileChangesQueue *queue, + NautilusFileChange *new_item) +{ + /* enqueue the new queue item while locking down the list */ + g_mutex_lock (&queue->mutex); + + queue->head = g_list_prepend (queue->head, new_item); + if (queue->tail == NULL) + { + queue->tail = queue->head; + } + + g_mutex_unlock (&queue->mutex); +} + +void +nautilus_file_changes_queue_file_added (GFile *location) +{ + NautilusFileChange *new_item; + NautilusFileChangesQueue *queue; + + queue = nautilus_file_changes_queue_get (); + + new_item = g_new0 (NautilusFileChange, 1); + new_item->kind = CHANGE_FILE_ADDED; + new_item->from = g_object_ref (location); + nautilus_file_changes_queue_add_common (queue, new_item); +} + +void +nautilus_file_changes_queue_file_changed (GFile *location) +{ + NautilusFileChange *new_item; + NautilusFileChangesQueue *queue; + + queue = nautilus_file_changes_queue_get (); + + new_item = g_new0 (NautilusFileChange, 1); + new_item->kind = CHANGE_FILE_CHANGED; + new_item->from = g_object_ref (location); + nautilus_file_changes_queue_add_common (queue, new_item); +} + +void +nautilus_file_changes_queue_file_removed (GFile *location) +{ + NautilusFileChange *new_item; + NautilusFileChangesQueue *queue; + + queue = nautilus_file_changes_queue_get (); + + new_item = g_new0 (NautilusFileChange, 1); + new_item->kind = CHANGE_FILE_REMOVED; + new_item->from = g_object_ref (location); + nautilus_file_changes_queue_add_common (queue, new_item); +} + +void +nautilus_file_changes_queue_file_moved (GFile *from, + GFile *to) +{ + NautilusFileChange *new_item; + NautilusFileChangesQueue *queue; + + queue = nautilus_file_changes_queue_get (); + + new_item = g_new (NautilusFileChange, 1); + new_item->kind = CHANGE_FILE_MOVED; + new_item->from = g_object_ref (from); + new_item->to = g_object_ref (to); + nautilus_file_changes_queue_add_common (queue, new_item); +} + +static NautilusFileChange * +nautilus_file_changes_queue_get_change (NautilusFileChangesQueue *queue) +{ + GList *new_tail; + NautilusFileChange *result; + + g_assert (queue != NULL); + + /* dequeue the tail item while locking down the list */ + g_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; + } + + g_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 */ + g_list_free_full (pairs, g_free); +} + +/* go through changes in the change queue, send ones with the same kind + * in a list to the different nautilus_directory_notify calls + */ +void +nautilus_file_changes_consume_changes (gboolean consume_all) +{ + g_autoptr (NautilusTagManager) tag_manager = nautilus_tag_manager_get (); + NautilusFileChange *change; + GList *additions, *changes, *deletions, *moves; + GFilePair *pair; + guint chunk_count; + NautilusFileChangesQueue *queue; + gboolean flush_needed; + + + additions = NULL; + changes = NULL; + deletions = NULL; + moves = NULL; + + queue = nautilus_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 = nautilus_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; + + flush_needed |= changes != NULL + && change->kind != CHANGE_FILE_CHANGED; + + flush_needed |= moves != NULL + && change->kind != CHANGE_FILE_MOVED; + + flush_needed |= deletions != NULL + && change->kind != CHANGE_FILE_REMOVED; + + 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); + nautilus_directory_notify_files_removed (deletions); + g_list_free_full (deletions, g_object_unref); + deletions = NULL; + } + if (moves != NULL) + { + moves = g_list_reverse (moves); + nautilus_directory_notify_files_moved (moves); + pairs_list_free (moves); + moves = NULL; + } + if (additions != NULL) + { + additions = g_list_reverse (additions); + nautilus_directory_notify_files_added (additions); + g_list_free_full (additions, g_object_unref); + additions = NULL; + } + if (changes != NULL) + { + changes = g_list_reverse (changes); + nautilus_directory_notify_files_changed (changes); + g_list_free_full (changes, g_object_unref); + changes = 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: + { + nautilus_tag_manager_update_moved_uris (tag_manager, + change->from, + change->to); + + pair = g_new (GFilePair, 1); + pair->from = change->from; + pair->to = change->to; + moves = g_list_prepend (moves, pair); + } + break; + + default: + { + g_assert_not_reached (); + } + break; + } + + g_free (change); + } +} |