[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