From 8fe6e56b9ebde70776dbc2e161cc47b8a62f2d50 Mon Sep 17 00:00:00 2001 From: Eric Koegel Date: Sun, 17 Feb 2013 19:11:36 +0300 Subject: [PATCH] Add thumbnail service support to xfdesktop-settings (Bug #6536) --- common/xfdesktop-common.c | 58 +++++++++++- common/xfdesktop-common.h | 2 + common/xfdesktop-thumbnailer.c | 49 ++++------- common/xfdesktop-thumbnailer.h | 1 + settings/main.c | 191 ++++++++++++++++++++++++++++++++++++---- 5 files changed, 249 insertions(+), 52 deletions(-) diff --git a/common/xfdesktop-common.c b/common/xfdesktop-common.c index 344611a..c8588c8 100644 --- a/common/xfdesktop-common.c +++ b/common/xfdesktop-common.c @@ -188,13 +188,67 @@ xfdesktop_backdrop_choose_random(const gchar *filename) return file; } +gchar * +xfdesktop_get_file_mimetype(const gchar *file) +{ + GFile *temp_file; + GFileInfo *file_info; + gchar *mime_type = NULL; + + g_return_val_if_fail(file != NULL, NULL); + + temp_file = g_file_new_for_path(file); + + g_return_val_if_fail(temp_file != NULL, NULL); + + file_info = g_file_query_info(temp_file, + "standard::content-type", + 0, + NULL, + NULL); + + if(file_info != NULL) { + mime_type = g_strdup(g_file_info_get_content_type(file_info)); + + g_object_unref(file_info); + } + + g_object_unref(temp_file); + + return mime_type; +} + gboolean xfdesktop_image_file_is_valid(const gchar *filename) { + GSList *pixbuf_formats = gdk_pixbuf_get_formats(); + GSList *l; + gboolean image_valid = FALSE; + gchar *file_mimetype; + g_return_val_if_fail(filename, FALSE); - /* if gdk can get pixbuf info from the file then it's an image file */ - return (gdk_pixbuf_get_file_info(filename, NULL, NULL) == NULL ? FALSE : TRUE); + file_mimetype = xfdesktop_get_file_mimetype(filename); + + if(file_mimetype == NULL) + return FALSE; + + /* Every pixbuf format has a list of mime types we can compare against */ + for(l = pixbuf_formats; l != NULL && image_valid == FALSE; l = g_slist_next(l)) { + gint i; + gchar ** mimetypes = gdk_pixbuf_format_get_mime_types(l->data); + + for(i = 0; mimetypes[i] != NULL && image_valid == FALSE; i++) { + if(g_strcmp0(file_mimetype, mimetypes[i]) == 0) + image_valid = TRUE; + } + g_strfreev(mimetypes); + } + + g_free(file_mimetype); + g_slist_free(pixbuf_formats); + + return image_valid; } gboolean diff --git a/common/xfdesktop-common.h b/common/xfdesktop-common.h index 7c7570d..5334ba8 100644 --- a/common/xfdesktop-common.h +++ b/common/xfdesktop-common.h @@ -76,6 +76,8 @@ gchar *xfdesktop_backdrop_choose_random(const gchar *filename); gboolean xfdesktop_image_file_is_valid(const gchar *filename); +gchar *xfdesktop_get_file_mimetype(const gchar *file); + gboolean xfdesktop_check_is_running(Window *xid); void xfdesktop_send_client_message(Window xid, const gchar *msg); diff --git a/common/xfdesktop-thumbnailer.c b/common/xfdesktop-thumbnailer.c index 8f39fe9..2c9f895 100644 --- a/common/xfdesktop-thumbnailer.c +++ b/common/xfdesktop-thumbnailer.c @@ -41,6 +41,7 @@ #include #include "xfdesktop-thumbnailer.h" #include "xfdesktop-marshal.h" +#include "xfdesktop-common.h" static void xfdesktop_thumbnailer_init(GObject *); static void xfdesktop_thumbnailer_class_init(GObjectClass *); @@ -257,36 +258,6 @@ xfdesktop_thumbnailer_new(void) return thumbnailer_object; } -static gchar * -xfdesktop_get_file_mimetype(gchar *file) -{ - GFile *temp_file; - GFileInfo *file_info; - gchar *mime_type = NULL; - - g_return_val_if_fail(file != NULL, NULL); - - temp_file = g_file_new_for_path(file); - - g_return_val_if_fail(temp_file != NULL, NULL); - - file_info = g_file_query_info(temp_file, - "standard::content-type", - 0, - NULL, - NULL); - - if(file_info != NULL) { - mime_type = g_strdup(g_file_info_get_content_type(file_info)); - - g_object_unref(file_info); - } - - g_object_unref(temp_file); - - return mime_type; -} - gboolean xfdesktop_thumbnailer_is_supported(XfdesktopThumbnailer *thumbnailer, gchar *file) @@ -370,6 +341,12 @@ xfdesktop_thumbnailer_queue_thumbnail(XfdesktopThumbnailer *thumbnailer, return TRUE; } +static void +xfdesktop_thumbnailer_dequeue_foreach(gpointer data, gpointer user_data) +{ + xfdesktop_thumbnailer_dequeue_thumbnail(user_data, data); +} + /** * xfdesktop_thumbnailer_dequeue_thumbnail: * @@ -402,9 +379,8 @@ xfdesktop_thumbnailer_dequeue_thumbnail(XfdesktopThumbnailer *thumbnailer, } if(g_slist_find(thumbnailer->priv->queue, file) != NULL) { - thumbnailer->priv->queue = g_slist_remove_all( - thumbnailer->priv->queue, - file); + thumbnailer->priv->queue = g_slist_remove_all(thumbnailer->priv->queue, + file); } thumbnailer->priv->request_timer_id = g_timeout_add_full( @@ -415,6 +391,13 @@ xfdesktop_thumbnailer_dequeue_thumbnail(XfdesktopThumbnailer *thumbnailer, NULL); } +void xfdesktop_thumbnailer_dequeue_all_thumbnails(XfdesktopThumbnailer *thumbnailer) +{ + g_return_if_fail(XFDESKTOP_IS_THUMBNAILER(thumbnailer)); + + g_slist_foreach(thumbnailer->priv->queue, (GFunc)xfdesktop_thumbnailer_dequeue_foreach, thumbnailer); +} + static gboolean xfdesktop_thumbnailer_queue_request_timer(XfdesktopThumbnailer *thumbnailer) { diff --git a/common/xfdesktop-thumbnailer.h b/common/xfdesktop-thumbnailer.h index 842f155..654d3e1 100644 --- a/common/xfdesktop-thumbnailer.h +++ b/common/xfdesktop-thumbnailer.h @@ -67,6 +67,7 @@ gboolean xfdesktop_thumbnailer_queue_thumbnail(XfdesktopThumbnailer *thumbnailer gchar *file); void xfdesktop_thumbnailer_dequeue_thumbnail(XfdesktopThumbnailer *thumbnailer, gchar *file); +void xfdesktop_thumbnailer_dequeue_all_thumbnails(XfdesktopThumbnailer *thumbnailer); void xfdesktop_thumbnailer_delete_thumbnail(XfdesktopThumbnailer *thumbnailer, gchar *src_file); diff --git a/settings/main.c b/settings/main.c index 441bcdc..6a8a7e0 100644 --- a/settings/main.c +++ b/settings/main.c @@ -50,6 +50,7 @@ #include #include "xfdesktop-common.h" +#include "xfdesktop-thumbnailer.h" #include "xfdesktop-settings-ui.h" #include "xfdesktop-settings-appearance-frame-ui.h" @@ -80,6 +81,12 @@ typedef struct { + GtkTreeModel *model; + GSList *iters; +} PreviewData; + +typedef struct +{ XfconfChannel *channel; gint screen; gint monitor; @@ -103,6 +110,11 @@ typedef struct GtkWidget *random_backdrop_order_chkbox; GThread *preview_thread; + PreviewData *pdata; + + XfdesktopThumbnailer *thumbnailer; + + gint request_timer_id; } AppearancePanel; @@ -111,6 +123,7 @@ enum COL_PIX = 0, COL_NAME, COL_FILENAME, + COL_THUMBNAIL, COL_COLLATE_KEY, N_COLS, }; @@ -131,15 +144,22 @@ static void xfdesktop_settings_do_single_preview(GtkTreeModel *model, GtkTreeIter *iter) { - gchar *name = NULL, *new_name = NULL, *filename = NULL; + gchar *name = NULL, *new_name = NULL, *filename = NULL, *thumbnail = NULL; GdkPixbuf *pix, *pix_scaled = NULL; gtk_tree_model_get(model, iter, COL_NAME, &name, COL_FILENAME, &filename, + COL_THUMBNAIL, &thumbnail, -1); - pix = gdk_pixbuf_new_from_file(filename, NULL); + if(thumbnail == NULL) { + pix = gdk_pixbuf_new_from_file(filename, NULL); + } else { + pix = gdk_pixbuf_new_from_file(thumbnail, NULL); + g_free(thumbnail); + } + g_free(filename); if(pix) { gint width, height; @@ -183,6 +203,27 @@ xfdesktop_settings_do_single_preview(GtkTreeModel *model, } static gpointer +xfdesktop_settings_create_some_previews(gpointer data) +{ + PreviewData *pdata = data; + GSList *l; + + GDK_THREADS_ENTER (); + + for(l = pdata->iters; l && l->data != NULL; l = l->next) + xfdesktop_settings_do_single_preview(pdata->model, l->data); + + g_object_unref(G_OBJECT(pdata->model)); + g_slist_foreach(pdata->iters, (GFunc)gtk_tree_iter_free, NULL); + g_slist_free(pdata->iters); + g_free(pdata); + + GDK_THREADS_LEAVE (); + + return NULL; +} + +static gpointer xfdesktop_settings_create_all_previews(gpointer data) { GtkTreeModel *model = data; @@ -219,6 +260,102 @@ xfdesktop_settings_create_all_previews(gpointer data) } static void +cb_request_timer(gpointer data) +{ + AppearancePanel *panel = data; + PreviewData *pdata = panel->pdata; + + TRACE("entering"); + + if(pdata == NULL) + return; + + panel->pdata = NULL; + g_source_remove(panel->request_timer_id); + panel->request_timer_id = 0; + + if(!g_thread_try_new("xfdesktop_settings_create_some_previews", + xfdesktop_settings_create_some_previews, + pdata, NULL)) + { + g_critical("Unable to create thread for image previews."); + g_object_unref(G_OBJECT(pdata->model)); + g_slist_foreach(pdata->iters, (GFunc)gtk_tree_iter_free, NULL); + g_slist_free(pdata->iters); + g_free(pdata); + } +} + +static void +cb_thumbnail_ready(XfdesktopThumbnailer *thumbnailer, + gchar *src_file, gchar *thumb_file, + gpointer user_data) +{ + AppearancePanel *panel = user_data; + GtkTreeModel *model = gtk_icon_view_get_model(GTK_ICON_VIEW(panel->image_iconview)); + GtkTreeIter iter; + + TRACE("entering for %s", src_file); + + if(gtk_tree_model_get_iter_first(model, &iter)) { + do { + gchar *filename = NULL; + gtk_tree_model_get(model, &iter, COL_FILENAME, &filename, -1); + + if(g_strcmp0(filename, src_file) == 0) { + gtk_list_store_set(GTK_LIST_STORE(model), &iter, + COL_THUMBNAIL, filename, -1); + + if(panel->request_timer_id != 0) { + g_source_remove(panel->request_timer_id); + } + + if(panel->pdata == NULL) { + panel->pdata = g_new0(PreviewData, 1); + panel->pdata->model = g_object_ref(G_OBJECT(model)); + } + + panel->pdata->iters = g_slist_prepend(panel->pdata->iters, gtk_tree_iter_copy(&iter)); + + panel->request_timer_id = g_timeout_add_full( + G_PRIORITY_LOW, + 1000, + (GSourceFunc)cb_request_timer, + panel, + NULL); + + g_free(filename); + return; + } + + g_free(filename); + } while(gtk_tree_model_iter_next(model, &iter)); + } +} + +/* Creates thumbnail previews if there's a thumbnail service available and + * returns TRUE. If it can't create thumbnails it returns FALSE. + */ +static gboolean +xfdesktop_settings_create_thumbnail_previews(GtkTreeModel *model, + AppearancePanel *panel) +{ + GtkTreeIter iter; + gboolean ret = FALSE; + + if(gtk_tree_model_get_iter_first(model, &iter)) { + do { + gchar *filename = NULL; + + gtk_tree_model_get(model, &iter, COL_FILENAME, &filename, -1); + ret = xfdesktop_thumbnailer_queue_thumbnail(panel->thumbnailer, filename); + } while(gtk_tree_model_iter_next(model, &iter)); + } + + return ret; +} + +static void cb_special_icon_toggled(GtkCellRendererToggle *render, gchar *path, gpointer user_data) { XfconfChannel *channel = g_object_get_data(G_OBJECT(user_data), @@ -607,6 +744,7 @@ cb_folder_selection_changed(GtkWidget *button, { AppearancePanel *panel = user_data; gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(button)); + static gchar *previous_filename = NULL; GtkListStore *ls; GtkTreeIter *iter; GtkTreePath *path; @@ -614,8 +752,15 @@ cb_folder_selection_changed(GtkWidget *button, TRACE("entering"); + /* Check to see if the folder actually did change */ + if(g_strcmp0(filename, previous_filename) == 0) + return; + + DBG("folder name %s", filename); + previous_filename = filename; + ls = gtk_list_store_new(N_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING); + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); property = xfdesktop_settings_generate_per_workspace_binding_string(panel, "last-image"); @@ -638,22 +783,29 @@ cb_folder_selection_changed(GtkWidget *button, } } - /* remove any preview threads that may still be running since we've probably - * changed to a new monitor/workspace */ - if(panel->preview_thread != NULL) { - g_thread_unref(panel->preview_thread); - panel->preview_thread = NULL; - } + /* stop any thumbnailing in progress */ + xfdesktop_thumbnailer_dequeue_all_thumbnails(panel->thumbnailer); + + /* Attempt to use the thumbnailer service if possible */ + if(!xfdesktop_settings_create_thumbnail_previews(GTK_TREE_MODEL(ls), panel)) { + /* Thumbnailer failed, fallback to loading the images in a thread. + * Remove any preview threads that may still be running since we've probably + * changed to a new monitor/workspace */ + if(panel->preview_thread != NULL) { + g_thread_unref(panel->preview_thread); + panel->preview_thread = NULL; + } - /* generate previews of each image -- the new thread will own - * the reference on the list store, so let's not unref it here */ - panel->preview_thread = g_thread_try_new("xfdesktop_settings_create_all_previews", - xfdesktop_settings_create_all_previews, - ls, NULL); + /* generate previews of each image -- the new thread will own + * the reference on the list store, so let's not unref it here */ + panel->preview_thread = g_thread_try_new("xfdesktop_settings_create_all_previews", + xfdesktop_settings_create_all_previews, + ls, NULL); - if(panel->preview_thread == NULL) { - g_critical("Failed to spawn thread; backdrop previews will be unavailable."); - g_object_unref(G_OBJECT(ls)); + if(panel->preview_thread == NULL) { + g_critical("Failed to spawn thread; backdrop previews will be unavailable."); + g_object_unref(G_OBJECT(ls)); + } } g_free(property); @@ -876,8 +1028,13 @@ xfdesktop_settings_setup_image_iconview(AppearancePanel *panel) "tooltip-column", COL_NAME, NULL); + panel->thumbnailer = xfdesktop_thumbnailer_new(); + g_signal_connect(G_OBJECT(iconview), "selection-changed", G_CALLBACK(cb_image_selection_changed), panel); + + g_signal_connect(panel->thumbnailer, "thumbnail-ready", + G_CALLBACK(cb_thumbnail_ready), panel); } static void -- 1.7.10.4