summaryrefslogtreecommitdiff
path: root/src/eom-list-store.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/eom-list-store.c')
-rw-r--r--src/eom-list-store.c931
1 files changed, 931 insertions, 0 deletions
diff --git a/src/eom-list-store.c b/src/eom-list-store.c
new file mode 100644
index 0000000..562019e
--- /dev/null
+++ b/src/eom-list-store.c
@@ -0,0 +1,931 @@
+/* Eye Of Mate - Image Store
+ *
+ * Copyright (C) 2006-2008 The Free Software Foundation
+ *
+ * Author: Claudio Saavedra <[email protected]>
+ *
+ * Based on code by: Jens Finke <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "eom-list-store.h"
+#include "eom-thumbnail.h"
+#include "eom-image.h"
+#include "eom-job-queue.h"
+#include "eom-jobs.h"
+
+#include <string.h>
+
+#define EOM_LIST_STORE_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_LIST_STORE, EomListStorePrivate))
+
+G_DEFINE_TYPE (EomListStore, eom_list_store, GTK_TYPE_LIST_STORE);
+
+struct _EomListStorePrivate {
+ GList *monitors; /* Monitors for the directories */
+ gint initial_image; /* The image that should be selected firstly by the view. */
+ GdkPixbuf *busy_image; /* Loading image icon */
+ GdkPixbuf *missing_image; /* Missing image icon */
+ GMutex *mutex; /* Mutex for saving the jobs in the model */
+};
+
+static void
+eom_list_store_finalize (GObject *object)
+{
+ EomListStore *store = EOM_LIST_STORE (object);
+
+ if (store->priv != NULL) {
+ g_free (store->priv);
+ store->priv = NULL;
+ }
+
+ G_OBJECT_CLASS (eom_list_store_parent_class)->finalize (object);
+}
+
+static void
+foreach_monitors_free (gpointer data, gpointer user_data)
+{
+ g_file_monitor_cancel (G_FILE_MONITOR (data));
+}
+
+static void
+eom_list_store_dispose (GObject *object)
+{
+ EomListStore *store = EOM_LIST_STORE (object);
+
+ g_list_foreach (store->priv->monitors,
+ foreach_monitors_free, NULL);
+
+ g_list_free (store->priv->monitors);
+
+ store->priv->monitors = NULL;
+
+ if(store->priv->busy_image != NULL) {
+ g_object_unref (store->priv->busy_image);
+ store->priv->busy_image = NULL;
+ }
+
+ if(store->priv->missing_image != NULL) {
+ g_object_unref (store->priv->missing_image);
+ store->priv->missing_image = NULL;
+ }
+
+ g_mutex_free (store->priv->mutex);
+
+ G_OBJECT_CLASS (eom_list_store_parent_class)->dispose (object);
+}
+
+static void
+eom_list_store_class_init (EomListStoreClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = eom_list_store_finalize;
+ object_class->dispose = eom_list_store_dispose;
+
+ g_type_class_add_private (object_class, sizeof (EomListStorePrivate));
+}
+
+/*
+ Sorting functions
+*/
+
+static gint
+eom_list_store_compare_func (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ gint r_value;
+
+ EomImage *image_a, *image_b;
+
+ gtk_tree_model_get (model, a,
+ EOM_LIST_STORE_EOM_IMAGE, &image_a,
+ -1);
+
+ gtk_tree_model_get (model, b,
+ EOM_LIST_STORE_EOM_IMAGE, &image_b,
+ -1);
+
+ r_value = strcmp (eom_image_get_collate_key (image_a),
+ eom_image_get_collate_key (image_b));
+
+ g_object_unref (G_OBJECT (image_a));
+ g_object_unref (G_OBJECT (image_b));
+
+ return r_value;
+}
+
+static GdkPixbuf *
+eom_list_store_get_icon (const gchar *icon_name)
+{
+ GError *error = NULL;
+ GtkIconTheme *icon_theme;
+ GdkPixbuf *pixbuf;
+
+ icon_theme = gtk_icon_theme_get_default ();
+
+ pixbuf = gtk_icon_theme_load_icon (icon_theme,
+ icon_name,
+ EOM_LIST_STORE_THUMB_SIZE,
+ 0,
+ &error);
+
+ if (!pixbuf) {
+ g_warning ("Couldn't load icon: %s", error->message);
+ g_error_free (error);
+ }
+
+ return pixbuf;
+}
+
+static void
+eom_list_store_init (EomListStore *self)
+{
+ GType types[EOM_LIST_STORE_NUM_COLUMNS];
+
+ types[EOM_LIST_STORE_THUMBNAIL] = GDK_TYPE_PIXBUF;
+ types[EOM_LIST_STORE_EOM_IMAGE] = G_TYPE_OBJECT;
+ types[EOM_LIST_STORE_THUMB_SET] = G_TYPE_BOOLEAN;
+ types[EOM_LIST_STORE_EOM_JOB] = G_TYPE_POINTER;
+
+ gtk_list_store_set_column_types (GTK_LIST_STORE (self),
+ EOM_LIST_STORE_NUM_COLUMNS, types);
+
+ self->priv = EOM_LIST_STORE_GET_PRIVATE (self);
+
+ self->priv->monitors = NULL;
+ self->priv->initial_image = -1;
+
+ self->priv->busy_image = eom_list_store_get_icon ("image-loading");
+ self->priv->missing_image = eom_list_store_get_icon ("image-missing");
+
+ self->priv->mutex = g_mutex_new ();
+
+ gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (self),
+ eom_list_store_compare_func,
+ NULL, NULL);
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self),
+ GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
+ GTK_SORT_ASCENDING);
+}
+
+/**
+ * eom_list_store_new:
+ *
+ * Creates a new and empty #EomListStore.
+ *
+ * Returns: a newly created #EomListStore.
+ **/
+GtkListStore*
+eom_list_store_new (void)
+{
+ return g_object_new (EOM_TYPE_LIST_STORE, NULL);
+}
+
+/*
+ Searchs for a file in the store. If found and @iter_found is not NULL,
+ then sets @iter_found to a #GtkTreeIter pointing to the file.
+ */
+static gboolean
+is_file_in_list_store (EomListStore *store,
+ const gchar *info_uri,
+ GtkTreeIter *iter_found)
+{
+ gboolean found = FALSE;
+ EomImage *image;
+ GFile *file;
+ gchar *str;
+ GtkTreeIter iter;
+
+ if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) {
+ return FALSE;
+ }
+
+ do {
+ gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
+ EOM_LIST_STORE_EOM_IMAGE, &image,
+ -1);
+ if (!image)
+ continue;
+
+ file = eom_image_get_file (image);
+ str = g_file_get_uri (file);
+
+ found = (strcmp (str, info_uri) == 0)? TRUE : FALSE;
+
+ g_object_unref (file);
+ g_free (str);
+ g_object_unref (G_OBJECT (image));
+
+ } while (!found &&
+ gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
+
+ if (found && iter_found != NULL) {
+ *iter_found = iter;
+ }
+
+ return found;
+}
+
+static gboolean
+is_file_in_list_store_file (EomListStore *store,
+ GFile *file,
+ GtkTreeIter *iter_found)
+{
+ gchar *uri_str;
+ gboolean result;
+
+ uri_str = g_file_get_uri (file);
+
+ result = is_file_in_list_store (store, uri_str, iter_found);
+
+ g_free (uri_str);
+
+ return result;
+}
+
+static void
+eom_job_thumbnail_cb (EomJobThumbnail *job, gpointer data)
+{
+ EomListStore *store;
+ GtkTreeIter iter;
+ EomImage *image;
+ GdkPixbuf *thumbnail;
+ GFile *file;
+
+ g_return_if_fail (EOM_IS_LIST_STORE (data));
+
+ store = EOM_LIST_STORE (data);
+
+ file = eom_image_get_file (job->image);
+
+ if (is_file_in_list_store_file (store, file, &iter)) {
+ gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
+ EOM_LIST_STORE_EOM_IMAGE, &image,
+ -1);
+
+ if (job->thumbnail) {
+ eom_image_set_thumbnail (image, job->thumbnail);
+
+ /* Getting the thumbnail, in case it needed
+ * transformations */
+ thumbnail = eom_image_get_thumbnail (image);
+ } else {
+ thumbnail = g_object_ref (store->priv->missing_image);
+ }
+
+ gtk_list_store_set (GTK_LIST_STORE (store), &iter,
+ EOM_LIST_STORE_THUMBNAIL, thumbnail,
+ EOM_LIST_STORE_THUMB_SET, TRUE,
+ EOM_LIST_STORE_EOM_JOB, NULL,
+ -1);
+
+ g_object_unref (thumbnail);
+ }
+
+ g_object_unref (file);
+}
+
+static void
+on_image_changed (EomImage *image, EomListStore *store)
+{
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gint pos;
+
+ pos = eom_list_store_get_pos_by_image (store, image);
+ path = gtk_tree_path_new_from_indices (pos, -1);
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
+ eom_list_store_thumbnail_refresh (store, &iter);
+ gtk_tree_path_free (path);
+}
+
+/**
+ * eom_list_store_remove:
+ * @store: An #EomListStore.
+ * @iter: A #GtkTreeIter.
+ *
+ * Removes the image pointed by @iter from @store.
+ **/
+static void
+eom_list_store_remove (EomListStore *store, GtkTreeIter *iter)
+{
+ EomImage *image;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+ EOM_LIST_STORE_EOM_IMAGE, &image,
+ -1);
+
+ g_signal_handlers_disconnect_by_func (image, on_image_changed, store);
+ g_object_unref (image);
+
+ gtk_list_store_remove (GTK_LIST_STORE (store), iter);
+}
+
+/**
+ * eom_list_store_append_image:
+ * @store: An #EomListStore.
+ * @image: An #EomImage.
+ *
+ * Adds an #EomImage to @store. The thumbnail of the image is not
+ * loaded and will only be loaded if the thumbnail is made visible
+ * or eom_list_store_set_thumbnail() is called.
+ *
+ **/
+void
+eom_list_store_append_image (EomListStore *store, EomImage *image)
+{
+ GtkTreeIter iter;
+
+ g_signal_connect (image, "changed",
+ G_CALLBACK (on_image_changed),
+ store);
+
+ gtk_list_store_append (GTK_LIST_STORE (store), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (store), &iter,
+ EOM_LIST_STORE_EOM_IMAGE, image,
+ EOM_LIST_STORE_THUMBNAIL, store->priv->busy_image,
+ EOM_LIST_STORE_THUMB_SET, FALSE,
+ -1);
+}
+
+static void
+eom_list_store_append_image_from_file (EomListStore *store,
+ GFile *file)
+{
+ EomImage *image;
+
+ g_return_if_fail (EOM_IS_LIST_STORE (store));
+
+ image = eom_image_new_file (file);
+
+ eom_list_store_append_image (store, image);
+}
+
+static void
+file_monitor_changed_cb (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event,
+ EomListStore *store)
+{
+ const char *mimetype;
+ GFileInfo *file_info;
+ GtkTreeIter iter;
+ EomImage *image;
+
+ switch (event) {
+ case G_FILE_MONITOR_EVENT_CHANGED:
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0, NULL, NULL);
+ if (file_info == NULL) {
+ break;
+ }
+ mimetype = g_file_info_get_content_type (file_info);
+
+ if (is_file_in_list_store_file (store, file, &iter)) {
+ if (eom_image_is_supported_mime_type (mimetype)) {
+ gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
+ EOM_LIST_STORE_EOM_IMAGE, &image,
+ -1);
+ eom_image_file_changed (image);
+ g_object_unref (image);
+ eom_list_store_thumbnail_refresh (store, &iter);
+ } else {
+ eom_list_store_remove (store, &iter);
+ }
+ } else {
+ if (eom_image_is_supported_mime_type (mimetype)) {
+ eom_list_store_append_image_from_file (store, file);
+ }
+ }
+ g_object_unref (file_info);
+ break;
+ case G_FILE_MONITOR_EVENT_DELETED:
+ if (is_file_in_list_store_file (store, file, &iter)) {
+ EomImage *image;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
+ EOM_LIST_STORE_EOM_IMAGE, &image,
+ -1);
+
+ eom_list_store_remove (store, &iter);
+ }
+ break;
+ case G_FILE_MONITOR_EVENT_CREATED:
+ if (!is_file_in_list_store_file (store, file, NULL)) {
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0, NULL, NULL);
+ if (file_info == NULL) {
+ break;
+ }
+ mimetype = g_file_info_get_content_type (file_info);
+
+ if (eom_image_is_supported_mime_type (mimetype)) {
+ eom_list_store_append_image_from_file (store, file);
+ }
+ g_object_unref (file_info);
+ }
+ break;
+ case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0, NULL, NULL);
+ if (file_info == NULL) {
+ break;
+ }
+ mimetype = g_file_info_get_content_type (file_info);
+ if (is_file_in_list_store_file (store, file, &iter) &&
+ eom_image_is_supported_mime_type (mimetype)) {
+ eom_list_store_thumbnail_refresh (store, &iter);
+ }
+ g_object_unref (file_info);
+ break;
+ case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+ case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
+ case G_FILE_MONITOR_EVENT_UNMOUNTED:
+ break;
+ }
+}
+
+/*
+ * Called for each file in a directory. Checks if the file is some
+ * sort of image. If so, it creates an image object and adds it to the
+ * list.
+ */
+static void
+directory_visit (GFile *directory,
+ GFileInfo *children_info,
+ EomListStore *store)
+{
+ GFile *child;
+ gboolean load_uri = FALSE;
+ const char *mime_type, *name;
+
+ mime_type = g_file_info_get_content_type (children_info);
+ name = g_file_info_get_name (children_info);
+
+ if (!g_str_has_prefix (name, ".")) {
+ if (eom_image_is_supported_mime_type (mime_type)) {
+ load_uri = TRUE;
+ }
+ }
+
+ if (load_uri) {
+ child = g_file_get_child (directory, name);
+ eom_list_store_append_image_from_file (store, child);
+ }
+}
+
+static void
+eom_list_store_append_directory (EomListStore *store,
+ GFile *file,
+ GFileType file_type)
+{
+ GFileMonitor *file_monitor;
+ GFileEnumerator *file_enumerator;
+ GFileInfo *file_info;
+
+ g_return_if_fail (file_type == G_FILE_TYPE_DIRECTORY);
+
+ file_monitor = g_file_monitor_directory (file,
+ 0, NULL, NULL);
+
+ if (file_monitor != NULL) {
+ g_signal_connect (file_monitor, "changed",
+ G_CALLBACK (file_monitor_changed_cb), store);
+
+ /* prepend seems more efficient to me, we don't need this list
+ to be sorted */
+ store->priv->monitors = g_list_prepend (store->priv->monitors, file_monitor);
+ }
+
+ file_enumerator = g_file_enumerate_children (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ 0, NULL, NULL);
+ file_info = g_file_enumerator_next_file (file_enumerator, NULL, NULL);
+
+ while (file_info != NULL)
+ {
+ directory_visit (file, file_info, store);
+ g_object_unref (file_info);
+ file_info = g_file_enumerator_next_file (file_enumerator, NULL, NULL);
+ }
+ g_object_unref (file_enumerator);
+}
+
+/**
+ * eom_list_store_add_files:
+ * @store: An #EomListStore.
+ * @file_list: A %NULL-terminated list of #GFile's.
+ *
+ * Adds a list of #GFile's to @store. The given list
+ * must be %NULL-terminated.
+ *
+ * If any of the #GFile's in @file_list is a directory, all the images
+ * in that directory will be added to @store. If the list of files contains
+ * only one file and this is a regular file, then all the images in the same
+ * directory will be added as well to @store.
+ *
+ **/
+void
+eom_list_store_add_files (EomListStore *store, GList *file_list)
+{
+ GList *it;
+ GFileInfo *file_info;
+ GFileType file_type;
+ GFile *initial_file = NULL;
+ GtkTreeIter iter;
+
+ if (file_list == NULL) {
+ return;
+ }
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
+ GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
+ GTK_SORT_ASCENDING);
+
+ for (it = file_list; it != NULL; it = it->next) {
+ GFile *file = (GFile *) it->data;
+
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE","
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0, NULL, NULL);
+ if (file_info == NULL) {
+ continue;
+ }
+ file_type = g_file_info_get_file_type (file_info);
+
+ /* Workaround for gvfs backends that don't set the GFileType. */
+ if (G_UNLIKELY (file_type == G_FILE_TYPE_UNKNOWN)) {
+ const gchar *ctype;
+
+ ctype = g_file_info_get_content_type (file_info);
+
+ /* If the content type is supported adjust file_type */
+ if (eom_image_is_supported_mime_type (ctype))
+ file_type = G_FILE_TYPE_REGULAR;
+ }
+
+ g_object_unref (file_info);
+
+ if (file_type == G_FILE_TYPE_DIRECTORY) {
+ eom_list_store_append_directory (store, file, file_type);
+ } else if (file_type == G_FILE_TYPE_REGULAR &&
+ g_list_length (file_list) == 1) {
+
+ initial_file = g_file_dup (file);
+
+ file = g_file_get_parent (file);
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ 0, NULL, NULL);
+
+ /* If we can't get a file_info,
+ file_type will stay as G_FILE_TYPE_REGULAR */
+ if (file_info != NULL) {
+ file_type = g_file_info_get_file_type (file_info);
+ g_object_unref (file_info);
+ }
+
+ if (file_type == G_FILE_TYPE_DIRECTORY) {
+ eom_list_store_append_directory (store, file, file_type);
+
+ if (!is_file_in_list_store_file (store,
+ initial_file,
+ &iter)) {
+ eom_list_store_append_image_from_file (store, initial_file);
+ }
+ } else {
+ eom_list_store_append_image_from_file (store, initial_file);
+ }
+ g_object_unref (file);
+ } else if (file_type == G_FILE_TYPE_REGULAR &&
+ g_list_length (file_list) > 1) {
+ eom_list_store_append_image_from_file (store, file);
+ }
+ }
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
+ GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
+ GTK_SORT_ASCENDING);
+
+ if (initial_file &&
+ is_file_in_list_store_file (store, initial_file, &iter)) {
+ store->priv->initial_image = eom_list_store_get_pos_by_iter (store, &iter);
+ g_object_unref (initial_file);
+ } else {
+ store->priv->initial_image = 0;
+ }
+}
+
+/**
+ * eom_list_store_remove_image:
+ * @store: An #EomListStore.
+ * @image: An #EomImage.
+ *
+ * Removes @image from @store.
+ **/
+void
+eom_list_store_remove_image (EomListStore *store, EomImage *image)
+{
+ GtkTreeIter iter;
+ GFile *file;
+
+ g_return_if_fail (EOM_IS_LIST_STORE (store));
+ g_return_if_fail (EOM_IS_IMAGE (image));
+
+ file = eom_image_get_file (image);
+
+ if (is_file_in_list_store_file (store, file, &iter)) {
+ eom_list_store_remove (store, &iter);
+ }
+ g_object_unref (file);
+}
+
+/**
+ * eom_list_store_new_from_glist:
+ * @list: a %NULL-terminated list of #EomImage's.
+ *
+ * Creates a new #EomListStore from a list of #EomImage's.
+ * The given list must be %NULL-terminated.
+ *
+ * Returns: a new #EomListStore.
+ **/
+GtkListStore *
+eom_list_store_new_from_glist (GList *list)
+{
+ GList *it;
+
+ GtkListStore *store = eom_list_store_new ();
+
+ for (it = list; it != NULL; it = it->next) {
+ eom_list_store_append_image (EOM_LIST_STORE (store),
+ EOM_IMAGE (it->data));
+ }
+
+ return store;
+}
+
+/**
+ * eom_list_store_get_pos_by_image:
+ * @store: An #EomListStore.
+ * @image: An #EomImage.
+ *
+ * Gets the position where @image is stored in @store. If @image
+ * is not stored in @store, -1 is returned.
+ *
+ * Returns: the position of @image in @store or -1 if not found.
+ **/
+gint
+eom_list_store_get_pos_by_image (EomListStore *store, EomImage *image)
+{
+ GtkTreeIter iter;
+ gint pos = -1;
+ GFile *file;
+
+ g_return_val_if_fail (EOM_IS_LIST_STORE (store), -1);
+ g_return_val_if_fail (EOM_IS_IMAGE (image), -1);
+
+ file = eom_image_get_file (image);
+
+ if (is_file_in_list_store_file (store, file, &iter)) {
+ pos = eom_list_store_get_pos_by_iter (store, &iter);
+ }
+
+ g_object_unref (file);
+ return pos;
+}
+
+/**
+ * eom_list_store_get_image_by_pos:
+ * @store: An #EomListStore.
+ * @pos: the position of the required #EomImage.
+ *
+ * Gets the #EomImage in the position @pos of @store. If there is
+ * no image at position @pos, %NULL is returned.
+ *
+ * Returns: the #EomImage in position @pos or %NULL.
+ *
+ **/
+EomImage *
+eom_list_store_get_image_by_pos (EomListStore *store, gint pos)
+{
+ EomImage *image = NULL;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (EOM_IS_LIST_STORE (store), NULL);
+
+ if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, NULL, pos)) {
+ gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
+ EOM_LIST_STORE_EOM_IMAGE, &image,
+ -1);
+ }
+
+ return image;
+}
+
+/**
+ * eom_list_store_get_pos_by_iter:
+ * @store: An #EomListStore.
+ * @iter: A #GtkTreeIter pointing to an image in @store.
+ *
+ * Gets the position of the image pointed by @iter.
+ *
+ * Returns: The position of the image pointed by @iter.
+ **/
+gint
+eom_list_store_get_pos_by_iter (EomListStore *store,
+ GtkTreeIter *iter)
+{
+ gint *indices;
+ GtkTreePath *path;
+ gint pos;
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter);
+ indices = gtk_tree_path_get_indices (path);
+ pos = indices [0];
+ gtk_tree_path_free (path);
+
+ return pos;
+}
+
+/**
+ * eom_list_store_length:
+ * @store: An #EomListStore.
+ *
+ * Returns the number of images in the store.
+ *
+ * Returns: The number of images in @store.
+ **/
+gint
+eom_list_store_length (EomListStore *store)
+{
+ g_return_val_if_fail (EOM_IS_LIST_STORE (store), -1);
+
+ return gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL);
+}
+
+/**
+ * eom_list_store_get_initial_pos:
+ * @store: An #EomListStore.
+ *
+ * Gets the position of the #EomImage that should be loaded first.
+ * If not set, it returns -1.
+ *
+ * Returns: the position of the image to be loaded first or -1.
+ *
+ **/
+gint
+eom_list_store_get_initial_pos (EomListStore *store)
+{
+ g_return_val_if_fail (EOM_IS_LIST_STORE (store), -1);
+
+ return store->priv->initial_image;
+}
+
+static void
+eom_list_store_remove_thumbnail_job (EomListStore *store,
+ GtkTreeIter *iter)
+{
+ EomJob *job;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+ EOM_LIST_STORE_EOM_JOB, &job,
+ -1);
+
+ if (job != NULL) {
+ g_mutex_lock (store->priv->mutex);
+ eom_job_queue_remove_job (job);
+ gtk_list_store_set (GTK_LIST_STORE (store), iter,
+ EOM_LIST_STORE_EOM_JOB, NULL,
+ -1);
+ g_mutex_unlock (store->priv->mutex);
+ }
+
+
+}
+
+static void
+eom_list_store_add_thumbnail_job (EomListStore *store, GtkTreeIter *iter)
+{
+ EomImage *image;
+ EomJob *job;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+ EOM_LIST_STORE_EOM_IMAGE, &image,
+ EOM_LIST_STORE_EOM_JOB, &job,
+ -1);
+
+ if (job != NULL) {
+ g_object_unref (image);
+ return;
+ }
+
+ job = eom_job_thumbnail_new (image);
+
+ g_signal_connect (job,
+ "finished",
+ G_CALLBACK (eom_job_thumbnail_cb),
+ store);
+
+ g_mutex_lock (store->priv->mutex);
+ gtk_list_store_set (GTK_LIST_STORE (store), iter,
+ EOM_LIST_STORE_EOM_JOB, job,
+ -1);
+ eom_job_queue_add_job (job);
+ g_mutex_unlock (store->priv->mutex);
+ g_object_unref (job);
+ g_object_unref (image);
+}
+
+/**
+ * eom_list_store_thumbnail_set:
+ * @store: An #EomListStore.
+ * @iter: A #GtkTreeIter pointing to an image in @store.
+ *
+ * Sets the thumbnail for the image pointed by @iter.
+ *
+ **/
+void
+eom_list_store_thumbnail_set (EomListStore *store,
+ GtkTreeIter *iter)
+{
+ gboolean thumb_set = FALSE;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+ EOM_LIST_STORE_THUMB_SET, &thumb_set,
+ -1);
+
+ if (thumb_set) {
+ return;
+ }
+
+ eom_list_store_add_thumbnail_job (store, iter);
+}
+
+/**
+ * eom_list_store_thumbnail_unset:
+ * @store: An #EomListStore.
+ * @iter: A #GtkTreeIter pointing to an image in @store.
+ *
+ * Unsets the thumbnail for the image pointed by @iter, changing
+ * it to a "busy" icon.
+ *
+ **/
+void
+eom_list_store_thumbnail_unset (EomListStore *store,
+ GtkTreeIter *iter)
+{
+ EomImage *image;
+
+ eom_list_store_remove_thumbnail_job (store, iter);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
+ EOM_LIST_STORE_EOM_IMAGE, &image,
+ -1);
+ eom_image_set_thumbnail (image, NULL);
+ g_object_unref (image);
+
+ gtk_list_store_set (GTK_LIST_STORE (store), iter,
+ EOM_LIST_STORE_THUMBNAIL, store->priv->busy_image,
+ EOM_LIST_STORE_THUMB_SET, FALSE,
+ -1);
+}
+
+/**
+ * eom_list_store_thumbnail_refresh:
+ * @store: An #EomListStore.
+ * @iter: A #GtkTreeIter pointing to an image in @store.
+ *
+ * Refreshes the thumbnail for the image pointed by @iter.
+ *
+ **/
+void
+eom_list_store_thumbnail_refresh (EomListStore *store,
+ GtkTreeIter *iter)
+{
+ eom_list_store_remove_thumbnail_job (store, iter);
+ eom_list_store_add_thumbnail_job (store, iter);
+}