From 0a1321274cf4a0202ca66abfce0fb3173b4ffd2f Mon Sep 17 00:00:00 2001 From: Eric Koegel Date: Mon, 25 Feb 2013 11:49:16 +0300 Subject: [PATCH] Thumbnail service support to xfdesktop-settings (Bug #6536) --- common/xfdesktop-common.c | 61 ++++++++- common/xfdesktop-common.h | 2 + common/xfdesktop-thumbnailer.c | 49 +++---- common/xfdesktop-thumbnailer.h | 3 + settings/main.c | 235 ++++++++++++++++++++++++++++++---- settings/xfdesktop-settings-ui.glade | 4 +- 6 files changed, 298 insertions(+), 56 deletions(-) diff --git a/common/xfdesktop-common.c b/common/xfdesktop-common.c index 344611a..755e273 100644 --- a/common/xfdesktop-common.c +++ b/common/xfdesktop-common.c @@ -188,13 +188,70 @@ 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) { + static GSList *pixbuf_formats = NULL; + 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); + if(pixbuf_formats == NULL) { + pixbuf_formats = gdk_pixbuf_get_formats(); + } + + 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); + + 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..2c735df 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,34 +258,14 @@ xfdesktop_thumbnailer_new(void) return thumbnailer_object; } -static gchar * -xfdesktop_get_file_mimetype(gchar *file) +gboolean xfdesktop_thumbnailer_service_available(XfdesktopThumbnailer *thumbnailer) { - 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_return_val_if_fail(XFDESKTOP_IS_THUMBNAILER(thumbnailer), FALSE); - g_object_unref(file_info); - } + if(thumbnailer->priv->proxy == NULL) + return FALSE; - g_object_unref(temp_file); - - return mime_type; + return TRUE; } gboolean @@ -370,6 +351,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 +389,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 +401,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..c7ea4c5 100644 --- a/common/xfdesktop-thumbnailer.h +++ b/common/xfdesktop-thumbnailer.h @@ -60,6 +60,8 @@ XfdesktopThumbnailer * xfdesktop_thumbnailer_new(void); GType xfdesktop_thumbnailer_get_type(void); +gboolean xfdesktop_thumbnailer_service_available(XfdesktopThumbnailer *thumbnailer); + gboolean xfdesktop_thumbnailer_is_supported(XfdesktopThumbnailer *thumbnailer, gchar *file); @@ -67,6 +69,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..eea4588 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,90 @@ 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; + + 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, thumb_file, -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, + 100, + (GSourceFunc)cb_request_timer, + panel, + NULL); + + g_free(filename); + return; + } + + g_free(filename); + } while(gtk_tree_model_iter_next(model, &iter)); + } +} + +/* Attempts to queue a thumbnail preview */ +static void +xfdesktop_settings_create_thumbnail_preview(GtkTreeModel *model, + GtkTreeIter *iter, + AppearancePanel *panel) +{ + gchar *filename; + + gtk_tree_model_get(model, iter, COL_FILENAME, &filename, -1); + xfdesktop_thumbnailer_queue_thumbnail(panel->thumbnailer, filename); +} + +static void cb_special_icon_toggled(GtkCellRendererToggle *render, gchar *path, gpointer user_data) { XfconfChannel *channel = g_object_get_data(G_OBJECT(user_data), @@ -343,7 +468,8 @@ image_list_compare(GtkTreeModel *model, static GtkTreeIter * xfdesktop_settings_image_iconview_add(GtkTreeModel *model, - const char *path) + const char *path, + AppearancePanel *panel) { gboolean added = FALSE, found = FALSE, valid = FALSE; GtkTreeIter iter, search_iter; @@ -386,6 +512,8 @@ xfdesktop_settings_image_iconview_add(GtkTreeModel *model, COL_COLLATE_KEY, lower, -1); + xfdesktop_settings_create_thumbnail_preview(model, &iter, panel); + added = TRUE; } } @@ -404,13 +532,17 @@ xfdesktop_settings_image_iconview_add(GtkTreeModel *model, static GtkTreeIter * xfdesktop_image_list_add_dir(GtkListStore *ls, const char *path, - const char *cur_image_file) + const char *cur_image_file, + AppearancePanel *panel) { GDir *dir; gboolean needs_slash = TRUE; const gchar *file; GtkTreeIter *iter, *iter_ret = NULL; gchar buf[PATH_MAX]; +#ifdef G_ENABLE_DEBUG + GTimer *timer = g_timer_new(); +#endif dir = g_dir_open(path, 0, 0); if(!dir) @@ -423,7 +555,7 @@ xfdesktop_image_list_add_dir(GtkListStore *ls, g_snprintf(buf, sizeof(buf), needs_slash ? "%s/%s" : "%s%s", path, file); - iter = xfdesktop_settings_image_iconview_add(GTK_TREE_MODEL(ls), buf); + iter = xfdesktop_settings_image_iconview_add(GTK_TREE_MODEL(ls), buf, panel); if(iter) { if(cur_image_file && !iter_ret && !strcmp(buf, cur_image_file)) iter_ret = iter; @@ -434,6 +566,11 @@ xfdesktop_image_list_add_dir(GtkListStore *ls, g_dir_close(dir); +#ifdef G_ENABLE_DEBUG + DBG("time %f", g_timer_elapsed(timer, NULL)); + g_timer_destroy(timer); +#endif + return iter_ret; } @@ -602,11 +739,44 @@ cb_xfdesktop_spin_icon_size_changed(GtkSpinButton *button, } static void +xfdesktop_settings_stop_image_loading(AppearancePanel *panel) +{ + /* stop any thumbnailing in progress */ + xfdesktop_thumbnailer_dequeue_all_thumbnails(panel->thumbnailer); + + /* Remove any pending thumbnail updates */ + if(panel->request_timer_id != 0) { + PreviewData *pdata = panel->pdata; + g_source_remove(panel->request_timer_id); + panel->request_timer_id = 0; + 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); + } + + /* Remove any preview threads that may still be running */ + if(panel->preview_thread != NULL) { + g_thread_unref(panel->preview_thread); + panel->preview_thread = NULL; + } +} + +static void +cb_xfdesktop_bnt_exit_clicked(GtkButton *button, gpointer user_data) +{ + AppearancePanel *panel = user_data; + + xfdesktop_settings_stop_image_loading(panel); +} + +static void cb_folder_selection_changed(GtkWidget *button, gpointer user_data) { 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 +784,17 @@ 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; + + TRACE("folder changed to: %s", filename); + previous_filename = filename; + + xfdesktop_settings_stop_image_loading(panel); + 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"); @@ -624,7 +803,7 @@ cb_folder_selection_changed(GtkWidget *button, if(last_image == NULL) last_image = DEFAULT_BACKDROP; - iter = xfdesktop_image_list_add_dir(ls, filename, last_image); + iter = xfdesktop_image_list_add_dir(ls, filename, last_image, panel); gtk_icon_view_set_model(GTK_ICON_VIEW(panel->image_iconview), GTK_TREE_MODEL(ls)); @@ -638,22 +817,18 @@ 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; - } - - /* 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)); + /* Thumbnailer not available, fallback to loading the images in a thread */ + if(!xfdesktop_thumbnailer_service_available(panel->thumbnailer)) { + /* 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)); + } } g_free(property); @@ -876,8 +1051,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 @@ -887,7 +1067,8 @@ xfdesktop_settings_dialog_setup_tabs(GtkBuilder *main_gxml, { GtkWidget *appearance_container, *chk_custom_font_size, *spin_font_size, *w, *box, *spin_icon_size, - *chk_show_thumbnails, *chk_single_click, *appearance_settings; + *chk_show_thumbnails, *chk_single_click, *appearance_settings, + *bnt_exit; GtkBuilder *appearance_gxml; AppearancePanel *panel = g_new0(AppearancePanel, 1); GError *error = NULL; @@ -900,6 +1081,12 @@ xfdesktop_settings_dialog_setup_tabs(GtkBuilder *main_gxml, appearance_container = GTK_WIDGET(gtk_builder_get_object(main_gxml, "notebook_screens")); + bnt_exit = GTK_WIDGET(gtk_builder_get_object(main_gxml, "bnt_exit")); + + g_signal_connect(G_OBJECT(bnt_exit), "clicked", + G_CALLBACK(cb_xfdesktop_bnt_exit_clicked), + panel); + /* Icons tab */ /* icon size */ spin_icon_size = GTK_WIDGET(gtk_builder_get_object(main_gxml, "spin_icon_size")); diff --git a/settings/xfdesktop-settings-ui.glade b/settings/xfdesktop-settings-ui.glade index 7bccb6b..b69fe4b 100644 --- a/settings/xfdesktop-settings-ui.glade +++ b/settings/xfdesktop-settings-ui.glade @@ -769,7 +769,7 @@ - + True True True @@ -791,7 +791,7 @@ button2 - button1 + bnt_exit -- 1.7.10.4