[gtk+/filesystemmodel] add directory monitoring with API to manually add and remove files



commit cd91f5bdbe4421d253206bb13137ffdf29d1943d
Author: Benjamin Otte <otte gnome org>
Date:   Wed Jun 24 10:12:49 2009 +0200

    add directory monitoring with API to manually add and remove files

 gtk/gtkfilechooserdefault.c |    2 +-
 gtk/gtkfilesystemmodel.c    |  233 ++++++++++++++++++++++++++++++++-----------
 gtk/gtkfilesystemmodel.h    |    8 ++-
 3 files changed, 183 insertions(+), 60 deletions(-)
---
diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c
index d5e891a..13e93fc 100644
--- a/gtk/gtkfilechooserdefault.c
+++ b/gtk/gtkfilechooserdefault.c
@@ -6756,7 +6756,7 @@ file_system_model_got_thumbnail (GObject *object, GAsyncResult *res, gpointer da
   copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
   copy_attribute (info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON);
 
-  _gtk_file_system_update_file (model, file, info, FALSE);
+  _gtk_file_system_model_update_file (model, file, info, FALSE);
 
   g_object_unref (info);
 }
diff --git a/gtk/gtkfilesystemmodel.c b/gtk/gtkfilesystemmodel.c
index a5cf03a..ef3df19 100644
--- a/gtk/gtkfilesystemmodel.c
+++ b/gtk/gtkfilesystemmodel.c
@@ -65,7 +65,7 @@ struct _GtkFileSystemModel
   GFile *               dir;            /* directory that's displayed */
   guint                 dir_thaw_source;/* GSource id for unfreezing the model */
   char *                attributes;     /* attributes the file info must contain */
-  char *                full_attributes;/* attributes the file info should contain */
+  GFileMonitor *        dir_monitor;    /* directory that is monitored */
 
   GCancellable *        cancellable;    /* cancellable in use for all operations - cancelled on dispose */
   GArray *              files;          /* array of FileModelNode containing all our files */
@@ -767,6 +767,8 @@ gtk_file_system_model_dispose (GObject *object)
   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
 
   g_cancellable_cancel (model->cancellable);
+  if (model->dir_monitor)
+    g_file_monitor_cancel (model->dir_monitor);
 
   G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->dispose (object);
 }
@@ -790,7 +792,10 @@ gtk_file_system_model_finalize (GObject *object)
 
   g_object_unref (model->cancellable);
   g_free (model->attributes);
-  g_object_unref (model->dir);
+  if (model->dir)
+    g_object_unref (model->dir);
+  if (model->dir_monitor)
+    g_object_unref (model->dir_monitor);
   g_hash_table_destroy (model->file_lookup);
 
   _gtk_tree_data_list_header_free (model->sort_list);
@@ -818,25 +823,6 @@ _gtk_file_system_model_class_init (GtkFileSystemModelClass *class)
 		  G_TYPE_NONE, 1, G_TYPE_POINTER);
 }
 
-/* NB: takes ownership of file and info */
-static void
-gtk_file_system_model_add_node (GtkFileSystemModel *model, GFile *file, GFileInfo *info)
-{
-  FileModelNode *node = g_slice_alloc0 (NODE_SIZE (model));
-
-  node->file = file;
-  node->info = info;
-  node->frozen_add = model->frozen ? TRUE : FALSE;
-
-  g_array_append_vals (model->files, node, 1);
-  g_slice_free1 (NODE_SIZE (model), node);
-
-  if (!model->frozen)
-    node_set_visible (model, model->files->len -1,
-                      node_should_be_visible (model, model->files->len - 1));
-  gtk_file_system_model_sort_node (model, model->files->len -1);
-}
-
 static void
 _gtk_file_system_model_init (GtkFileSystemModel *model)
 {
@@ -908,7 +894,9 @@ gtk_file_system_model_got_files (GObject *object, GAsyncResult *res, gpointer da
               continue;
             }
           file = g_file_get_child (model->dir, name);
-          gtk_file_system_model_add_node (model, file, info);
+          _gtk_file_system_model_add_file (model, file, info);
+          g_object_unref (file);
+          g_object_unref (info);
         }
       g_list_free (files);
     }
@@ -946,6 +934,56 @@ gtk_file_system_model_got_files (GObject *object, GAsyncResult *res, gpointer da
 }
 
 static void
+gtk_file_system_model_query_done (GObject *     object,
+                                  GAsyncResult *res,
+                                  gpointer      data)
+{
+  GtkFileSystemModel *model = data; /* only a valid pointer if not cancelled */
+  GFile *file = G_FILE (object);
+  GFileInfo *info;
+
+  info = g_file_query_info_finish (file, res, NULL);
+  if (info == NULL)
+    return;
+
+  _gtk_file_system_model_update_file (model, file, info, TRUE);
+}
+
+static void
+gtk_file_system_model_monitor_change (GFileMonitor *      monitor,
+                                      GFile *             file,
+                                      GFile *             other_file,
+                                      GFileMonitorEvent   type,
+                                      GtkFileSystemModel *model)
+{
+  switch (type)
+    {
+      case G_FILE_MONITOR_EVENT_CREATED:
+      case G_FILE_MONITOR_EVENT_CHANGED:
+      case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
+        /* We can treat all of these the same way */
+        g_file_query_info_async (file,
+                                 model->attributes,
+                                 0,
+                                 IO_PRIORITY,
+                                 model->cancellable,
+                                 gtk_file_system_model_query_done,
+                                 model);
+        break;
+      case G_FILE_MONITOR_EVENT_DELETED:
+        _gtk_file_system_model_remove_file (model, file);
+        break;
+      case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+        /* FIXME: use freeze/thaw with this somehow? */
+      case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
+      case G_FILE_MONITOR_EVENT_UNMOUNTED:
+      default:
+        /* ignore these */
+        break;
+    }
+}
+
+static void
 gtk_file_system_model_got_enumerator (GObject *dir, GAsyncResult *res, gpointer data)
 {
   GtkFileSystemModel *model = data;
@@ -970,6 +1008,15 @@ gtk_file_system_model_got_enumerator (GObject *dir, GAsyncResult *res, gpointer
                                           gtk_file_system_model_got_files,
                                           model);
       g_object_unref (enumerator);
+      model->dir_monitor = g_file_monitor_directory (model->dir,
+                                                     0,
+                                                     model->cancellable,
+                                                     NULL);
+      if (model->dir_monitor)
+        g_signal_connect (model->dir_monitor,
+                          "changed",
+                          G_CALLBACK (gtk_file_system_model_monitor_change),
+                          model);
     }
 
   gdk_threads_leave ();
@@ -1003,28 +1050,42 @@ gtk_file_system_model_set_n_columns (GtkFileSystemModel *model,
 
   model->files = g_array_new (FALSE, FALSE, NODE_SIZE (model));
   /* add editable node at start */
-  gtk_file_system_model_add_node (model, NULL, NULL);
+  g_array_set_size (model->files, 1);
+  memset (get_node (model, 0), 0, NODE_SIZE (model));
+}
+
+static void
+gtk_file_system_model_set_directory (GtkFileSystemModel *model,
+                                     GFile *             dir,
+			             const gchar *       attributes)
+{
+  g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
+  g_return_if_fail (dir == NULL || G_IS_FILE (dir));
+
+  model->dir = g_object_ref (dir);
+  model->attributes = g_strdup (attributes);
+
+  g_object_ref (model);
+  g_file_enumerate_children_async (model->dir,
+                                   attributes,
+                                   0,
+                                   IO_PRIORITY,
+                                   model->cancellable,
+                                   gtk_file_system_model_got_enumerator,
+                                   model);
+
 }
 
 /**
  * _gtk_file_system_model_new:
  * @directory: the directory to show.
  * @attributes: attributes to immediately load or %NULL for all
- * @full_attributes: %NULL or a superset of @attributes
  * @error: location to store error, or %NULL.
  *
  * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
  * object wraps the given @directory as a #GtkTreeModel.
  * The model will query the given @attributes immediately and only add 
- * files with those attributes present. After all files have been added,
- * @full_attributes will be queried on-demand and the file infos will be
- * updated. The GtkTreeModel::row-changed signal will be emitted when this
- * happens. It is suggested that you only query attributes that are 
- * immediately necessary for using this model and add all other attributes
- * to @full_attributes to avoid slow load times. Worst offenders are 
- * content type and thumbnails.
- * If @full_attributes is non-%NULL, the model will also initially not 
- * follow symlinks to improve load times.
+ * files with those attributes present.
  * 
  * Return value: the newly created #GtkFileSystemModel object, or NULL if there
  * was an error.
@@ -1043,8 +1104,6 @@ _gtk_file_system_model_new (GFile *                    dir,
   g_return_val_if_fail (G_IS_FILE (dir), NULL);
 
   model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
-  model->dir = g_object_ref (dir);
-  model->attributes = g_strdup (attributes);
   model->get_func = get_func;
   model->get_data = get_data;
 
@@ -1052,14 +1111,7 @@ _gtk_file_system_model_new (GFile *                    dir,
   gtk_file_system_model_set_n_columns (model, n_columns, args);
   va_end (args);
 
-  g_object_ref (model);
-  g_file_enumerate_children_async (dir,
-                                   attributes,
-                                   0,
-                                   IO_PRIORITY,
-                                   model->cancellable,
-                                   gtk_file_system_model_got_enumerator,
-                                   model);
+  gtk_file_system_model_set_directory (model, dir, attributes);
 
   return model;
 }
@@ -1171,15 +1223,12 @@ _gtk_file_system_model_get_cancellable (GtkFileSystemModel *model)
  * @iter: a #GtkTreeIter pointing to a row of @model
  * 
  * Gets the #GFileInfo structure for a particular row
- * of @model. The information included in this structure
- * is determined by the attributes and full_attributes 
- * parameter to _gtk_file_system_model_new(). Note that the
- * full_attributes might not be filled in yet.
+ * of @model.
  * 
  * Return value: a #GFileInfo structure. This structure
  *   is owned by @model and must not be modified or freed.
- *   If you want to save the information for later use,
- *   you must make a copy, since the structure may be
+ *   If you want to keep the information for later use,
+ *   you must take a reference, since the structure may be
  *   freed on later changes to the file system.  If you have
  *   called _gtk_file_system_model_add_editable() and the @iter
  *   corresponds to the row that this function returned, the
@@ -1313,20 +1362,87 @@ _gtk_file_system_model_get_iter_for_file (GtkFileSystemModel *model,
 }
 
 /**
- * _gtk_file_system_update_file:
+ * _gtk_file_system_model_add_file:
+ * @model: the model
+ * @file: the file to add
+ * @info: the information to associate with the file
+ *
+ * Adds the given @file with its associated @info to the @model. 
+ * If the model is frozen, the file will only show up after it is thawn.
+ **/
+void
+_gtk_file_system_model_add_file (GtkFileSystemModel *model,
+                                 GFile              *file,
+                                 GFileInfo          *info)
+{
+  FileModelNode *node;
+  
+  g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
+  g_return_if_fail (G_IS_FILE (file));
+  g_return_if_fail (G_IS_FILE_INFO (info));
+
+  node = g_slice_alloc0 (NODE_SIZE (model));
+  node->file = g_object_ref (file);
+  if (info)
+    node->info = g_object_ref (info);
+  node->frozen_add = model->frozen ? TRUE : FALSE;
+
+  g_array_append_vals (model->files, node, 1);
+  g_slice_free1 (NODE_SIZE (model), node);
+
+  if (!model->frozen)
+    node_set_visible (model, model->files->len -1,
+                      node_should_be_visible (model, model->files->len - 1));
+  gtk_file_system_model_sort_node (model, model->files->len -1);
+}
+
+/**
+ * _gtk_file_system_model_remove_file:
+ * @model: the model
+ * @file: file to remove from the model. The file must have been 
+ *        added to the model previously
+ *
+ * Removes the given file from the model. If the file is not part of 
+ * @model, this function does nothing.
+ **/
+void
+_gtk_file_system_model_remove_file (GtkFileSystemModel *model,
+                                   GFile              *file)
+{
+  FileModelNode *node;
+  guint id;
+
+  g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
+  g_return_if_fail (G_IS_FILE (file));
+
+  id = node_get_for_file (model, file);
+  if (id == 0)
+    return;
+
+  node = get_node (model, id);
+  node_set_visible (model, id, FALSE);
+  g_object_unref (node->file);
+  if (node->info)
+    g_object_unref (node->info);
+  g_array_remove_index (model->files, id);
+}
+
+/**
+ * _gtk_file_system_model_update_file:
  * @model: the model
- * @file: the file, which must be part of the model
+ * @file: the file
  * @info: the new file info
  * @requires_resort: FIXME: get rid of this argument
  *
  * Tells the file system model that the file changed and that the 
- * new @info should be used for it now. 
+ * new @info should be used for it now.  If the file is not part of 
+ * @model, it will get added automatically.
  **/
 void
-_gtk_file_system_update_file (GtkFileSystemModel *model,
-                              GFile              *file,
-                              GFileInfo          *info,
-                              gboolean            requires_resort)
+_gtk_file_system_model_update_file (GtkFileSystemModel *model,
+                                    GFile              *file,
+                                    GFileInfo          *info,
+                                    gboolean            requires_resort)
 {
   FileModelNode *node;
   guint i, id;
@@ -1337,7 +1453,7 @@ _gtk_file_system_update_file (GtkFileSystemModel *model,
 
   id = node_get_for_file (model, file);
   if (id == 0)
-    g_assert_not_reached ();
+    _gtk_file_system_model_add_file (model, file, info);
 
   node = get_node (model, id);
   if (node->info)
@@ -1498,7 +1614,8 @@ void
 _gtk_file_system_model_clear_cache (GtkFileSystemModel *model,
                                     int                 column)
 {
-  guint i, start, end;
+  guint i;
+  int start, end;
   gboolean changed;
 
   g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
diff --git a/gtk/gtkfilesystemmodel.h b/gtk/gtkfilesystemmodel.h
index cb91fce..a6cc44b 100644
--- a/gtk/gtkfilesystemmodel.h
+++ b/gtk/gtkfilesystemmodel.h
@@ -58,7 +58,13 @@ GFile *             _gtk_file_system_model_get_file         (GtkFileSystemModel
 const GValue *      _gtk_file_system_model_get_value        (GtkFileSystemModel *model,
                                                              GtkTreeIter *       iter,
                                                              int                 column);
-void                _gtk_file_system_update_file            (GtkFileSystemModel *model,
+
+void                _gtk_file_system_model_add_file         (GtkFileSystemModel *model,
+                                                             GFile              *file,
+                                                             GFileInfo          *info);
+void                _gtk_file_system_model_remove_file      (GtkFileSystemModel *model,
+                                                             GFile              *file);
+void                _gtk_file_system_model_update_file      (GtkFileSystemModel *model,
                                                              GFile              *file,
                                                              GFileInfo          *info,
                                                              gboolean            requires_resort);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]