[Xfce4-commits] <tumbler:master> Support specialized thumbnailers and overrides files properly.

Jannis Pohlmann noreply at xfce.org
Mon Nov 16 14:54:01 CET 2009


Updating branch refs/heads/master
         to 47b8b19d478b6f0846454aeecb25fc67c7113a20 (commit)
       from a13a6b37a203cf03937d714a5faa40879d9a696c (commit)

commit 47b8b19d478b6f0846454aeecb25fc67c7113a20
Author: Jannis Pohlmann <jannis at xfce.org>
Date:   Wed Oct 21 17:57:20 2009 +0200

    Support specialized thumbnailers and overrides files properly.
    
    This required some major changes, most of which are documented in the
    source code. It still needs to be tested but it should be fine. The
    mutex locks in TumblerManager could probably be optimized and some other
    parts are not optimal with regards to speed and memory usage yet.
    
    All in all, what's done now is:
      - overrides files are parsed into override info structs and kept in
        a (hashkey -> override info list) hash table
      - each of these override info lists is sorted in the order of the
        directories the overrides files are located in (with higher priority
        directories coming first)
      - the first override info in each list determines which specialized
        thumbnailer is set as preferred for the corresponding hash key in
        the registry
      - thumbnailer service files are parsed into thumbnailer info structs
        and kept in a (basename -> thumbnailer info list) hash table
      - each of these thumbnailer info lists is sorted in the order of
        the directories the thumbnailer service infos come from
      - the first thumbnailers in each list are added to the registry
      - whenever something changes on the disk, this internal representation
        is updated

 configure.ac                               |    2 +-
 tumbler/tumbler-thumbnailer.c              |   24 +-
 tumbler/tumbler-thumbnailer.h              |   30 +-
 tumblerd/tumbler-manager.c                 | 1580 +++++++++++++++++++++++++++-
 tumblerd/tumbler-manager.h                 |    4 +-
 tumblerd/tumbler-registry.c                |  197 +++--
 tumblerd/tumbler-registry.h                |    7 +
 tumblerd/tumbler-specialized-thumbnailer.c |   82 ++-
 tumblerd/tumbler-specialized-thumbnailer.h |   29 +-
 9 files changed, 1815 insertions(+), 140 deletions(-)

diff --git a/configure.ac b/configure.ac
index e24ea92..f8988d7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -106,7 +106,7 @@ dnl ***************************************
 dnl *** Check for standard header files ***
 dnl ***************************************
 AC_HEADER_STDC()
-AC_CHECK_HEADERS([unistd.h sched.h linux/sched.h syscall.h])
+AC_CHECK_HEADERS([unistd.h sched.h linux/sched.h syscall.h sys/types.h sys/stat.h])
 
 dnl ************************************
 dnl *** Check for standard functions ***
diff --git a/tumbler/tumbler-thumbnailer.c b/tumbler/tumbler-thumbnailer.c
index 588cfa2..d40c1b8 100644
--- a/tumbler/tumbler-thumbnailer.c
+++ b/tumbler/tumbler-thumbnailer.c
@@ -125,7 +125,7 @@ tumbler_thumbnailer_class_init (TumblerThumbnailerIface *klass)
   tumbler_thumbnailer_signals[SIGNAL_UNREGISTER] =
     g_signal_new ("unregister",
                   TUMBLER_TYPE_THUMBNAILER,
-                  G_SIGNAL_RUN_LAST,
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
                   G_STRUCT_OFFSET (TumblerThumbnailerIface, unregister),
                   NULL,
                   NULL,
@@ -191,6 +191,28 @@ tumbler_thumbnailer_get_uri_schemes (TumblerThumbnailer *thumbnailer)
 
 
 
+gboolean
+tumbler_thumbnailer_supports_hash_key (TumblerThumbnailer *thumbnailer,
+                                       const gchar        *hash_key)
+{
+  gboolean supported = FALSE;
+  GStrv    hash_keys;
+  guint    n;
+
+  g_return_val_if_fail (TUMBLER_IS_THUMBNAILER (thumbnailer), FALSE);
+  g_return_val_if_fail (hash_key != NULL && *hash_key != '\0', FALSE);
+
+  hash_keys = tumbler_thumbnailer_get_hash_keys (thumbnailer);
+
+  for (n = 0; !supported && hash_keys != NULL && hash_keys[n] != NULL; ++n)
+    if (g_strcmp0 (hash_keys[n], hash_key) == 0)
+      supported = TRUE;
+
+  return supported;
+}
+
+
+
 TumblerThumbnailer **
 tumbler_thumbnailer_array_copy (TumblerThumbnailer **thumbnailers,
                                 guint                length)
diff --git a/tumbler/tumbler-thumbnailer.h b/tumbler/tumbler-thumbnailer.h
index 9194fe4..36adf6d 100644
--- a/tumbler/tumbler-thumbnailer.h
+++ b/tumbler/tumbler-thumbnailer.h
@@ -54,25 +54,27 @@ struct _TumblerThumbnailerIface
   void (*unregister) (TumblerThumbnailer *thumbnailer);
 
   /* virtual methods */
-  void (*create) (TumblerThumbnailer     *thumbnailer,
-                  GCancellable           *cancellable,
-                  TumblerFileInfo        *info);
+  void (*create) (TumblerThumbnailer *thumbnailer,
+                  GCancellable       *cancellable,
+                  TumblerFileInfo    *info);
 };
 
-GType                tumbler_thumbnailer_get_type        (void) G_GNUC_CONST;
+GType                tumbler_thumbnailer_get_type          (void) G_GNUC_CONST;
 
-void                 tumbler_thumbnailer_create          (TumblerThumbnailer     *thumbnailer,
-                                                          GCancellable           *cancellable,
-                                                          TumblerFileInfo        *info);
+void                 tumbler_thumbnailer_create            (TumblerThumbnailer     *thumbnailer,
+                                                            GCancellable           *cancellable,
+                                                            TumblerFileInfo        *info);
 
-GStrv                tumbler_thumbnailer_get_hash_keys   (TumblerThumbnailer  *thumbnailer);
-GStrv                tumbler_thumbnailer_get_mime_types  (TumblerThumbnailer  *thumbnailer);
-GStrv                tumbler_thumbnailer_get_uri_schemes (TumblerThumbnailer  *thumbnailer);
+GStrv                tumbler_thumbnailer_get_hash_keys     (TumblerThumbnailer  *thumbnailer);
+GStrv                tumbler_thumbnailer_get_mime_types    (TumblerThumbnailer  *thumbnailer);
+GStrv                tumbler_thumbnailer_get_uri_schemes   (TumblerThumbnailer  *thumbnailer);
+gboolean             tumbler_thumbnailer_supports_hash_key (TumblerThumbnailer  *thumbnailer,
+                                                            const gchar         *hash_key);
 
-TumblerThumbnailer **tumbler_thumbnailer_array_copy      (TumblerThumbnailer **thumbnailers,
-                                                          guint                length);
-void                 tumbler_thumbnailer_array_free      (TumblerThumbnailer **thumbnailers,
-                                                          guint                length);
+TumblerThumbnailer **tumbler_thumbnailer_array_copy        (TumblerThumbnailer **thumbnailers,
+                                                            guint                length);
+void                 tumbler_thumbnailer_array_free        (TumblerThumbnailer **thumbnailers,
+                                                            guint                length);
 
 G_END_DECLS
 
diff --git a/tumblerd/tumbler-manager.c b/tumblerd/tumbler-manager.c
index b5c3e1b..ad614a3 100644
--- a/tumblerd/tumbler-manager.c
+++ b/tumblerd/tumbler-manager.c
@@ -22,9 +22,19 @@
 #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>
+#include <glib/gstdio.h>
+
+#include <gio/gio.h>
 
 #include <dbus/dbus.h>
 #include <dbus/dbus-glib.h>
@@ -47,15 +57,41 @@ 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_monitor_unref     (GFileMonitor     *monitor,
+                                                           TumblerManager   *manager);
+static void             tumbler_manager_load_thumbnailers (TumblerManager   *manager,
+                                                           GFile            *directory);
+static void             tumbler_manager_directory_changed (TumblerManager   *manager,
+                                                           GFile            *file,
+                                                           GFile            *other_file,
+                                                           GFileMonitorEvent event_type,
+                                                           GFileMonitor     *monitor);
+
+static OverrideInfo    *override_info_new                 (void);
+static void             override_info_free                (gpointer          pointer);
+static void             override_info_list_free           (gpointer          pointer);
+static ThumbnailerInfo *thumbnailer_info_new              (void);
+static void             thumbnailer_info_free             (ThumbnailerInfo  *info);
+static void             thumbnailer_info_list_free        (gpointer          pointer);
+
+#ifdef DEBUG
+static void             dump_overrides                    (TumblerManager   *manager);
+static void             dump_thumbnailers                 (TumblerManager   *manager);
+#endif
 
 
 
@@ -71,9 +107,44 @@ struct _TumblerManager
   DBusGConnection *connection;
   TumblerRegistry *registry;
 
+  /* Directory and monitor objects for the thumbnailer dirs in 
+   * XDG_DATA_HOME and XDG_DATA_DIRS */
+  GList           *directories;
+  GList           *monitors;
+
+  /* hash table for override information, mapping hash keys to lists
+   * of override infos. in each of these lists the infos are sorted by 
+   * the directory index, with higher priority directories (smaller 
+   * directory index) coming first */
+  GHashTable      *overrides;
+
+  /* hash table for thumbnailer service information, mapping .service 
+   * basenames to lists of thumbnailer infos installed into the
+   * system with these basenames. in each of these lists the infos are
+   * sorted by the directory index, with higher priority directories 
+   * (smaller directory index) coming first */
+  GHashTable      *thumbnailers;
+
   GMutex          *mutex;
 };
 
+struct _OverrideInfo
+{
+  gchar *name;
+  gchar *uri_scheme;
+  gchar *mime_type;
+  gint   dir_index;
+};
+
+struct _ThumbnailerInfo
+{
+  TumblerThumbnailer *thumbnailer;
+#ifdef DEBUG
+  GFile              *file;
+#endif
+  gint                dir_index;
+};
+
 
 
 G_DEFINE_TYPE (TumblerManager, tumbler_manager, G_TYPE_OBJECT);
@@ -111,7 +182,17 @@ tumbler_manager_class_init (TumblerManagerClass *klass)
 static void
 tumbler_manager_init (TumblerManager *manager)
 {
+  manager->directories = NULL;
+  manager->monitors = NULL;
   manager->mutex = g_mutex_new ();
+
+  /* create the overrides info hash table */
+  manager->overrides = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                              g_free, override_info_list_free);
+
+  /* create the thumbnailer info hash table */
+  manager->thumbnailers = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                 g_free, thumbnailer_info_list_free);
 }
 
 
@@ -121,10 +202,29 @@ tumbler_manager_finalize (GObject *object)
 {
   TumblerManager *manager = TUMBLER_MANAGER (object);
 
+  g_mutex_lock (manager->mutex);
+
+  /* release all thumbnailer directory monitors */
+  g_list_foreach (manager->monitors, (GFunc) tumbler_manager_monitor_unref, manager);
+  g_list_free (manager->monitors);
+
+  /* release all directory objects */
+  g_list_foreach (manager->directories, (GFunc) g_object_unref, NULL);
+  g_list_free (manager->directories);
+
+  /* destroy the hash tables */
+  g_hash_table_unref (manager->thumbnailers);
+  g_hash_table_unref (manager->overrides);
+
+  /* release the registry */
   g_object_unref (manager->registry);
 
+  /* release the D-Bus connection object */
   dbus_g_connection_unref (manager->connection);
 
+  g_mutex_unlock (manager->mutex);
+
+  /* destroy the mutex */
   g_mutex_free (manager->mutex);
 
   (*G_OBJECT_CLASS (tumbler_manager_parent_class)->finalize) (object);
@@ -177,6 +277,1452 @@ tumbler_manager_set_property (GObject      *object,
       break;
     }
 }
+ 
+ 
+ 
+static void
+tumbler_manager_monitor_unref (GFileMonitor   *monitor,
+                               TumblerManager *manager)
+{
+  if (monitor != NULL)
+    {
+      /* disconnect from the changed signal */
+      g_signal_handlers_disconnect_matched (monitor, G_SIGNAL_MATCH_DATA,
+                                            0, 0, NULL, NULL, manager);
+
+      /* release (destroy) the monitor */
+      g_object_unref (monitor);
+    }
+}
+
+
+
+/**
+ * tumbler_manager_get_dir_index:
+ * @manager   : a #TumblerManager.
+ * @directory : a #GFile pointing to a thumbnailer directory.
+ *
+ * This function returns the index of the @directory in the thumbnailer
+ * directory list that is managed by the @manager. A low index means 
+ * higher priority (e.g. XDG_DATA_HOME/thumbnailers comes before 
+ * any thumbnailer dir in XDG_DATA_DIRS in the list). An index of -1
+ * means the directory is not a thumbnailer directory.
+ *
+ * Return value: Index of the thumbnailer @directory (lower index indicating
+ *               a higher priority). -1 if the @directory is not a thumbnailer
+ *               directory.
+ */
+static gint
+tumbler_manager_get_dir_index (TumblerManager *manager,
+                               GFile          *directory)
+{
+  GList *lp;
+  gint   dir_index = -1;
+  gint   n;
+
+  /* iterate over all thumbnailer directories */
+  for (lp = manager->directories, n = 0; dir_index < 0 && lp != NULL; lp = lp->next, ++n)
+    {
+      /* remember the directory index if the directories match */
+      if (g_file_equal (lp->data, directory))
+        dir_index = n;
+    }
+
+  /* return the index of the first thumbnailer directory that matches 
+   * the input directory or -1 if there is no such directory */
+  return n;
+}
+
+
+
+/**
+ * tumbler_manager_update_preferred:
+ * @manager  : a #TumblerManager.
+ * @hash_key : a hash key consisting of a URI scheme and a MIME type.
+ *
+ * This function picks the highest priority override section for the @hash_key
+ * and iterates over all thumbnailers provided by the thumbnailer service
+ * files. It selects the first thumbnailer that matches the override section
+ * (by comparing its name to the thumbnailer name defined in the section)
+ * and calls tumbler_registry_set_preferred() to make this thumbnailer the
+ * preferred thumbnailer for the URI scheme and MIME type combination.
+ *
+ * If there is no such thumbnailer, the preferred thumbnailer for this
+ * hash key will be cleared.
+ *
+ * Note that we do not call tumbler_registry_update_supported() here as
+ * the selected thumbnailer (if there is any that matches the override
+ * section) is supposed to be known by the registry already.
+ */
+static void
+tumbler_manager_update_preferred (TumblerManager *manager,
+                                  const gchar    *hash_key)
+{
+  TumblerThumbnailer *thumbnailer = NULL;
+  ThumbnailerInfo    *info;
+  GHashTableIter      iter;
+  const gchar        *current_name;
+  const gchar        *name;
+  GList             **overrides;
+  GList             **thumbnailers;
+
+  g_return_if_fail (TUMBLER_IS_MANAGER (manager));
+  g_return_if_fail (hash_key != NULL && *hash_key != '\0');
+
+  /* fetch all override infos for this hash key */
+  overrides = g_hash_table_lookup (manager->overrides, hash_key);
+  if (overrides != NULL)
+    {
+      /* if there is a value for the hash key it has to be a valid pointer
+       * to a non-empty override info list, otherwise there is a bug */
+      g_assert (overrides != NULL && *overrides != NULL);
+
+      /* determine the name of the active (= the first) override info 
+       * for this hash key */
+      name = ((OverrideInfo *)(*overrides)->data)->name;
+      
+      /* iterate over all thumbnailer info lists we have. stop as soon as we
+       * have one matching thumbnailer info that has the correct name and
+       * supports the hash key */
+      g_hash_table_iter_init (&iter, manager->thumbnailers);
+      while (thumbnailer == NULL 
+             && g_hash_table_iter_next (&iter, NULL, (gpointer) &thumbnailers))
+        {
+          /* each value in the thumbnailer info hash table has to be a 
+           * valid pointer to a non-empty thumbnailer info list, otherwise
+           * there is a bug */
+          g_assert (thumbnailers != NULL && *thumbnailers != NULL);
+
+          /* get the active (= the first) thumbnailer info in the list */
+          info = (ThumbnailerInfo *)(*thumbnailers)->data;
+
+          /* each element in the thumbnailer info list has to be a valid
+           * thumbnailer info with a specialized thumbnailer object */
+          g_assert (info != NULL);
+          g_assert (TUMBLER_IS_SPECIALIZED_THUMBNAILER (info->thumbnailer));
+
+          /* determine the name of the thumbnailer */
+          current_name = tumbler_specialized_thumbnailer_get_name (
+            TUMBLER_SPECIALIZED_THUMBNAILER (info->thumbnailer));
+
+          /* check if the current thumbnailer matches the override info name */
+          if (g_strcmp0 (name, current_name) == 0)
+            {
+              /* check if the thumbnailer supports the hash key at all */
+              if (tumbler_thumbnailer_supports_hash_key (info->thumbnailer, hash_key))
+                thumbnailer = info->thumbnailer;
+            }
+        }
+    }
+
+  /* update the preferred information of the registry. if no 
+   * thumbnailer was found, this will reset the preferred information
+   * for this hash key */
+  tumbler_registry_set_preferred (manager->registry, hash_key, thumbnailer);
+}
+
+
+
+/**
+ * tumbler_manager_parse_overrides: 
+ * @manager : a #TumblerManager.
+ * @file    : a #GFile that is supposed to be parsed.
+ *
+ * Parses the override sections from @file into a list of 
+ * #OverrideInfo<!---->s, one for each hash key in the @file. The caller
+ * is responsible to free the returned infos with override_info_list_free().
+ *
+ * Return value: A list of #OverrideInfo<!---->s parsed from the input @file.
+ */
+static GList *
+tumbler_manager_parse_overrides (TumblerManager *manager,
+                                 GFile          *file)
+{
+  GKeyFile *key_file;
+  GError   *error = NULL;
+  GList    *overrides = NULL;
+  GFile    *directory;
+  gchar   **sections;
+  gchar    *filename;
+  gchar    *uri_type;
+  guint     n;
+
+  g_return_val_if_fail (TUMBLER_IS_MANAGER (manager), NULL);
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  
+  filename = g_file_get_path (file);
+
+  if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+    {
+      g_free (filename);
+      return NULL;
+    }
+
+  /* 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 NULL;
+    }
+
+  /* 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\" in file \"%s\": "
+                       "Mismatch between section name and UriScheme/MimeType"),
+                     sections[n], filename);
+
+          g_free (uri_type);
+          override_info_free (info);
+          g_free (sections[n]);
+
+          continue;
+        }
+      g_free (uri_type);
+
+      directory = g_file_get_parent (file);
+      info->dir_index = tumbler_manager_get_dir_index (manager, directory);
+      g_object_unref (directory);
+
+      overrides = g_list_prepend (overrides, info);
+
+      g_free (sections[n]);
+    }
+
+  g_free (sections[n]);
+  g_key_file_free (key_file);
+  g_free (filename);
+
+  return overrides;
+}
+
+
+
+/**
+ * tumbler_manager_load_overrides_file:
+ * @manager : a #TumblerManager.
+ * @file    : an overrides GFile to load.
+ *
+ * This function tries to parse @file into a number of override infos, one
+ * for each section in the @file. If this succeeds, it adds each info to 
+ * the correct override info list in the hash table that maps hash keys to
+ * infos. The infos are inserted in sorted order (where the sort key is the
+ * directory index). This ensures that infos from thumbnailer directories
+ * with higher priority always come first.
+ */
+static void
+tumbler_manager_load_overrides_file (TumblerManager *manager,
+                                     GFile          *file)
+{
+  OverrideInfo *info;
+  OverrideInfo *info2;
+  gboolean      first = FALSE;
+  gboolean      inserted = FALSE;
+  GList        *overrides;
+  GList        *lp;
+  GList        *op;
+  GList       **list;
+  GFile        *directory;
+  gchar        *hash_key;
+  gint          dir_index;
+
+  g_return_if_fail (TUMBLER_MANAGER (manager));
+  g_return_if_fail (G_IS_FILE (file));
+
+  /* determine the index of the thumbnailer directory the file is located in */
+  directory = g_file_get_parent (file);
+  dir_index = tumbler_manager_get_dir_index (manager, directory);
+  g_object_unref (directory);
+
+  /* do nothing if the file comes from an unknown directory */
+  if (dir_index < 0)
+    return;
+
+  /* try parsing the file into override infos */
+  overrides = tumbler_manager_parse_overrides (manager, file);
+  
+  /* iterate over all override infos we parsed successfully */
+  for (op = overrides; op != NULL; op = op->next)
+    {
+      info = op->data;
+
+      /* generate the hash key for the current info */
+      hash_key = g_strdup_printf ("%s-%s", info->uri_scheme, info->mime_type);
+
+      /* determine which list to add the info to */
+      list = g_hash_table_lookup (manager->overrides, hash_key);
+
+      /* check if we need to create the list because it doesn't exist yet */
+      if (list == NULL)
+        {
+          /* allocate a pointer to a list */
+          list = g_slice_alloc0 (sizeof (GList **));
+
+          /* add the info to the list */
+          *list = g_list_prepend (*list, info);
+
+          /* insert the list into the hash table */
+          g_hash_table_insert (manager->overrides, hash_key, list);
+
+          /* update the preferred information for this hash key, as we now
+           * have a new info at the very beginning of the list */
+          tumbler_manager_update_preferred (manager, hash_key);
+        }
+      else
+        {
+          g_free (hash_key);
+
+          first = FALSE;
+          inserted = FALSE;
+
+          /* find the right place in the list to insert the info and insert it
+           * if the list is non-empty */
+          for (lp = *list; !inserted && lp != NULL; lp = lp->next)
+            {
+              info2 = lp->data;
+
+              /* we should NEVER have two sections with the same hash key in the
+               * same overrides file. ideally, we'd make use of the assertion below
+               * but since people are doomed to make mistakes, we're simply ignoring
+               * duplicate sections here and give priority to those coming earlier
+               * in the list */
+#if 0
+              g_assert (info2->dir_index != dir_index);
+#endif
+
+              if (info2->dir_index > dir_index)
+                {
+                  if (lp == *list)
+                    first = TRUE;
+
+                  *list = g_list_insert_before (*list, lp, info);
+
+                  inserted = TRUE;
+                }
+            }
+
+          if (!inserted)
+            *list = g_list_append (*list, info);
+
+          if (first)
+            {
+              /* update the preferred information for this hash key, as we now
+               * have a new info at the very beginning of the list */
+              tumbler_manager_update_preferred (manager, hash_key);
+            }
+        }
+    }
+
+  g_list_free (overrides);
+}
+
+
+
+/**
+ * tumbler_manager_load_overrides:
+ * @manager : a #TumblerManager.
+ *
+ * Attempts to load the override infos of all overrides files in known
+ * thumbnailer direcotories into the hash key -> override info list
+ * hash table.
+ */
+static void
+tumbler_manager_load_overrides (TumblerManager *manager)
+{
+  GFileType type;
+  GFile    *file;
+  GList    *lp;
+
+  g_return_if_fail (TUMBLER_IS_MANAGER (manager));
+
+  /* iterate over all thumbnailer directories */
+  for (lp = manager->directories; lp != NULL; lp = lp->next)
+    {
+      /* build the filename for the overrides file in this directory */
+      file = g_file_get_child (lp->data, "overrides");
+
+      /* query the file type. we can only parse regular files */
+      type = g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL);
+
+      /* if the file is regular, try to load it */
+      if (type == G_FILE_TYPE_REGULAR)
+        tumbler_manager_load_overrides_file (manager, file);
+
+      /* we no longer need the file object */
+      g_object_unref (file);
+    }
+
+#ifdef DEBUG
+  dump_overrides (manager);
+#endif
+}
+
+
+
+/**
+ * tumbler_manager_unload_overrides_file:
+ * @manager : a #TumblerManager.
+ * @file    : the overrides #GFile to unload.
+ *
+ * This removes all override infos belonging to this @file from the
+ * hash key -> override info list hash table. It does so by computing
+ * the directory index of the @file and matching it against all override
+ * infos. Override infos that have the same directory index are removed.
+ *
+ * Whenever the first element in one of the override info lists is removed,
+ * the preferred information of the registry is updated for that particular
+ * hash key.
+ *
+ * The function is supposed to be called when an overrides file is deleted
+ * from the hard disk and we need to synchronize our internal 
+ * representation.
+ */
+static void
+tumbler_manager_unload_overrides_file (TumblerManager *manager,
+                                       GFile          *file)
+{
+  GHashTableIter iter;
+  OverrideInfo  *info;
+  const gchar   *hash_key;
+  gboolean       first;
+  GFile         *directory;
+  GList         *lp;
+  GList        **overrides;
+  gint           dir_index;
+
+  g_return_if_fail (TUMBLER_IS_MANAGER (manager));
+  g_return_if_fail (G_IS_FILE (file));
+
+  /* compute the directory index for the overrides file */
+  directory = g_file_get_parent (file);
+  dir_index = tumbler_manager_get_dir_index (manager, directory);
+  g_object_unref (directory);
+
+  /* do nothing if the overrides file doesn't come from one of our known
+   * thumbnailer directories */
+  if (dir_index < 0)
+    return;
+
+  /* iterate over all (hash key, override info list) pairs from the hash table */
+  g_hash_table_iter_init (&iter, manager->overrides);
+  while (g_hash_table_iter_next (&iter, (gpointer) &hash_key, (gpointer) &overrides))
+    {
+      first = FALSE;
+
+      /* all values in the hash table should be valid pointers to non-empty
+       * override info lists, otherwise there's a bug in the code */
+      g_assert (overrides != NULL);
+      g_assert (*overrides != NULL);
+
+      /* iterate over all override infos in the current list */
+      for (lp = *overrides; lp != NULL; lp = lp->next)
+        {
+          info = (OverrideInfo *)lp->data;
+
+          /* there should be no NULL elements in the list, otherwise... bug! */
+          g_assert (info != NULL);
+
+          /* check if the info has the same directory index as the overrides
+           * file and thus, belongs to that file */
+          if (info->dir_index == dir_index)
+            {
+              /* if this is true and the info is first in the list, we need
+               * to update the preferred thumbnailer for this hash key later */
+              if (lp == *overrides)
+                first = TRUE;
+
+              /* destroy the override info */
+              override_info_free (info);
+
+              /* remove the element from the list */
+              *overrides = g_list_delete_link (*overrides, lp);
+
+              /* we assume there's only one override info with the same hash key
+               * coming from the same file, so we can stop searching here */
+              break;
+            }
+        }
+
+      /* if there is no element left after removing the matching ones, we
+       * need to remove the entire list from the hash table */
+      if (*overrides == NULL)
+        g_hash_table_remove (manager->overrides, hash_key);
+
+      /* if we removed an info from the list and it was the first one, we
+       * need to update the preferred thumbnailer for this hash key now */
+      if (first)
+        tumbler_manager_update_preferred (manager, hash_key);
+    }
+}
+
+
+
+/**
+ * tumbler_manager_load_thumbnailer:
+ * @manager : a #TumblerManager.
+ * @file    : the #GFile to load thumbnailer information from.
+ *
+ * Attempts to load information about a permanently installed specialized
+ * thumbnailer from the @file. On success, the resulting thumbnailer info
+ * is added to the basename -> thumbnailer info list hash table. If it is
+ * inserted as the first element of that list, the preferred thumbnailer
+ * for all hash keys supported by the thumbnailer is updated.
+ */
+static void
+tumbler_manager_load_thumbnailer (TumblerManager *manager,
+                                  GFile          *file)
+{
+  ThumbnailerInfo *info;
+  ThumbnailerInfo *info2;
+  struct stat      file_stat;
+  GKeyFile        *key_file;
+  gboolean         first = FALSE;
+  gboolean         inserted = FALSE;
+  GError          *error = NULL;
+  GFile           *directory;
+  GList          **list;
+  GList           *lp;
+  GStrv            hash_keys;
+  gchar           *base_name;
+  gchar           *filename;
+  gchar           *name;
+  gchar           *object_path;
+  gchar          **uri_schemes;
+  gchar          **mime_types;
+  guint            n;
+
+  g_return_if_fail (TUMBLER_IS_MANAGER (manager));
+  g_return_if_fail (G_IS_FILE (file));
+
+  /* determine the absolute filename of the input file */
+  filename = g_file_get_path (file);
+
+  /* allocate a new key file object */
+  key_file = g_key_file_new ();
+
+  /* try to load the key file data from the input 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 the name of the specialized thumbnailer */
+  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);
+
+      return;
+    }
+
+  /* determine the D-Bus object path of the specialized thumbnailer */
+  object_path = g_key_file_get_string (key_file, "Specialized Thumbnailer", 
+                                       "ObjectPath", &error);
+  if (object_path == NULL)
+    {
+      g_warning (_("Malformed file \"%s\": %s"), filename, error->message);
+      g_clear_error (&error);
+
+      g_key_file_free (key_file);
+      g_free (filename);
+
+      return;
+    }
+
+  /* determine the MIME types supported by this thumbnailer */
+  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 (object_path);
+      g_free (name);
+      g_key_file_free (key_file);
+      g_free (filename);
+
+      return;
+    }
+
+  /* determine the URI schemes supported by this thumbnailer */
+  uri_schemes = g_key_file_get_string_list (key_file, "Specialized Thumbnailer",
+                                            "UriSchemes", NULL, &error);
+
+  /* if no URI schemes are specified, we default to the "file" scheme */
+  if (uri_schemes == NULL)
+    {
+      uri_schemes = g_new0 (gchar *, 2);
+      uri_schemes[0] = g_strdup ("file");
+      uri_schemes[1] = NULL;
+    }
+
+  /* determine the time the file was last modified */
+  if (g_stat (filename, &file_stat) != 0)
+    {
+      g_warning (_("Failed to determine last modified time of \"%s\""), filename); 
+
+      g_strfreev (uri_schemes);
+      g_strfreev (mime_types);
+      g_free (object_path);
+      g_free (name);
+      g_key_file_free (key_file);
+      g_free (filename);
+
+      return;
+    }
+
+  /* allocate a new thumbnailer info */
+  info = thumbnailer_info_new ();
+#ifdef DEBUG
+  info->file = g_object_ref (file);
+#endif
+
+  /* compute the directory index of the input file */
+  directory = g_file_get_parent (file);
+  info->dir_index = tumbler_manager_get_dir_index (manager, directory);
+  g_object_unref (directory);
+
+  /* create a new specialized thumbnailer object and pass all the required
+   * information on to it */
+  info->thumbnailer = 
+    tumbler_specialized_thumbnailer_new (manager->connection, name, object_path,
+                                         (const gchar *const *)uri_schemes,
+                                         (const gchar *const *)mime_types,
+                                         file_stat.st_mtime);
+
+  /* free stuff */
+  g_strfreev (uri_schemes);
+  g_strfreev (mime_types);
+  g_free (object_path);
+  g_free (name);
+  g_key_file_free (key_file);
+  g_free (filename);
+  
+  /* determine the basename of the file */
+  base_name = g_file_get_basename (file);
+
+  /* determine the list to insert the thumbnailer info in */
+  list = g_hash_table_lookup (manager->thumbnailers, base_name);
+
+  first = FALSE;
+
+  /* check if we need to create a new list */
+  if (list == NULL)
+    {
+      /* allocate memory for a pointer to the list */
+      list = g_slice_alloc0 (sizeof (GList **));
+
+      /* add the thumbnailer info to the new list */
+      *list = g_list_prepend (*list, info);
+
+      /* add the list to the hash table */
+      g_hash_table_insert (manager->thumbnailers, base_name, list);
+
+      /* the new thumbnailer info is first in the new list, so we need to 
+       * update the preferred thumbnailer for all hash keys supported by
+       * it */
+      first = TRUE;
+    }
+  else
+    {
+      /* free the basename */
+      g_free (base_name);
+
+      inserted = FALSE;
+
+      /* iterate over all infos in the current list */
+      for (lp = *list; !inserted && lp != NULL; lp = lp->next)
+        {
+          info2 = lp->data;
+
+          /* we should never have two thumbnailer infos coming from the
+           * same file, otherwise there'd be a bug in this program */
+          g_assert (info2->dir_index != info->dir_index);
+
+          /* check if we need to insert the thumbnailer info before the
+           * current item in the list */
+          if (info2->dir_index > info->dir_index)
+            {
+              /* if the current item is the first in the list, we'll have
+               * to update the preferred thumbnailer for all hash keys 
+               * supported by the new info later */
+              if (lp == *list)
+                first = TRUE;
+
+              /* insert the new thumbnailer info into the list */
+              *list = g_list_insert_before (*list, lp, info);
+
+              /* we're done */
+              inserted = TRUE;
+            }
+        }
+
+      /* if we couldn't find an info with a dir index of lower priority, we
+       * have to append the new info to the list */
+      if (!inserted)
+        *list = g_list_append (*list, info);
+    }
+
+  /* check if we need to update the preferred thumbnailer for the hash keys
+   * supported by the new thumbnailer info */
+  if (first)
+    {
+      /* check if there is another element in the list. that one was the first
+       * info in the list before, thus its thumbnailer object should've been added 
+       * to the registry. we now have to remove it and replace it by the new first 
+       * info */
+      if ((*list)->next != NULL)
+        {
+          info2 = (*list)->next->data;
+          tumbler_registry_remove (manager->registry, info2->thumbnailer);
+        }
+
+      /* add the new thumbnailer to the registry */
+      tumbler_registry_add (manager->registry, info->thumbnailer);
+
+      /* determine all hash keys supported by the new thumbnailer info */
+      hash_keys = tumbler_thumbnailer_get_hash_keys (info->thumbnailer);
+
+      /* update the preferred thumbnailer for each hash key */
+      for (n = 0; hash_keys != NULL && hash_keys[n] != NULL; ++n)
+        {
+          /* TODO we could check if an update is needed here */
+          tumbler_manager_update_preferred (manager, hash_keys[n]);
+        }
+
+      /* free the hash key array */
+      g_strfreev (hash_keys);
+    }
+}
+
+
+
+/**
+ * tumbler_manager_load_thumbnailers:
+ * @manager   : a #TumblerManager.
+ * @directory : #GFile pointing to a thumbnailer directory.
+ *
+ * Attempts to load the thumbnailer .service files from all known
+ * thumbnailer directories. The resulting thumbnailer infos are then
+ * inserted into the basename -> thumbnailer info list hash table.
+ *
+ * The preferred thumbnailer for affected hash keys are updated
+ * whenever needed.
+ *
+ * TODO: As an optimization we could add a boolean update_preferred
+ * parameter to tumbler_manager_load_thumbnailer to avoid updating
+ * the preferred thumbnailer repeatedly. We could then only update
+ * the preferred thumbnailers of all hash keys at once.
+ */
+static void
+tumbler_manager_load_thumbnailers (TumblerManager *manager,
+                                   GFile          *directory)
+{
+  const gchar *base_name;
+  GFileType    type;
+  GFile       *file;
+  gchar       *dirname;
+  GDir        *dir;
+
+  g_return_if_fail (TUMBLER_IS_MANAGER (manager));
+  g_return_if_fail (G_IS_FILE (directory));
+
+  /* determine the absolute path to the directory */
+  dirname = g_file_get_path (directory);
+
+  /* try to open the directory for reading */
+  dir = g_dir_open (dirname, 0, NULL);
+  if (dir == NULL)
+    {
+      g_free (dirname);
+      return;
+    }
+
+  /* iterate over all files in the directory */
+  for (base_name = g_dir_read_name (dir); 
+       base_name != NULL; 
+       base_name = g_dir_read_name (dir))
+    {
+      /* skip files that don't end with the .service extension */
+      if (!g_str_has_suffix (base_name, ".service"))
+        continue;
+
+      /* create a file object for the service file and query it's type */
+      file = g_file_get_child (directory, base_name);
+      type = g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL);
+
+      /* try to load the file if it is regular */
+      if (type == G_FILE_TYPE_REGULAR)
+        tumbler_manager_load_thumbnailer (manager, file);
+
+      /* we no longer need the file object */
+      g_object_unref (file);
+    }
+
+  /* close the directory handle */
+  g_dir_close (dir);
+
+  /* free memory used for the directory path */
+  g_free (dirname);
+}
+
+
+
+/**
+ * tumbler_manager_load:
+ * @manager : a #TumblerManager.
+ *
+ * This function should only be called once. It initializes the thumbnailer
+ * directory objects, directory monitors, override infos and thumbnailer 
+ * infos. 
+ */
+static void
+tumbler_manager_load (TumblerManager *manager)
+{
+  const gchar *const *data_dirs;
+  GFileMonitor       *monitor;
+  GList              *directories = NULL;
+  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 (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);
+    }
+
+  /* reverse the directory list so that the directories with highest 
+   * priority come first */
+  directories = g_list_reverse (directories);
+
+  g_mutex_lock (manager->mutex);
+
+  /* pass the ownership of the directories list to the manager */
+  manager->directories = directories;
+  manager->monitors = NULL;
+
+  /* update the thumbnailer cache */
+  for (iter = manager->directories; iter != NULL; iter = iter->next)
+    tumbler_manager_load_thumbnailers (manager, iter->data);
+
+#ifdef DEBUG
+  dump_thumbnailers (manager);
+#endif
+
+  /* update the overrides cache */
+  tumbler_manager_load_overrides (manager);
+
+  /* update the supported information */
+  tumbler_registry_update_supported (manager->registry);
+
+  /* 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);
+}
+
+
+
+/**
+ * tumbler_manager_thumbnailer_file_deleted:
+ * @manager : a #TumblerManager.
+ * @file    : the #GFile pointing to the service file that was deleted.
+ *
+ * This function should be called when a thumbnailer service file has
+ * been deleted and our internal representation needs to be updated.
+ * 
+ * If the corresponding thumbnailer info is the first in its basename
+ * list, the preferred thumbnailers for all affected hash keys need
+ * to be updated, and the corresponding thumbnailer has to be removed
+ * from the registry and replaced by the next one in the list.
+ */
+static void
+tumbler_manager_thumbnailer_file_deleted (TumblerManager *manager,
+                                          GFile          *file)
+{
+  ThumbnailerInfo *info;
+  ThumbnailerInfo *info2;
+  GFile           *directory;
+  GList          **list;
+  GList           *lp;
+  GStrv            hash_keys;
+  gchar           *base_name;
+  guint            n;
+  gint             dir_index;
+
+  g_return_if_fail (TUMBLER_IS_MANAGER (manager));
+  g_return_if_fail (G_IS_FILE (file));
+
+  /* compute the directory index for the thumbnailer file */
+  directory = g_file_get_parent (file);
+  dir_index = tumbler_manager_get_dir_index (manager, directory);
+  g_object_unref (directory);
+
+  /* ignore the service file if it does not come from a know thumbnailer
+   * directory */
+  if (dir_index < 0)
+    return;
+
+  /* look up the basename thumbnailer info list for this file */
+  base_name = g_file_get_basename (file);
+  list = g_hash_table_lookup (manager->thumbnailers, base_name);
+  g_free (base_name);
+
+  /* ignore the service file if there is no thumbnailer info for this
+   * basename */
+  if (list == NULL)
+    return;
+
+  /* if we have a list it has to be non-empty, otherwise there is a bug
+   * in this program */
+  g_assert (*list != NULL);
+
+  /* iterate over all thumbnailer infos in the list */
+  for (lp = *list; lp != NULL; lp = lp->next)
+    {
+      info = lp->data;
+
+      /* check if the current info comes from the thumbnailer file that was 
+       * deleted */
+      if (info->dir_index == dir_index)
+        {
+          /* check if the thumbnailer info is the first in the list */
+          if (lp == *list)
+            {
+              /* remove the thumbnailer info from the list */
+              *list = g_list_delete_link (*list, lp);
+
+              /* remove the corresponding thumbnailer from the registry */
+              tumbler_registry_remove (manager->registry, info->thumbnailer);
+
+              /* if the list is non-empty after we've removed the info we need
+               * to add the new first thumbnailer to the registry */
+              if (*list != NULL)
+                {
+                  info2 = (*list)->data;
+
+                  /* there should be no NULL element in the list */
+                  g_assert (info2 != NULL);
+
+                  /* add the new first thumbnailer to the registry */
+                  tumbler_registry_add (manager->registry, info2->thumbnailer);
+                }
+
+              /* determine all hash kayes supported by the info we're about to
+               * destroy */
+              hash_keys = tumbler_thumbnailer_get_hash_keys (info->thumbnailer);
+
+              /* update the preferred thumbnailer for all these hash keys */
+              for (n = 0; hash_keys != NULL && hash_keys[n] != NULL; ++n)
+                {
+                  /* TODO we could check if an update is needed here */
+                  tumbler_manager_update_preferred (manager, hash_keys[n]);
+                }
+
+              /* free the hash keys array */
+              g_strfreev (hash_keys);
+            }
+          else
+            {
+              /* we're not first in the list, so we can simply remove the
+               * info from the list */
+              *list = g_list_delete_link (*list, lp);
+            }
+              
+          /* the info was removed from the list and the registry was updated,
+           * so destroy it now */
+          thumbnailer_info_free (info);
+
+          /* we've found the thumbnailer info for the file, so we can stop
+           * searching now */
+          break;
+        }
+    }
+}
+
+
+
+/**
+ * tumbler_manager_directory_created:
+ * @manager   : a #TumblerManager.
+ * @directory : a #GFile pointing to the created thumbnailer directory.
+ *
+ * This function should be called when a new thumbnailer directory is 
+ * created and the registry needs to be updated. All thumbnailers and 
+ * overrides information defined in this directory are loaded and
+ * the registry is updated.
+ */
+static void
+tumbler_manager_directory_created (TumblerManager *manager,
+                                   GFile          *directory,
+                                   gint            dir_index)
+{
+  GFile *file;
+
+  g_return_if_fail (TUMBLER_IS_MANAGER (manager));
+  g_return_if_fail (G_IS_FILE (directory));
+  g_return_if_fail (dir_index >= 0);
+
+  /* try to load all thumbnailers in this directory */
+  tumbler_manager_load_thumbnailers (manager, directory);
+
+  /* try to load the overrides file in this directory */
+  file = g_file_get_child (directory, "overrides");
+  tumbler_manager_load_overrides_file (manager, file);
+  g_object_unref (file);
+}
+
+
+
+/**
+ * tumbler_manager_directory_deleted:
+ * @manager   : a #TumblerManager.
+ * @directory : a #GFile pointing to the deleted thumbnailer directory.
+ *
+ * This function should be called when a thumbnailer directory is deleted.
+ * All thumbnailer and override infos from this directory are destroyed, 
+ * preferred thumbnailers updated and thumbnailers removed/added from the
+ * registry.
+ */
+static void
+tumbler_manager_directory_deleted (TumblerManager *manager,
+                                   GFile          *directory,
+                                   gint            dir_index)
+{
+  ThumbnailerInfo *info;
+  ThumbnailerInfo *info2;
+  GHashTableIter   iter;
+  GFile           *file;
+  GList          **list;
+  GList           *lp;
+  GStrv            hash_keys;
+  guint            n;
+
+  g_return_if_fail (TUMBLER_IS_MANAGER (manager));
+  g_return_if_fail (G_IS_FILE (directory));
+  g_return_if_fail (dir_index >= 0);
+
+  /* unload the overrides file in this directory */
+  file = g_file_get_child (directory, "overrides");
+  tumbler_manager_unload_overrides_file (manager, file);
+  g_object_unref (file);
+
+  /* iterate over all thumbnailer info lists */
+  g_hash_table_iter_init (&iter, manager->thumbnailers);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer) &list))
+    {
+      /* all lists in the hash table should be defined and non-empty */
+      g_assert (list != NULL);
+      g_assert (*list != NULL);
+
+      /* iterate over all thumbnailers in the current list */
+      for (lp = *list; lp != NULL; lp = lp->next)
+        {
+          info = lp->data;
+
+          /* check if the thumbnailer info comes from the deleted directory */
+          if (info->dir_index == dir_index)
+            {
+              /* check if the thumbnailer info is the first in the list. if this
+               * is the case we need to remove it from the registry and replace it
+               * with the second element in the list (if available) */
+              if (lp == *list)
+                {
+                  /* remove the info from the list */
+                  *list = g_list_delete_link (*list, lp);
+
+                  /* remove the corresponding thumbnailer from the registry */
+                  tumbler_registry_remove (manager->registry, info->thumbnailer);
+
+                  /* if the list is non-empty after removing the info, we need to
+                   * add the new first thumbnailer to the registry */
+                  if (*list != NULL)
+                    {
+                      info2 = (*list)->data;
+
+                      /* all infos in the list should be non-NULL */
+                      g_assert (info2 != NULL);
+
+                      /* add the new first thumbnailer to the registry */
+                      tumbler_registry_add (manager->registry, info2->thumbnailer);
+                    }
+
+                  /* determine all hash keys supported by the removed thumbnailer info */
+                  hash_keys = tumbler_thumbnailer_get_hash_keys (info->thumbnailer);
+
+                  /* update the preferred thumbnailer for all these hash keys */
+                  for (n = 0; hash_keys != NULL && hash_keys[n] != NULL; ++n)
+                    {
+                      /* TODO we could check if an update is needed here */
+                      tumbler_manager_update_preferred (manager, hash_keys[n]);
+                    }
+
+                  /* free the hash keys array */
+                  g_strfreev (hash_keys);
+                }
+              else
+                {
+                  /* the info is not the first in the list, so we can simply
+                   * remove it from the list */
+                  *list = g_list_delete_link (*list, lp);
+                }
+                  
+              /* destroy the thumbnailer info */
+              thumbnailer_info_free (info);
+            }
+        }
+    }
+}
+
+
+
+/**
+ * tumbler_manager_directory_changed:
+ * @manager    : a #TumblerManager.
+ * @file       : 
+ * @other_file :
+ * @event_type :
+ * @monitor    :
+ *
+ * TODO We can probably optimize the locking a little bit.
+ */
+static void
+tumbler_manager_directory_changed (TumblerManager   *manager,
+                                   GFile            *file,
+                                   GFile            *other_file,
+                                   GFileMonitorEvent event_type,
+                                   GFileMonitor     *monitor)
+{
+  GFileType type;
+  gchar    *base_name;
+  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));
+
+#ifdef DEBUG
+  g_print ("Directory (contents) changed\n\n");
+#endif
+
+  if (event_type == G_FILE_MONITOR_EVENT_DELETED)
+    {
+      base_name = g_file_get_basename (file);
+
+      if (g_strcmp0 (base_name, "overrides") == 0)
+        {
+          g_mutex_lock (manager->mutex);
+          tumbler_manager_unload_overrides_file (manager, file);
+          tumbler_registry_update_supported (manager->registry);
+#ifdef DEBUG
+          dump_overrides (manager);
+#endif
+          g_mutex_unlock (manager->mutex);
+        }
+      else if (g_str_has_suffix (base_name, ".service"))
+        {
+          g_mutex_lock (manager->mutex);
+          tumbler_manager_thumbnailer_file_deleted (manager, file);
+          tumbler_registry_update_supported (manager->registry);
+#ifdef DEBUG
+          dump_thumbnailers (manager);
+#endif
+          g_mutex_unlock (manager->mutex);
+        }
+      else
+        {
+          g_mutex_lock (manager->mutex);
+          dir_index = tumbler_manager_get_dir_index (manager, file);
+          g_mutex_unlock (manager->mutex);
+
+          if (dir_index >= 0)
+            {
+              g_mutex_lock (manager->mutex);
+              tumbler_manager_directory_deleted (manager, file, dir_index);
+              tumbler_registry_update_supported (manager->registry);
+#ifdef DEBUG
+              dump_overrides (manager);
+              dump_thumbnailers (manager);
+#endif
+              g_mutex_unlock (manager->mutex);
+            }
+        }
+    }
+  else 
+    {
+      type = g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL);
+
+      if (type == G_FILE_TYPE_REGULAR)
+        {
+          base_name = g_file_get_basename (file);
+
+          if (g_strcmp0 (base_name, "overrides") == 0)
+            {
+              if (event_type == G_FILE_MONITOR_EVENT_CREATED)
+                {
+                  g_mutex_lock (manager->mutex);
+                  tumbler_manager_load_overrides_file (manager, file);
+                  tumbler_registry_update_supported (manager->registry);
+#ifdef DEBUG
+                  dump_overrides (manager);
+#endif
+                  g_mutex_unlock (manager->mutex);
+                }
+            }
+          else if (g_str_has_suffix (base_name, ".service"))
+            {
+              if (event_type == G_FILE_MONITOR_EVENT_CREATED)
+                {
+                  g_mutex_lock (manager->mutex);
+                  tumbler_manager_load_thumbnailer (manager, file);
+                  tumbler_registry_update_supported (manager->registry);
+#ifdef DEBUG
+                  dump_thumbnailers (manager);
+#endif
+                  g_mutex_unlock (manager->mutex);
+                }
+            }
+        }
+      else
+        {
+          g_mutex_lock (manager->mutex);
+          dir_index = tumbler_manager_get_dir_index (manager, file);
+          g_mutex_unlock (manager->mutex);
+
+          if (dir_index >= 0)
+            {
+              g_mutex_lock (manager->mutex);
+              tumbler_manager_directory_created (manager, file, dir_index);
+              tumbler_registry_update_supported (manager->registry);
+#ifdef DEBUG
+              dump_overrides (manager);
+              dump_thumbnailers (manager);
+#endif
+              g_mutex_unlock (manager->mutex);
+            }
+        }
+    }
+}
+
+
+
+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 void
+override_info_list_free (gpointer pointer)
+{
+  GList **infos = pointer;
+  GList  *iter;
+
+  for (iter = *infos; iter != NULL; iter = iter->next)
+    override_info_free (iter->data);
+
+  g_list_free (*infos);
+  g_slice_free1 (sizeof (GList **), infos);
+}
+
+
+
+static ThumbnailerInfo *
+thumbnailer_info_new (void)
+{
+  return g_slice_new0 (ThumbnailerInfo);
+}
+
+
+
+static void
+thumbnailer_info_free (ThumbnailerInfo *info)
+{
+  if (info == NULL)
+    return;
+
+#ifdef DEBUG
+  g_object_unref (info->file);
+#endif
+  g_object_unref (info->thumbnailer);
+  g_slice_free (ThumbnailerInfo, info);
+}
+
+
+
+static void
+thumbnailer_info_list_free (gpointer pointer)
+{
+  GList **infos = pointer;
+  GList  *iter;
+
+  for (iter = *infos; iter != NULL; iter = iter->next)
+    thumbnailer_info_free (iter->data);
+
+  g_list_free (*infos);
+  g_slice_free1 (sizeof (GList **), infos);
+}
+
+
+
+#ifdef DEBUG
+static void
+dump_overrides (TumblerManager *manager)
+{
+  GHashTableIter table_iter;
+  const gchar   *hash_key;
+  GList        **list;
+  GList         *iter;
+
+  g_print ("Overrides:\n");
+
+  g_hash_table_iter_init (&table_iter, manager->overrides);
+  while (g_hash_table_iter_next (&table_iter, (gpointer) &hash_key, (gpointer) &list))
+    {
+      g_print ("  %s:\n", hash_key);
+      for (iter = *list; iter != NULL; iter = iter->next)
+        g_print ("    %s\n", ((OverrideInfo *)iter->data)->name);
+    }
+
+  g_print ("\n");
+}
+
+
+
+static void
+dump_thumbnailers (TumblerManager *manager)
+{
+  GHashTableIter table_iter;
+  const gchar   *base_name;
+  GList        **list;
+  GList         *iter;
+
+  g_print ("Thumbnailers:\n");
+
+  g_hash_table_iter_init (&table_iter, manager->thumbnailers);
+  while (g_hash_table_iter_next (&table_iter, (gpointer) &base_name, (gpointer) &list))
+    {
+      g_print ("  %s:\n", base_name);
+      for (iter = *list; iter != NULL; iter = iter->next)
+        g_print ("    %s\n", g_file_get_path (((ThumbnailerInfo *)iter->data)->file));
+    }
+
+  g_print ("\n");
+}
+#endif /* DEBUG */
 
 
 
@@ -247,6 +1793,9 @@ tumbler_manager_start (TumblerManager *manager,
 
   g_mutex_unlock (manager->mutex);
 
+  /* load thumbnailers installed into the system permanently */
+  tumbler_manager_load (manager);
+
   /* this is how I roll */
   return TRUE;
 }
@@ -255,24 +1804,24 @@ tumbler_manager_start (TumblerManager *manager,
 
 void
 tumbler_manager_register (TumblerManager        *manager, 
-                          gchar                 *uri_scheme, 
-                          gchar                 *mime_type, 
+                          const gchar *const    *uri_schemes, 
+                          const gchar *const    *mime_types, 
                           DBusGMethodInvocation *context)
 {
   TumblerThumbnailer *thumbnailer;
   gchar              *sender_name;
 
   dbus_async_return_if_fail (TUMBLER_IS_MANAGER (manager), context);
-  dbus_async_return_if_fail (uri_scheme != NULL, context);
-  dbus_async_return_if_fail (mime_type != NULL, context);
+  dbus_async_return_if_fail (uri_schemes != NULL, context);
+  dbus_async_return_if_fail (mime_types != NULL, context);
 
   sender_name = dbus_g_method_get_sender (context);
 
   g_mutex_lock (manager->mutex);
 
   thumbnailer = tumbler_specialized_thumbnailer_new_foreign (manager->connection,
-                                                             sender_name, uri_scheme, 
-                                                             mime_type);
+                                                             sender_name, uri_schemes, 
+                                                             mime_types);
 
   tumbler_registry_add (manager->registry, thumbnailer);
 
@@ -284,4 +1833,3 @@ tumbler_manager_register (TumblerManager        *manager,
 
   dbus_g_method_return (context);
 }
-
diff --git a/tumblerd/tumbler-manager.h b/tumblerd/tumbler-manager.h
index de02186..0f79f6e 100644
--- a/tumblerd/tumbler-manager.h
+++ b/tumblerd/tumbler-manager.h
@@ -44,8 +44,8 @@ TumblerManager *tumbler_manager_new           (DBusGConnection       *connection
 gboolean        tumbler_manager_start         (TumblerManager        *manager,
                                                GError               **error);
 void            tumbler_manager_register      (TumblerManager        *manager, 
-                                               gchar                 *uri_scheme, 
-                                               gchar                 *mime_type, 
+                                               const gchar *const    *uri_schemes, 
+                                               const gchar *const    *mime_types, 
                                                DBusGMethodInvocation *context);
 void            tumbler_manager_get_supported (TumblerManager        *manager, 
                                                DBusGMethodInvocation *context);
diff --git a/tumblerd/tumbler-registry.c b/tumblerd/tumbler-registry.c
index b4f28d9..9450e6a 100644
--- a/tumblerd/tumbler-registry.c
+++ b/tumblerd/tumbler-registry.c
@@ -32,28 +32,10 @@
 
 
 
-/* Property identifiers */
-enum
-{
-  PROP_0,
-};
-
-
-
 static void         tumbler_registry_finalize                  (GObject            *object);
-static void         tumbler_registry_get_property              (GObject            *object,
-                                                                guint               prop_id,
-                                                                GValue             *value,
-                                                                GParamSpec         *pspec);
-static void         tumbler_registry_set_property              (GObject            *object,
-                                                                guint               prop_id,
-                                                                const GValue       *value,
-                                                                GParamSpec         *pspec);
-static void         tumbler_registry_remove                    (const gchar        *key,
+static void         tumbler_registry_remove_thumbnailer        (const gchar        *key,
                                                                 GList             **list,
                                                                 TumblerThumbnailer *thumbnailer);
-static void         tumbler_registry_unregister                (TumblerThumbnailer *thumbnailer,
-                                                                TumblerRegistry    *registry);
 static void         tumbler_registry_list_free                 (gpointer            data);
 static GList       *tumbler_registry_get_thumbnailers_internal (TumblerRegistry    *registry);
 static gint         tumbler_registry_compare                   (TumblerThumbnailer *a,
@@ -73,6 +55,7 @@ struct _TumblerRegistry
   GObject       __parent__;
 
   GHashTable   *thumbnailers;
+  GHashTable   *preferred_thumbnailers;
   GMutex       *mutex;
 
   gchar       **uri_schemes;
@@ -92,8 +75,6 @@ tumbler_registry_class_init (TumblerRegistryClass *klass)
 
   gobject_class = G_OBJECT_CLASS (klass);
   gobject_class->finalize = tumbler_registry_finalize; 
-  gobject_class->get_property = tumbler_registry_get_property;
-  gobject_class->set_property = tumbler_registry_set_property;
 }
 
 
@@ -104,6 +85,8 @@ tumbler_registry_init (TumblerRegistry *registry)
   registry->mutex = g_mutex_new ();
   registry->thumbnailers = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                   g_free, tumbler_registry_list_free);
+  registry->preferred_thumbnailers = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                            g_free, g_object_unref);
 }
 
 
@@ -114,6 +97,7 @@ tumbler_registry_finalize (GObject *object)
   TumblerRegistry *registry = TUMBLER_REGISTRY (object);
 
   /* release all thumbnailers */
+  g_hash_table_unref (registry->preferred_thumbnailers);
   g_hash_table_unref (registry->thumbnailers);
 
   /* free the cached URI schemes and MIME types */
@@ -128,80 +112,66 @@ tumbler_registry_finalize (GObject *object)
 
 
 
+#ifdef DEBUG
 static void
-tumbler_registry_get_property (GObject    *object,
-                               guint       prop_id,
-                               GValue     *value,
-                               GParamSpec *pspec)
+dump_registry (TumblerRegistry *registry)
 {
-#if 0
-  TumblerRegistry *registry = TUMBLER_REGISTRY (object);
-#endif
+  TumblerThumbnailer *thumbnailer;
+  GHashTableIter      iter;
+  const gchar        *hash_key;
+  GList             **thumbnailers;
+  GList              *lp;
+
+  g_print ("Registry:\n");
 
-  switch (prop_id)
+  g_print ("  Preferred Thumbnailers:\n");
+  g_hash_table_iter_init (&iter, registry->preferred_thumbnailers);
+  while (g_hash_table_iter_next (&iter, (gpointer) &hash_key, (gpointer) &thumbnailer))
     {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
+      g_print ("    %s: %s\n", hash_key,
+               tumbler_specialized_thumbnailer_get_name (TUMBLER_SPECIALIZED_THUMBNAILER (thumbnailer)));
     }
-}
 
-
-
-static void
-tumbler_registry_set_property (GObject      *object,
-                               guint         prop_id,
-                               const GValue *value,
-                               GParamSpec   *pspec)
-{
-#if 0
-  TumblerRegistry *registry = TUMBLER_REGISTRY (object);
-#endif
-
-  switch (prop_id)
+  g_print ("  Registry Thumbnailers:\n");
+  g_hash_table_iter_init (&iter, registry->thumbnailers);
+  while (g_hash_table_iter_next (&iter, (gpointer) &hash_key, (gpointer) &thumbnailers))
     {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
+      for (lp = *thumbnailers; lp != NULL; lp = lp->next)
+        {
+          if (TUMBLER_IS_SPECIALIZED_THUMBNAILER (lp->data))
+            {
+              g_print ("    %s: %s\n", 
+                       hash_key, tumbler_specialized_thumbnailer_get_name (lp->data));
+            }
+        }
     }
+
+  g_print ("\n");
 }
+#endif
 
 
 
 static void
-tumbler_registry_remove (const gchar        *key,
-                         GList             **list,
-                         TumblerThumbnailer *thumbnailer)
+tumbler_registry_remove_thumbnailer (const gchar        *key,
+                                     GList             **list,
+                                     TumblerThumbnailer *thumbnailer)
 {
   GList *lp;
 
   for (lp = *list; lp != NULL; lp = lp->next)
     {
       if (lp->data == thumbnailer)
-        *list = g_list_delete_link (*list, lp);
+        {
+          g_object_unref (lp->data);
+          *list = g_list_delete_link (*list, lp);
+          break;
+        }
     }
 }
 
 
 
-static void
-tumbler_registry_unregister (TumblerThumbnailer *thumbnailer,
-                             TumblerRegistry    *registry)
-{
-  g_return_if_fail (TUMBLER_IS_THUMBNAILER (thumbnailer));
-  g_return_if_fail (TUMBLER_IS_REGISTRY (registry));
-
-  g_mutex_lock (registry->mutex);
-
-  /* remove the thumbnailer from all hash key lists */
-  g_hash_table_foreach (registry->thumbnailers, (GHFunc) tumbler_registry_remove,
-                        thumbnailer);
-
-  g_mutex_unlock (registry->mutex);
-}
-
-
-
 static gint
 tumbler_registry_compare (TumblerThumbnailer *a,
                           TumblerThumbnailer *b)
@@ -318,6 +288,10 @@ tumbler_registry_lookup (TumblerRegistry *registry,
   g_return_val_if_fail (TUMBLER_IS_REGISTRY (registry), NULL);
   g_return_val_if_fail (hash_key != NULL, NULL);
 
+  thumbnailer = g_hash_table_lookup (registry->preferred_thumbnailers, hash_key);
+  if (thumbnailer != NULL)
+    return g_object_ref (thumbnailer);
+
   list = g_hash_table_lookup (registry->thumbnailers, hash_key);
 
   if (list != NULL)
@@ -396,14 +370,39 @@ tumbler_registry_add (TumblerRegistry    *registry,
           /* insert the pointer to the list in the hash table */
           g_hash_table_insert (registry->thumbnailers, g_strdup (hash_keys[n]), list);
         }
-
-      /* connect to the unregister signal of the thumbnailer */
-      g_signal_connect (thumbnailer, "unregister", 
-                        G_CALLBACK (tumbler_registry_unregister), registry);
     }
 
+  /* connect to the unregister signal of the thumbnailer */
+  g_signal_connect_swapped (thumbnailer, "unregister", 
+                            G_CALLBACK (tumbler_registry_remove), registry);
+
   g_strfreev (hash_keys);
 
+#ifdef DEBUG
+  dump_registry (registry);
+#endif
+
+  g_mutex_unlock (registry->mutex);
+}
+
+
+
+void
+tumbler_registry_remove (TumblerRegistry    *registry,
+                         TumblerThumbnailer *thumbnailer)
+{
+  g_return_if_fail (TUMBLER_IS_REGISTRY (registry));
+  g_return_if_fail (TUMBLER_IS_THUMBNAILER (thumbnailer));
+
+  g_mutex_lock (registry->mutex);
+
+  g_signal_handlers_disconnect_matched (thumbnailer, G_SIGNAL_MATCH_DATA, 
+                                        0, 0, NULL, NULL, registry);
+                                        
+  /* remove the thumbnailer from all hash key lists */
+  g_hash_table_foreach (registry->thumbnailers, 
+                        (GHFunc) tumbler_registry_remove_thumbnailer, thumbnailer);
+
   g_mutex_unlock (registry->mutex);
 }
 
@@ -627,3 +626,51 @@ tumbler_registry_get_supported (TumblerRegistry     *registry,
 
   g_mutex_unlock (registry->mutex);
 }
+
+
+
+TumblerThumbnailer *
+tumbler_registry_get_preferred (TumblerRegistry *registry,
+                                const gchar     *hash_key)
+{
+  TumblerThumbnailer *thumbnailer = NULL;
+
+  g_return_val_if_fail (TUMBLER_IS_REGISTRY (registry), NULL);
+  g_return_val_if_fail (hash_key != NULL && *hash_key != '\0', NULL);
+
+  g_mutex_lock (registry->mutex);
+  thumbnailer = g_hash_table_lookup (registry->preferred_thumbnailers, hash_key);
+  g_mutex_unlock (registry->mutex);
+
+  return thumbnailer != NULL ? g_object_ref (thumbnailer) : NULL;
+}
+
+
+
+void
+tumbler_registry_set_preferred (TumblerRegistry    *registry,
+                                const gchar        *hash_key,
+                                TumblerThumbnailer *thumbnailer)
+{
+  g_return_if_fail (TUMBLER_IS_REGISTRY (registry));
+  g_return_if_fail (hash_key != NULL && *hash_key != '\0');
+  g_return_if_fail (thumbnailer == NULL || TUMBLER_IS_THUMBNAILER (thumbnailer));
+
+  g_mutex_lock (registry->mutex);
+  
+  if (thumbnailer == NULL)
+    {
+      g_hash_table_remove (registry->preferred_thumbnailers, hash_key);
+    }
+  else
+    {
+      g_hash_table_insert (registry->preferred_thumbnailers, 
+                           g_strdup (hash_key), g_object_ref (thumbnailer));
+    }
+
+#ifdef DEBUG
+  dump_registry (registry);
+#endif
+
+  g_mutex_unlock (registry->mutex);
+}
diff --git a/tumblerd/tumbler-registry.h b/tumblerd/tumbler-registry.h
index dcd3ad3..c58a0e0 100644
--- a/tumblerd/tumbler-registry.h
+++ b/tumblerd/tumbler-registry.h
@@ -42,6 +42,8 @@ gboolean             tumbler_registry_load                  (TumblerRegistry
                                                              GError             **error);
 void                 tumbler_registry_add                   (TumblerRegistry     *registry,
                                                              TumblerThumbnailer  *thumbnailer);
+void                 tumbler_registry_remove                (TumblerRegistry     *registry,
+                                                             TumblerThumbnailer  *thumbnailer);
 GList               *tumbler_registry_get_thumbnailers      (TumblerRegistry     *registry) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
 TumblerThumbnailer **tumbler_registry_get_thumbnailer_array (TumblerRegistry     *registry,
                                                              TumblerFileInfo    **infos,
@@ -50,6 +52,11 @@ void                 tumbler_registry_update_supported      (TumblerRegistry
 void                 tumbler_registry_get_supported         (TumblerRegistry     *registry,
                                                              const gchar *const **uri_schemes,
                                                              const gchar *const **mime_types);
+TumblerThumbnailer  *tumbler_registry_get_preferred         (TumblerRegistry     *registry,
+                                                             const gchar         *hash_key) G_GNUC_WARN_UNUSED_RESULT;
+void                 tumbler_registry_set_preferred         (TumblerRegistry     *registry,
+                                                             const gchar         *hash_key,
+                                                             TumblerThumbnailer  *thumbnailer);
 
 G_END_DECLS
 
diff --git a/tumblerd/tumbler-specialized-thumbnailer.c b/tumblerd/tumbler-specialized-thumbnailer.c
index 95e0625..1af2c33 100644
--- a/tumblerd/tumbler-specialized-thumbnailer.c
+++ b/tumblerd/tumbler-specialized-thumbnailer.c
@@ -35,10 +35,8 @@
 enum
 {
   PROP_0,
-  PROP_MIME_TYPES,
-  PROP_URI_SCHEMES,
-  PROP_HASH_KEYS,
   PROP_NAME,
+  PROP_OBJECT_PATH,
   PROP_CONNECTION,
   PROP_PROXY,
   PROP_FOREIGN,
@@ -90,6 +88,7 @@ struct _TumblerSpecializedThumbnailer
   guint64          modified;
 
   gchar           *name;
+  gchar           *object_path;
 };
 
 
@@ -123,6 +122,15 @@ tumbler_specialized_thumbnailer_class_init (TumblerSpecializedThumbnailerClass *
                                                         G_PARAM_READWRITE));
 
   g_object_class_install_property (gobject_class,
+                                   PROP_OBJECT_PATH,
+                                   g_param_spec_string ("object-path",
+                                                        "object-path",
+                                                        "object-path",
+                                                        NULL,
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class,
                                    PROP_CONNECTION,
                                    g_param_spec_pointer ("connection",
                                                          "connection",
@@ -178,7 +186,6 @@ static void
 tumbler_specialized_thumbnailer_constructed (GObject *object)
 {
   TumblerSpecializedThumbnailer *thumbnailer = TUMBLER_SPECIALIZED_THUMBNAILER (object);
-  gchar                         *bus_path;
 
   g_return_if_fail (TUMBLER_SPECIALIZED_THUMBNAILER (thumbnailer));
 
@@ -186,17 +193,12 @@ tumbler_specialized_thumbnailer_constructed (GObject *object)
   if (G_OBJECT_CLASS (tumbler_specialized_thumbnailer_parent_class)->constructed != NULL)
     (G_OBJECT_CLASS (tumbler_specialized_thumbnailer_parent_class)->constructed) (object);
 
-  bus_path = g_strdup_printf ("/%s", thumbnailer->name);
-  bus_path = g_strdelimit (bus_path, ".", '/');
-
   thumbnailer->proxy = 
     dbus_g_proxy_new_for_name (thumbnailer->connection,
                                thumbnailer->name,
-                               bus_path,
+                               thumbnailer->object_path,
                                "org.freedesktop.thumbnails.SpecializedThumbnailer1");
 
-  g_free (bus_path);
-
   dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__STRING,
                                      G_TYPE_NONE, 
                                      G_TYPE_STRING,
@@ -236,6 +238,7 @@ tumbler_specialized_thumbnailer_finalize (GObject *object)
   TumblerSpecializedThumbnailer *thumbnailer = TUMBLER_SPECIALIZED_THUMBNAILER (object);
 
   g_free (thumbnailer->name);
+  g_free (thumbnailer->object_path);
 
   dbus_g_proxy_disconnect_signal (thumbnailer->proxy, "Ready",
                                   G_CALLBACK (tumbler_specialized_thumbnailer_proxy_ready),
@@ -245,6 +248,9 @@ tumbler_specialized_thumbnailer_finalize (GObject *object)
                                   G_CALLBACK (tumbler_specialized_thumbnailer_proxy_error),
                                   thumbnailer);
 
+  g_signal_handlers_disconnect_matched (thumbnailer->proxy, G_SIGNAL_MATCH_DATA,
+                                        0, 0, NULL, NULL, thumbnailer);
+
   g_object_unref (thumbnailer->proxy);
 
   dbus_g_connection_unref (thumbnailer->connection);
@@ -270,6 +276,9 @@ tumbler_specialized_thumbnailer_get_property (GObject    *object,
     case PROP_NAME:
       g_value_set_string (value, thumbnailer->name);
       break;
+    case PROP_OBJECT_PATH:
+      g_value_set_string (value, thumbnailer->object_path);
+      break;
     case PROP_PROXY:
       g_value_set_object (value, thumbnailer->proxy);
       break;
@@ -303,6 +312,9 @@ tumbler_specialized_thumbnailer_set_property (GObject      *object,
     case PROP_NAME:
       thumbnailer->name = g_value_dup_string (value);
       break;
+    case PROP_OBJECT_PATH:
+      thumbnailer->object_path = g_value_dup_string (value);
+      break;
     case PROP_FOREIGN:
       thumbnailer->foreign = g_value_get_boolean (value);
       break;
@@ -371,19 +383,44 @@ tumbler_specialized_thumbnailer_proxy_destroyed (DBusGProxy                    *
 
 
 TumblerThumbnailer *
-tumbler_specialized_thumbnailer_new_foreign (DBusGConnection *connection,
-                                             const gchar     *name,
-                                             const gchar     *uri_scheme,
-                                             const gchar     *mime_type)
+tumbler_specialized_thumbnailer_new (DBusGConnection    *connection,
+                                     const gchar        *name,
+                                     const gchar        *object_path,
+                                     const gchar *const *uri_schemes,
+                                     const gchar *const *mime_types,
+                                     guint64             modified)
+{
+  TumblerSpecializedThumbnailer *thumbnailer;
+
+  g_return_val_if_fail (connection != NULL, NULL);
+  g_return_val_if_fail (object_path != NULL && *object_path != '\0', NULL);
+  g_return_val_if_fail (name != NULL && *name != '\0', NULL);
+  g_return_val_if_fail (uri_schemes != NULL, NULL);
+  g_return_val_if_fail (mime_types != NULL, NULL);
+
+  thumbnailer = g_object_new (TUMBLER_TYPE_SPECIALIZED_THUMBNAILER, 
+                              "connection", connection, "foreign", FALSE, "name", name, 
+                              "object-path", object_path, "uri-schemes", uri_schemes, 
+                              "mime-types", mime_types,
+                              "modified", modified, NULL);
+
+  return TUMBLER_THUMBNAILER (thumbnailer);
+}
+
+
+
+TumblerThumbnailer *
+tumbler_specialized_thumbnailer_new_foreign (DBusGConnection    *connection,
+                                             const gchar        *name,
+                                             const gchar *const *uri_schemes,
+                                             const gchar *const *mime_types)
 {
   TumblerSpecializedThumbnailer *thumbnailer;
-  const gchar                   *uri_schemes[2] = { uri_scheme, NULL };
-  const gchar                   *mime_types[2] = { mime_type, NULL };
 
   g_return_val_if_fail (connection != NULL, NULL);
   g_return_val_if_fail (name != NULL, NULL);
-  g_return_val_if_fail (uri_scheme != NULL, NULL);
-  g_return_val_if_fail (mime_type != NULL, NULL);
+  g_return_val_if_fail (uri_schemes != NULL, NULL);
+  g_return_val_if_fail (mime_types != NULL, NULL);
 
   thumbnailer = g_object_new (TUMBLER_TYPE_SPECIALIZED_THUMBNAILER, 
                               "connection", connection, "foreign", TRUE, "name", name, 
@@ -395,6 +432,15 @@ tumbler_specialized_thumbnailer_new_foreign (DBusGConnection *connection,
 
 
 
+const gchar *
+tumbler_specialized_thumbnailer_get_name (TumblerSpecializedThumbnailer *thumbnailer)
+{
+  g_return_val_if_fail (TUMBLER_IS_SPECIALIZED_THUMBNAILER (thumbnailer), FALSE);
+  return thumbnailer->name;
+}
+
+
+
 gboolean
 tumbler_specialized_thumbnailer_get_foreign (TumblerSpecializedThumbnailer *thumbnailer)
 {
diff --git a/tumblerd/tumbler-specialized-thumbnailer.h b/tumblerd/tumbler-specialized-thumbnailer.h
index 7914618..97c922f 100644
--- a/tumblerd/tumbler-specialized-thumbnailer.h
+++ b/tumblerd/tumbler-specialized-thumbnailer.h
@@ -37,19 +37,22 @@ G_BEGIN_DECLS
 typedef struct _TumblerSpecializedThumbnailerClass TumblerSpecializedThumbnailerClass;
 typedef struct _TumblerSpecializedThumbnailer      TumblerSpecializedThumbnailer;
 
-GType               tumbler_specialized_thumbnailer_get_type     (void) G_GNUC_CONST;
-
-TumblerThumbnailer *tumbler_specialized_thumbnailer_new          (DBusGConnection               *connection,
-                                                                  const gchar                   *name,
-                                                                  const GStrv                   *uri_schemes,
-                                                                  const GStrv                   *mime_types,
-                                                                  guint64                        modified) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
-TumblerThumbnailer *tumbler_specialized_thumbnailer_new_foreign  (DBusGConnection               *connection,
-                                                                  const gchar                   *name,
-                                                                  const gchar                   *uri_scheme,
-                                                                  const gchar                   *mime_type) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
-gboolean            tumbler_specialized_thumbnailer_get_foreign  (TumblerSpecializedThumbnailer *thumbnailer);
-guint64             tumbler_specialized_thumbnailer_get_modified (TumblerSpecializedThumbnailer *thumbnailer);
+GType               tumbler_specialized_thumbnailer_get_type        (void) G_GNUC_CONST;
+
+TumblerThumbnailer *tumbler_specialized_thumbnailer_new             (DBusGConnection               *connection,
+                                                                     const gchar                   *name,
+                                                                     const gchar                   *object_path,
+                                                                     const gchar *const            *uri_schemes,
+                                                                     const gchar *const            *mime_types,
+                                                                     guint64                        modified) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+TumblerThumbnailer *tumbler_specialized_thumbnailer_new_foreign     (DBusGConnection               *connection,
+                                                                     const gchar                   *name,
+                                                                     const gchar *const            *uri_scheme,
+                                                                     const gchar *const            *mime_type) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+const gchar        *tumbler_specialized_thumbnailer_get_name        (TumblerSpecializedThumbnailer *thumbnailer);
+const gchar        *tumbler_specialized_thumbnailer_get_object_path (TumblerSpecializedThumbnailer *thumbnailer);
+gboolean            tumbler_specialized_thumbnailer_get_foreign     (TumblerSpecializedThumbnailer *thumbnailer);
+guint64             tumbler_specialized_thumbnailer_get_modified    (TumblerSpecializedThumbnailer *thumbnailer);
 
 G_END_DECLS
 



More information about the Xfce4-commits mailing list