[Xfce4-commits] <tumbler:jannis/specialized> Squashme: Halfway through implementing specialized thumbnailer support.

Jannis Pohlmann noreply at xfce.org
Wed Oct 21 18:08:03 CEST 2009


Updating branch refs/heads/jannis/specialized
         to f0611bd69fc9732e75c11da57bf0fd49f0eb4296 (commit)
       from a08e126df38314568ed23b40da5317654ae12fdb (commit)

commit f0611bd69fc9732e75c11da57bf0fd49f0eb4296
Author: Jannis Pohlmann <jannis at xfce.org>
Date:   Wed Oct 21 18:00:38 2009 +0200

    Squashme: Halfway through implementing specialized thumbnailer support.
    
    Use a hash table to store all final override sections. When an overrides
    file is created/changed/deleted, we reload the entire override hash
    table.
    
    Use a hash table for mapping thumbnailer names to arrays with
    specialized thumbnailer infos (as the same service could be defined in
    two different thumbnailer directories). When a service file is
    deleted, we can simple remove the corresponding info from the array.
    When a service file is changed, we can update the corresponding info (I
    think in most cases we'll receive a deleted and a created event from the
    monitor, which would mean to remove and re-add the info from/to the
    array).
    
    TODO: Add object member to the ThumbnailerInfo, create/unref objects
    when .service files change. Update the registry supported cache whenever
    we're done with updating our internal data structures.

 tumblerd/tumbler-manager.c |  636 +++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 627 insertions(+), 9 deletions(-)

diff --git a/tumblerd/tumbler-manager.c b/tumblerd/tumbler-manager.c
index b5c3e1b..8914f54 100644
--- a/tumblerd/tumbler-manager.c
+++ b/tumblerd/tumbler-manager.c
@@ -22,6 +22,13 @@
 #include <config.h>
 #endif
 
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
 #include <glib.h>
 #include <glib-object.h>
 #include <glib/gi18n.h>
@@ -47,15 +54,26 @@ enum
 
 
 
-static void tumbler_manager_finalize     (GObject      *object);
-static void tumbler_manager_get_property (GObject      *object,
-                                          guint         prop_id,
-                                          GValue       *value,
-                                          GParamSpec   *pspec);
-static void tumbler_manager_set_property (GObject      *object,
-                                          guint         prop_id,
-                                          const GValue *value,
-                                          GParamSpec   *pspec);
+typedef struct _OverrideInfo    OverrideInfo;
+typedef struct _ThumbnailerInfo ThumbnailerInfo;
+
+
+
+static void tumbler_manager_finalize          (GObject          *object);
+static void tumbler_manager_get_property      (GObject          *object,
+                                               guint             prop_id,
+                                               GValue           *value,
+                                               GParamSpec       *pspec);
+static void tumbler_manager_set_property      (GObject          *object,
+                                               guint             prop_id,
+                                               const GValue     *value,
+                                               GParamSpec       *pspec);
+static void tumbler_manager_load_thumbnailers (TumblerManager   *manager);
+static void tumbler_manager_directory_changed (TumblerManager   *manager,
+                                               GFile            *file,
+                                               GFile            *other_file,
+                                               GFileMonitorEvent event_type,
+                                               GFileMonitor     *monitor);
 
 
 
@@ -71,9 +89,31 @@ struct _TumblerManager
   DBusGConnection *connection;
   TumblerRegistry *registry;
 
+  GList           *directories;
+  GList           *monitors;
+
+  GHashTable      *overrides;
+  GHashTable      *thumbnailers;
+
   GMutex          *mutex;
 };
 
+struct _OverrideInfo
+{
+  gchar *name;
+  gchar *uri_scheme;
+  gchar *mime_type;
+  gint   dir_index;
+};
+
+struct _ThumbnailerInfo
+{
+  gchar *name;
+  gchar **uri_schemes;
+  gchar **mime_types;
+  time_t  mtime;
+};
+
 
 
 G_DEFINE_TYPE (TumblerManager, tumbler_manager, G_TYPE_OBJECT);
@@ -111,20 +151,44 @@ tumbler_manager_class_init (TumblerManagerClass *klass)
 static void
 tumbler_manager_init (TumblerManager *manager)
 {
+  manager->directories = NULL;
+  manager->monitors = NULL;
   manager->mutex = g_mutex_new ();
 }
 
 
 
 static void
+monitor_unref (GFileMonitor *monitor)
+{
+  if (monitor != NULL)
+    g_object_unref (monitor);
+}
+
+
+
+static void
 tumbler_manager_finalize (GObject *object)
 {
   TumblerManager *manager = TUMBLER_MANAGER (object);
 
+  g_mutex_lock (manager->mutex);
+
+  g_list_foreach (manager->directories, (GFunc) g_object_unref, NULL);
+  g_list_free (manager->directories);
+
+  g_list_foreach (manager->monitors, (GFunc) monitor_unref, NULL);
+  g_list_free (manager->monitors);
+
+  if (manager->overrides != NULL)
+    g_hash_table_unref (manager->overrides);
+
   g_object_unref (manager->registry);
 
   dbus_g_connection_unref (manager->connection);
 
+  g_mutex_unlock (manager->mutex);
+
   g_mutex_free (manager->mutex);
 
   (*G_OBJECT_CLASS (tumbler_manager_parent_class)->finalize) (object);
@@ -180,6 +244,557 @@ tumbler_manager_set_property (GObject      *object,
 
 
 
+static OverrideInfo *
+override_info_new (void)
+{
+  return g_slice_new0 (OverrideInfo);
+}
+
+
+
+static void
+override_info_free (gpointer pointer)
+{
+  OverrideInfo *info = pointer;
+
+  if (info == NULL)
+    return;
+
+  g_free (info->name);
+  g_free (info->uri_scheme);
+  g_free (info->mime_type);
+
+  g_slice_free (OverrideInfo, info);
+}
+
+
+
+static gint
+override_info_compare (gconstpointer a,
+                       gconstpointer b)
+{
+  const OverrideInfo *info_a = a;
+  const OverrideInfo *info_b = b;
+
+  return CLAMP (info_b->dir_index - info_a->dir_index, -1, 1);
+}
+
+
+
+static void
+override_info_array_free (gpointer pointer)
+{
+  GPtrArray *infos = pointer;
+
+  g_ptr_array_foreach (infos, (GFunc) override_info_free, NULL);
+  g_ptr_array_free (infos, TRUE);
+}
+
+
+
+static void
+tumbler_manager_load_overrides (TumblerManager *manager,
+                                GFile          *directory)
+{
+  GPtrArray *overrides;
+  GKeyFile  *key_file;
+  GError    *error = NULL;
+  gchar    **sections;
+  gchar     *dirname;
+  gchar     *filename;
+  gchar     *uri_type;
+  guint      n;
+
+  g_return_if_fail (TUMBLER_IS_MANAGER (manager));
+  g_return_if_fail (G_IS_FILE (directory));
+
+  /* get the overrides file */
+  dirname = g_file_get_path (directory);
+  filename = g_build_filename (dirname, "overrides", NULL);
+  g_free (dirname);
+
+  if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+    {
+      g_free (filename);
+      return;
+    }
+
+  g_debug ("load_overrides: filename %s", filename);
+
+  /* allocate the key file */
+  key_file = g_key_file_new ();
+
+  /* try to load the key file from the overrides file */
+  if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &error))
+    {
+      g_warning (_("Failed to load the file \"%s\": %s"), filename, error->message);
+      g_clear_error (&error);
+      g_key_file_free (key_file);
+      g_free (filename);
+      return;
+    }
+
+  /* determine sections in the key file */
+  sections = g_key_file_get_groups (key_file, NULL);
+
+  /* iterate over all sections */
+  for (n = 0; sections != NULL && sections[n] != NULL; ++n)
+    {
+      OverrideInfo *info = override_info_new ();
+
+      info->name = g_key_file_get_string (key_file, sections[n], "Name", &error);
+      if (info->name == NULL)
+        {
+          g_warning (_("Malformed section \"%s\" in file \"%s\": %s"),
+                     sections[n], filename, error->message);
+          g_clear_error (&error);
+
+          override_info_free (info);
+          g_free (sections[n]);
+
+          continue;
+        }
+
+      info->uri_scheme = g_key_file_get_string (key_file, sections[n], 
+                                                "UriScheme", &error);
+      if (info->uri_scheme == NULL)
+        {
+          g_warning (_("Malformed section \"%s\" in file \"%s\": %s"),
+                     sections[n], filename, error->message);
+          g_clear_error (&error);
+
+          override_info_free (info);
+          g_free (sections[n]);
+
+          continue;
+        }
+
+      info->mime_type = g_key_file_get_string (key_file, sections[n], 
+                                               "MimeType", &error);
+      if (info->mime_type == NULL)
+        {
+          g_warning (_("Malformed section \"%s\" in file \"%s\": %s"),
+                     sections[n], filename, error->message);
+          g_clear_error (&error);
+
+          override_info_free (info);
+          g_free (sections[n]);
+
+          continue;
+        }
+
+      uri_type = g_strdup_printf ("%s-%s", info->uri_scheme, info->mime_type);
+      if (g_strcmp0 (sections[n], uri_type) != 0)
+        {
+          g_warning (_("Malformed section \"%s\": "
+                       "Mismatch between section name and UriScheme/MimeType"),
+                     sections[n]);
+
+          g_free (uri_type);
+          override_info_free (info);
+          g_free (sections[n]);
+
+          continue;
+        }
+      g_free (uri_type);
+
+      info->dir_index = g_list_index (manager->directories, directory);
+
+      overrides = g_hash_table_lookup (manager->overrides, sections[n]);
+      if (overrides == NULL)
+        {
+          overrides = g_ptr_array_new ();
+          g_hash_table_insert (manager->overrides, sections[n], overrides);
+        }
+
+      g_ptr_array_add (overrides, info);
+    }
+
+  g_free (sections[n]);
+  g_key_file_free (key_file);
+  g_free (filename);
+}
+
+
+
+static void
+tumbler_manager_update_overrides (TumblerManager *manager)
+{
+  GHashTableIter hash_iter;
+  GPtrArray     *infos;
+  GList         *iter;
+
+  g_return_if_fail (TUMBLER_IS_MANAGER (manager));
+
+  /* check if the overrides cache has been allocated already */
+  if (manager->overrides != NULL)
+    {
+      /* clear the overrides cache */
+      g_hash_table_remove_all (manager->overrides);
+    }
+  else
+    {
+      /* otherwise allocate the overrides cache on-demand */
+      manager->overrides = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                  g_free, override_info_array_free);
+    }
+
+  /* iterate over all thumbnailer directories with increasing priority */
+  for (iter = manager->directories; iter != NULL; iter = iter->next)
+    tumbler_manager_load_overrides (manager, iter->data);
+
+  g_hash_table_iter_init (&hash_iter, manager->overrides);
+  while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &infos))
+    g_ptr_array_sort (infos, override_info_compare);
+
+  const gchar *key;
+  g_hash_table_iter_init (&hash_iter, manager->overrides);
+  while (g_hash_table_iter_next (&hash_iter, (gpointer) &key, (gpointer) &infos))
+    {
+      guint n;
+
+      g_debug ("override section: %s", key);
+
+      for (n = 0; n < infos->len; ++n)
+        g_debug ("  %s", ((OverrideInfo *)g_ptr_array_index (infos, n))->name);
+    }
+}
+
+
+
+static ThumbnailerInfo *
+thumbnailer_info_new (void)
+{
+  return g_slice_new0 (ThumbnailerInfo);
+}
+
+
+
+static void
+thumbnailer_info_free (ThumbnailerInfo *info)
+{
+  if (info == NULL)
+    return;
+
+  g_free (info->name);
+  g_strfreev (info->uri_schemes);
+  g_strfreev (info->mime_types);
+  g_slice_free (ThumbnailerInfo, info);
+}
+
+
+
+static gint
+thumbnailer_info_compare (gconstpointer a, 
+                          gconstpointer b)
+{
+  const ThumbnailerInfo *info_a = a;
+  const ThumbnailerInfo *info_b = b;
+
+  return CLAMP (info_a->mtime - info_b->mtime, -1, 1);
+}
+
+
+
+static void
+thumbnailer_info_array_free (gpointer pointer)
+{
+  GPtrArray *infos = pointer;
+
+  g_ptr_array_foreach (infos, (GFunc) thumbnailer_info_free, NULL);
+  g_ptr_array_free (infos, TRUE);
+}
+
+
+
+static void
+tumbler_manager_load_thumbnailer_infos (TumblerManager *manager,
+                                        GFile          *directory)
+{
+  ThumbnailerInfo *info;
+  GPtrArray       *infos;
+  struct stat      stat;
+  const gchar     *basename;
+  GKeyFile        *key_file;
+  GError          *error = NULL;
+  GDir            *dir;
+  gchar           *dirname;
+  gchar           *filename;
+  gchar           *name;
+  gchar          **uri_schemes;
+  gchar          **mime_types;
+  guint            n;
+
+  g_return_if_fail (TUMBLER_IS_MANAGER (manager));
+  g_return_if_fail (G_IS_FILE (directory));
+
+  dirname = g_file_get_path (directory);
+
+  dir = g_dir_open (dirname, 0, NULL);
+
+  if (dir == NULL)
+    {
+      g_free (dirname);
+      return;
+    }
+
+  for (basename = g_dir_read_name (dir); 
+       basename != NULL; 
+       basename = g_dir_read_name (dir))
+    {
+      /* skip non-service files */
+      if (!g_str_has_suffix (basename, ".service"))
+        continue;
+
+      filename = g_build_filename (dirname, basename, NULL);
+
+      if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+        {
+          g_free (filename);
+          continue;
+        }
+
+      g_debug ("load_thumbnailer_infos: filename %s", filename);
+
+      key_file = g_key_file_new ();
+
+      if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &error))
+        {
+          g_warning (_("Failed to load the file \"%s\": %s"), filename, error->message);
+          g_clear_error (&error);
+
+          g_key_file_free (key_file);
+          g_free (filename);
+
+          continue;
+        }
+
+      name = g_key_file_get_string (key_file, "Specialized Thumbnailer", "Name", &error);
+      if (name == NULL)
+        {
+          g_warning (_("Malformed file \"%s\": %s"), filename, error->message);
+          g_clear_error (&error);
+
+          g_key_file_free (key_file);
+          g_free (filename);
+
+          continue;
+        }
+
+      mime_types = g_key_file_get_string_list (key_file, "Specialized Thumbnailer",
+                                               "MimeTypes", NULL, &error);
+      if (mime_types == NULL)
+        {
+          g_warning (_("Malformed file \"%s\": %s"), filename, error->message);
+          g_clear_error (&error);
+
+          g_free (name);
+          g_key_file_free (key_file);
+          g_free (filename);
+
+          continue;
+        }
+
+      uri_schemes = g_key_file_get_string_list (key_file, "Specialized Thumbnailer",
+                                                "UriSchemes", NULL, &error);
+      if (uri_schemes == NULL)
+        {
+          uri_schemes = g_new0 (gchar *, 2);
+          uri_schemes[0] = g_strdup ("file");
+          uri_schemes[1] = NULL;
+        }
+
+      if (g_stat (filename, &stat) != 0)
+        {
+          g_warning (_("Failed to determine last modified time of \"%s\""), filename); 
+
+          g_strfreev (uri_schemes);
+          g_strfreev (mime_types);
+          g_free (name);
+          g_key_file_free (key_file);
+          g_free (filename);
+
+          continue;
+        }
+
+      infos = g_hash_table_lookup (manager->thumbnailers, name);
+      if (infos == NULL)
+        {
+          infos = g_ptr_array_new ();
+          g_hash_table_replace (manager->thumbnailers, name, infos);
+        }
+
+      info = thumbnailer_info_new ();
+      info->name = name;
+      info->mtime = stat.st_mtime;
+      info->uri_schemes = uri_schemes;
+      info->mime_types = mime_types;
+
+      g_ptr_array_add (infos, info);
+    }
+}
+
+
+
+static void
+tumbler_manager_update_thumbnailers (TumblerManager *manager)
+{
+  GHashTableIter hash_iter;
+  GPtrArray     *infos;
+  GList         *iter;
+
+  g_return_if_fail (TUMBLER_IS_MANAGER (manager));
+
+  if (manager->thumbnailers != NULL)
+    {
+      /* clear the thumbnailer cache */
+      g_hash_table_remove_all (manager->thumbnailers);
+    }
+  else
+    {
+      /* otheriwse allocate the thumbnailer cache on-demand */
+      manager->thumbnailers = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                     g_free, thumbnailer_info_array_free);
+    }
+
+  /* iterate over all thumbnailer directories with increasing priority */
+  for (iter = manager->directories; iter != NULL; iter = iter->next)
+    tumbler_manager_load_thumbnailer_infos (manager, iter->data);
+
+  g_hash_table_iter_init (&hash_iter, manager->thumbnailers);
+  while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &infos))
+    g_ptr_array_sort (infos, thumbnailer_info_compare);
+}
+
+
+
+static void
+tumbler_manager_load_thumbnailers (TumblerManager *manager)
+{
+  const gchar *const *data_dirs;
+  GFileMonitor       *monitor;
+  GList              *directories;
+  GList              *iter;
+  gchar              *dirname;
+  guint               n;
+
+  g_return_if_fail (TUMBLER_MANAGER (manager));
+
+  g_mutex_lock (manager->mutex);
+
+  /* this function may only be called once */
+  g_assert (manager->directories == NULL);
+  g_assert (manager->monitors == NULL);
+
+  g_mutex_unlock (manager->mutex);
+
+  /* prepend $XDG_DATA_HOME/thumbnailers/ to the directory list */
+  dirname = g_build_filename (g_get_user_data_dir (), "thumbnailers", NULL);
+  directories = g_list_prepend (directories, g_file_new_for_path (dirname));
+  g_free (dirname);
+
+  /* determine system data dirs */
+  data_dirs = g_get_system_data_dirs ();
+
+  /* build $XDG_DATA_DIRS/thumbnailers dirnames and prepend them to the list */
+  for (data_dirs, n = 0; data_dirs[n] != NULL; ++n)
+    {
+      dirname = g_build_filename (data_dirs[n], "thumbnailers", NULL);
+      directories = g_list_prepend (directories, g_file_new_for_path (dirname));
+      g_free (dirname);
+    }
+
+  g_mutex_lock (manager->mutex);
+
+  /* pass the ownership of the directories list to the manager */
+  manager->directories = directories;
+  manager->monitors = NULL;
+
+  /* update the overrides cache */
+  tumbler_manager_update_overrides (manager);
+
+  /* update the thumbnailer cache */
+  tumbler_manager_update_thumbnailers (manager);
+
+  /* monitor the directories for changes */
+  for (iter = g_list_last (manager->directories); iter != NULL; iter = iter->prev)
+    {
+      /* allocate a monitor, connect to it and prepend it to the monitor list */
+      monitor = g_file_monitor_directory (iter->data, G_FILE_MONITOR_NONE, NULL, NULL);
+      g_signal_connect_swapped (monitor, "changed", 
+                                G_CALLBACK (tumbler_manager_directory_changed), manager);
+      manager->monitors = g_list_prepend (manager->monitors, monitor);
+    }
+
+  g_mutex_unlock (manager->mutex);
+}
+
+
+
+static gint
+compare_files (gconstpointer a,
+               gconstpointer b)
+{
+  GFile *file_a = G_FILE (a);
+  GFile *file_b = G_FILE (b);
+
+  if (g_file_equal (file_a, file_b))
+    return 0;
+  else
+    return -1;
+}
+
+
+
+static void
+tumbler_manager_directory_changed (TumblerManager   *manager,
+                                   GFile            *file,
+                                   GFile            *other_file,
+                                   GFileMonitorEvent event_type,
+                                   GFileMonitor     *monitor)
+{
+  GFileType type;
+  GList    *iter;
+  gchar    *basename;
+  gint      dir_index;
+
+  g_return_if_fail (TUMBLER_IS_MANAGER (manager));
+  g_return_if_fail (G_IS_FILE (file));
+  g_return_if_fail (G_IS_FILE_MONITOR (monitor));
+
+  type = g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL);
+
+  if (type == G_FILE_TYPE_REGULAR)
+    {
+      basename = g_file_get_basename (file);
+
+      if (g_strcmp0 (basename, "overrides") == 0)
+        {
+          g_debug ("overrides file %s changed", g_file_get_path (file));
+
+          tumbler_manager_update_overrides (manager);
+        }
+      else if (g_str_has_suffix (basename, ".service"))
+        {
+          g_debug ("service file %s changed", g_file_get_path (file));
+        }
+    }
+  else if (type == G_FILE_TYPE_DIRECTORY)
+    {
+      iter = g_list_find_custom (manager->directories, file, compare_files);
+
+      if (iter != NULL)
+        {
+          dir_index = g_list_position (manager->directories, iter);
+          g_debug ("dir at index %i changed", dir_index);
+
+          /* TODO remove overrides and thumbnailers for this dir_index */
+        }
+    }
+}
+
+
+
 TumblerManager *
 tumbler_manager_new (DBusGConnection *connection,
                      TumblerRegistry *registry)
@@ -247,6 +862,9 @@ tumbler_manager_start (TumblerManager *manager,
 
   g_mutex_unlock (manager->mutex);
 
+  /* load thumbnailers installed into the system permanently */
+  tumbler_manager_load_thumbnailers (manager);
+
   /* this is how I roll */
   return TRUE;
 }



More information about the Xfce4-commits mailing list