Logo Search packages:      
Sourcecode: nautilus version File versions

nautilus-file-changes-queue.c

/* -*- 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 <pavel@eazel.com>
*/

#include <config.h>
#include "nautilus-file-changes-queue.h"

#include "nautilus-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
} NautilusFileChangeKind;

typedef struct {
      NautilusFileChangeKind kind;
      GFile *from;
      GFile *to;
      GdkPoint point;
      int screen;
} NautilusFileChange;

typedef struct {
      GList *head;
      GList *tail;
#ifdef G_THREADS_ENABLED
      GMutex *mutex;
#endif
} NautilusFileChangesQueue;

static NautilusFileChangesQueue *
nautilus_file_changes_queue_new (void)
{
      NautilusFileChangesQueue *result;

      result = g_new0 (NautilusFileChangesQueue, 1);
      
#ifdef G_THREADS_ENABLED
      result->mutex = g_mutex_new ();
#endif
      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;
}

#if 0 /* no public free call yet */

static void
nautilus_file_change_free (NautilusFileChange *change)
{
      if (change->from) {
            g_object_unref (change->from);
      }
      if (change->to) {
            g_object_unref (change->to);
      }
}

void
nautilus_file_changes_queue_free (NautilusFileChangesQueue *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) {
            nautilus_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
nautilus_file_changes_queue_add_common (NautilusFileChangesQueue *queue, 
      NautilusFileChange *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
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);
}

void
nautilus_file_changes_queue_schedule_position_set (GFile *location, 
                                       GdkPoint point,
                                       int screen)
{
      NautilusFileChange *new_item;
      NautilusFileChangesQueue *queue;

      queue = nautilus_file_changes_queue_get ();

      new_item = g_new (NautilusFileChange, 1);
      new_item->kind = CHANGE_POSITION_SET;
      new_item->from = g_object_ref (location);
      new_item->point = point;
      new_item->screen = screen;
      nautilus_file_changes_queue_add_common (queue, new_item);
}

void
nautilus_file_changes_queue_schedule_position_remove (GFile *location)
{
      NautilusFileChange *new_item;
      NautilusFileChangesQueue *queue;

      queue = nautilus_file_changes_queue_get ();

      new_item = g_new (NautilusFileChange, 1);
      new_item->kind = CHANGE_POSITION_REMOVE;
      new_item->from = g_object_ref (location);
      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 */
      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;
      NautilusFileChangesQueuePosition *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 nautilus_directory_notify calls
 */ 
void
nautilus_file_changes_consume_changes (gboolean consume_all)
{
      NautilusFileChange *change;
      GList *additions, *changes, *deletions, *moves;
      GList *position_set_requests;
      GFilePair *pair;
      NautilusFileChangesQueuePosition *position_set;
      guint chunk_count;
      NautilusFileChangesQueue *queue;
      gboolean flush_needed;
      

      additions = NULL;
      changes = NULL;
      deletions = NULL;
      moves = NULL;
      position_set_requests = 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
                        && 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);
                        nautilus_directory_notify_files_removed (deletions);
                        eel_g_object_list_free (deletions);
                        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);
                        eel_g_object_list_free (additions);
                        additions = NULL;
                  }
                  if (changes != NULL) {
                        changes = g_list_reverse (changes);
                        nautilus_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);
                        nautilus_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 (NautilusFileChangesQueuePosition, 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 (NautilusFileChangesQueuePosition, 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);
      }     
}

Generated by  Doxygen 1.6.0   Back to index