diff --git a/thunar/Makefile.am b/thunar/Makefile.am index 0b45ce907454ad9c94dc783a59c4423e231089f6..b715fea7d6e192cf6ea8c92c1ed6eb4d1c802b3e 100644 --- a/thunar/Makefile.am +++ b/thunar/Makefile.am @@ -201,6 +201,8 @@ Thunar_SOURCES = \ thunar-throbber.h \ thunar-throbber-fallback.c \ thunar-throbber-fallback.h \ + thunar-thumbnail-cache.c \ + thunar-thumbnail-cache.h \ thunar-thumbnailer.c \ thunar-thumbnailer.h \ thunar-thumbnail-frame.c \ @@ -260,6 +262,7 @@ Thunar_DEPENDENCIES = \ if HAVE_DBUS thunar_built_sources += \ thunar-dbus-service-infos.h \ + thunar-thumbnail-cache-proxy.h \ thunar-thumbnailer-proxy.h thunar_dbus_sources = \ @@ -267,6 +270,7 @@ thunar_dbus_sources = \ thunar-dbus-client.h \ thunar-dbus-service.c \ thunar-dbus-service.h \ + thunar-thumbnail-cache-proxy.h \ thunar-thumbnailer-proxy.h Thunar_CFLAGS += \ @@ -344,6 +348,14 @@ thunar-thumbnailer-proxy.h: $(srcdir)/thunar-thumbnailer-dbus.xml Makefile && sed -i -e 's/org_freedesktop_thumbnails_Thumbnailer1/thunar_thumbnailer_proxy/g' \ thunar-thumbnailer-proxy.h \ ) + +thunar-thumbnail-cache-proxy.h: $(srcdir)/thunar-thumbnail-cache-dbus.xml Makefile + $(AM_V_GEN) ( \ + dbus-binding-tool --mode=glib-client \ + $(srcdir)/thunar-thumbnail-cache-dbus.xml > thunar-thumbnail-cache-proxy.h \ + && sed -i -e 's/org_freedesktop_thumbnails_Cache1/thunar_thumbnail_cache_proxy/g' \ + thunar-thumbnail-cache-proxy.h \ + ) endif thunar-throbber-fallback.c: $(srcdir)/thunar-throbber-fallback.png Makefile diff --git a/thunar/thunar-application.c b/thunar/thunar-application.c index 4490105f0b475327082ee3212d7fa0900dd96516..8a5744a5b762ec1a56d62da601e56358d97e6eeb 100644 --- a/thunar/thunar-application.c +++ b/thunar/thunar-application.c @@ -1,22 +1,23 @@ -/* $Id$ */ +/* vi:set et ai sw=2 sts=2 ts=2: */ /*- * Copyright (c) 2005-2007 Benedikt Meurer <benny@xfce.org> * Copyright (c) 2005 Jeff Franks <jcfranks@xfce.org> - * Copyright (c) 2009-2010 Jannis Pohlmann <jannis@xfce.org> + * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org> * - * 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 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. + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H @@ -53,6 +54,7 @@ #include <thunar/thunar-private.h> #include <thunar/thunar-progress-dialog.h> #include <thunar/thunar-renamer-dialog.h> +#include <thunar/thunar-thumbnail-cache.h> #include <thunar/thunar-util.h> @@ -130,6 +132,8 @@ struct _ThunarApplication GtkWidget *progress_dialog; GList *windows; + ThunarThumbnailCache *thumbnail_cache; + gboolean daemon; guint show_dialogs_timer_id; @@ -205,6 +209,7 @@ thunar_application_init (ThunarApplication *application) /* initialize the application */ application->preferences = thunar_preferences_get (); + application->thumbnail_cache = thunar_thumbnail_cache_new (); application->files_to_launch = NULL; application->progress_dialog = NULL; @@ -279,6 +284,9 @@ thunar_application_finalize (GObject *object) } g_list_free (application->windows); + /* release the thumbnail cache */ + g_object_unref (G_OBJECT (application->thumbnail_cache)); + /* disconnect from the preferences */ g_object_unref (G_OBJECT (application->preferences)); @@ -1985,3 +1993,12 @@ thunar_application_restore_files (ThunarApplication *application, } + +ThunarThumbnailCache * +thunar_application_get_thumbnail_cache (ThunarApplication *application) +{ + _thunar_return_val_if_fail (THUNAR_IS_APPLICATION (application), NULL); + return g_object_ref (application->thumbnail_cache); +} + + diff --git a/thunar/thunar-application.h b/thunar/thunar-application.h index cb42877a62e1ccb1779224599ae0d1ec8fc1e70a..50845a3e01a4dc6fdc236b7063c87b532257773b 100644 --- a/thunar/thunar-application.h +++ b/thunar/thunar-application.h @@ -1,28 +1,30 @@ -/* $Id$ */ +/* vi:set et ai sw=2 sts=2 ts=2: */ /*- * Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org> * Copyright (c) 2005 Jeff Franks <jcfranks@xfce.org> - * Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org> + * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org> * - * 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 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. + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __THUNAR_APPLICATION_H__ #define __THUNAR_APPLICATION_H__ #include <thunar/thunar-window.h> +#include <thunar/thunar-thumbnail-cache.h> G_BEGIN_DECLS; @@ -36,110 +38,112 @@ typedef struct _ThunarApplication ThunarApplication; #define THUNAR_IS_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_APPLICATION)) #define THUNAR_APPLICATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_APPLICATION, ThunarApplicationClass)) -GType thunar_application_get_type (void) G_GNUC_CONST; - -ThunarApplication *thunar_application_get (void); - -gboolean thunar_application_get_daemon (ThunarApplication *application); -void thunar_application_set_daemon (ThunarApplication *application, - gboolean daemon); - -GList *thunar_application_get_windows (ThunarApplication *application); - -gboolean thunar_application_has_windows (ThunarApplication *application); - -void thunar_application_take_window (ThunarApplication *application, - GtkWindow *window); - -GtkWidget *thunar_application_open_window (ThunarApplication *application, - ThunarFile *directory, - GdkScreen *screen, - const gchar *startup_id); - -gboolean thunar_application_bulk_rename (ThunarApplication *application, - const gchar *working_directory, - gchar **filenames, - gboolean standalone, - GdkScreen *screen, - const gchar *startup_id, - GError **error); - -GtkWidget *thunar_application_get_progress_dialog (ThunarApplication *application); - -gboolean thunar_application_process_filenames (ThunarApplication *application, - const gchar *working_directory, - gchar **filenames, - GdkScreen *screen, - const gchar *startup_id, - GError **error); - -gboolean thunar_application_is_processing (ThunarApplication *application); - -void thunar_application_rename_file (ThunarApplication *application, - ThunarFile *file, - GdkScreen *screen, - const gchar *startup_id); -void thunar_application_create_file (ThunarApplication *application, - ThunarFile *parent_directory, - const gchar *content_type, - GdkScreen *screen, - const gchar *startup_id); -void thunar_application_create_file_from_template (ThunarApplication *application, - ThunarFile *parent_directory, - ThunarFile *template_file, - GdkScreen *screen, - const gchar *startup_id); -void thunar_application_copy_to (ThunarApplication *application, - gpointer parent, - GList *source_file_list, - GList *target_file_list, - GClosure *new_files_closure); - -void thunar_application_copy_into (ThunarApplication *application, - gpointer parent, - GList *source_file_list, - GFile *target_file, - GClosure *new_files_closure); - -void thunar_application_link_into (ThunarApplication *application, - gpointer parent, - GList *source_file_list, - GFile *target_file, - GClosure *new_files_closure); - -void thunar_application_move_into (ThunarApplication *application, - gpointer parent, - GList *source_file_list, - GFile *target_file, - GClosure *new_files_closure); - -void thunar_application_unlink_files (ThunarApplication *application, - gpointer parent, - GList *file_list, - gboolean permanently); - -void thunar_application_trash (ThunarApplication *application, - gpointer parent, - GList *file_list); - -void thunar_application_creat (ThunarApplication *application, - gpointer parent, - GList *file_list, - GClosure *new_files_closure); - -void thunar_application_mkdir (ThunarApplication *application, - gpointer parent, - GList *file_list, - GClosure *new_files_closure); - -void thunar_application_empty_trash (ThunarApplication *application, - gpointer parent, - const gchar *startup_id); - -void thunar_application_restore_files (ThunarApplication *application, - gpointer parent, - GList *trash_file_list, - GClosure *new_files_closure); +GType thunar_application_get_type (void) G_GNUC_CONST; + +ThunarApplication *thunar_application_get (void); + +gboolean thunar_application_get_daemon (ThunarApplication *application); +void thunar_application_set_daemon (ThunarApplication *application, + gboolean daemon); + +GList *thunar_application_get_windows (ThunarApplication *application); + +gboolean thunar_application_has_windows (ThunarApplication *application); + +void thunar_application_take_window (ThunarApplication *application, + GtkWindow *window); + +GtkWidget *thunar_application_open_window (ThunarApplication *application, + ThunarFile *directory, + GdkScreen *screen, + const gchar *startup_id); + +gboolean thunar_application_bulk_rename (ThunarApplication *application, + const gchar *working_directory, + gchar **filenames, + gboolean standalone, + GdkScreen *screen, + const gchar *startup_id, + GError **error); + +GtkWidget *thunar_application_get_progress_dialog (ThunarApplication *application); + +gboolean thunar_application_process_filenames (ThunarApplication *application, + const gchar *working_directory, + gchar **filenames, + GdkScreen *screen, + const gchar *startup_id, + GError **error); + +gboolean thunar_application_is_processing (ThunarApplication *application); + +void thunar_application_rename_file (ThunarApplication *application, + ThunarFile *file, + GdkScreen *screen, + const gchar *startup_id); +void thunar_application_create_file (ThunarApplication *application, + ThunarFile *parent_directory, + const gchar *content_type, + GdkScreen *screen, + const gchar *startup_id); +void thunar_application_create_file_from_template (ThunarApplication *application, + ThunarFile *parent_directory, + ThunarFile *template_file, + GdkScreen *screen, + const gchar *startup_id); +void thunar_application_copy_to (ThunarApplication *application, + gpointer parent, + GList *source_file_list, + GList *target_file_list, + GClosure *new_files_closure); + +void thunar_application_copy_into (ThunarApplication *application, + gpointer parent, + GList *source_file_list, + GFile *target_file, + GClosure *new_files_closure); + +void thunar_application_link_into (ThunarApplication *application, + gpointer parent, + GList *source_file_list, + GFile *target_file, + GClosure *new_files_closure); + +void thunar_application_move_into (ThunarApplication *application, + gpointer parent, + GList *source_file_list, + GFile *target_file, + GClosure *new_files_closure); + +void thunar_application_unlink_files (ThunarApplication *application, + gpointer parent, + GList *file_list, + gboolean permanently); + +void thunar_application_trash (ThunarApplication *application, + gpointer parent, + GList *file_list); + +void thunar_application_creat (ThunarApplication *application, + gpointer parent, + GList *file_list, + GClosure *new_files_closure); + +void thunar_application_mkdir (ThunarApplication *application, + gpointer parent, + GList *file_list, + GClosure *new_files_closure); + +void thunar_application_empty_trash (ThunarApplication *application, + gpointer parent, + const gchar *startup_id); + +void thunar_application_restore_files (ThunarApplication *application, + gpointer parent, + GList *trash_file_list, + GClosure *new_files_closure); + +ThunarThumbnailCache *thunar_application_get_thumbnail_cache (ThunarApplication *application); G_END_DECLS; diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c index 1281841dd5e7ee5f0f190c0eedcdaaf0c3d88abe..ebb10325b1e76a03930c6a8c857bf2112454cbaf 100644 --- a/thunar/thunar-file.c +++ b/thunar/thunar-file.c @@ -1254,11 +1254,13 @@ thunar_file_rename (ThunarFile *file, gboolean called_from_job, GError **error) { - GKeyFile *key_file; - GError *err = NULL; - GFile *previous_file; - GFile *renamed_file; - gint watch_count; + ThunarApplication *application; + ThunarThumbnailCache *thumbnail_cache; + GKeyFile *key_file; + GError *err = NULL; + GFile *previous_file; + GFile *renamed_file; + gint watch_count; _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); _thunar_return_val_if_fail (g_utf8_validate (name, -1, NULL), FALSE); @@ -1317,6 +1319,13 @@ thunar_file_rename (ThunarFile *file, /* try to rename the file */ renamed_file = g_file_set_display_name (file->gfile, name, cancellable, error); + /* notify the thumbnail cache that we can now also move the thumbnail */ + application = thunar_application_get (); + thumbnail_cache = thunar_application_get_thumbnail_cache (application); + thunar_thumbnail_cache_move_file (thumbnail_cache, previous_file, renamed_file); + g_object_unref (thumbnail_cache); + g_object_unref (application); + /* check if we succeeded */ if (renamed_file != NULL) { diff --git a/thunar/thunar-icon-factory.c b/thunar/thunar-icon-factory.c index 576329a4f3bb61ec099f533ec5795404263ecaaf..4f2ec42ddf69d433c741b6b35f8b198fd53be40f 100644 --- a/thunar/thunar-icon-factory.c +++ b/thunar/thunar-icon-factory.c @@ -1,25 +1,22 @@ -/* $Id$ */ +/* vi:set et ai sw=2 sts=2 ts=2: */ /*- * Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org> - * Copyright (c) 2009-2010 Jannis Pohlmann <jannis@xfce.org> + * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org> * - * 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 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. + * 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 - * - * The basic idea for the icon factory implementation was borrowed from - * Nautilus initially, but the implementation is very different from - * what Nautilus does. + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H @@ -38,7 +35,6 @@ #include <thunar/thunar-preferences.h> #include <thunar/thunar-private.h> #include <thunar/thunar-thumbnail-frame.h> -#include <thunar/thunar-thumbnailer.h> @@ -106,8 +102,6 @@ struct _ThunarIconFactory { GObject __parent__; - ThunarThumbnailer *thumbnailer; - ThunarPreferences *preferences; GdkPixbuf *recently[MAX_RECENTLY]; /* ring buffer */ @@ -209,9 +203,6 @@ thunar_icon_factory_init (ThunarIconFactory *factory) /* allocate the hash table for the icon cache */ factory->icon_cache = g_hash_table_new_full (thunar_icon_key_hash, thunar_icon_key_equal, g_free, g_object_unref); - - /* create a new thumbnailer */ - factory->thumbnailer = thunar_thumbnailer_new (); } @@ -250,9 +241,6 @@ thunar_icon_factory_finalize (GObject *object) /* clear the icon cache hash table */ g_hash_table_destroy (factory->icon_cache); - /* release the thumbnailer */ - g_object_unref (G_OBJECT (factory->thumbnailer)); - /* remove the "changed" emission hook from the GtkIconTheme class */ g_signal_remove_emission_hook (g_signal_lookup ("changed", GTK_TYPE_ICON_THEME), factory->changed_hook_id); @@ -804,7 +792,6 @@ thunar_icon_factory_load_file_icon (ThunarIconFactory *factory, ThunarFileIconState icon_state, gint icon_size) { - ThunarFileThumbState thumb_state; GInputStream *stream; GtkIconInfo *icon_info; const gchar *thumbnail_path; @@ -830,20 +817,6 @@ thunar_icon_factory_load_file_icon (ThunarIconFactory *factory, /* check if thumbnails are enabled and we can display a thumbnail for the item */ if (G_LIKELY (factory->show_thumbnails && thunar_file_is_regular (file))) { - /* this is how thumbnails for files are loaded: first, we check the thumbnail - * state. If that is unknown, we request a thumbnail to be generated in the - * background. At the same time we already try to load the thumbnail, in case - * it's already there. when the thumbnail is ready, we just load it */ - - /* determine the thumbnail state of the file */ - thumb_state = thunar_file_get_thumb_state (file); - - if (thumb_state == THUNAR_FILE_THUMB_STATE_UNKNOWN) - { - /* we don't know the state yet so request a new thumbnail in the background */ - thunar_thumbnailer_queue_file (factory->thumbnailer, file); - } - /* determine the preview icon first */ gicon = thunar_file_get_preview_icon (file); diff --git a/thunar/thunar-io-jobs.c b/thunar/thunar-io-jobs.c index 898118aa45b790fb620c21ffdcbb912b6c3e86f6..859bdef5aba82010ad1f4318bc91555fa7c749b9 100644 --- a/thunar/thunar-io-jobs.c +++ b/thunar/thunar-io-jobs.c @@ -24,6 +24,7 @@ #include <gio/gio.h> +#include <thunar/thunar-application.h> #include <thunar/thunar-enum-types.h> #include <thunar/thunar-gio-extensions.h> #include <thunar/thunar-io-scan-directory.h> @@ -32,6 +33,7 @@ #include <thunar/thunar-job.h> #include <thunar/thunar-private.h> #include <thunar/thunar-simple-job.h> +#include <thunar/thunar-thumbnail-cache.h> #include <thunar/thunar-transfer-job.h> @@ -367,13 +369,15 @@ _thunar_io_jobs_unlink (ThunarJob *job, GValueArray *param_values, GError **error) { - ThunarJobResponse response; - GFileInfo *info; - GError *err = NULL; - GList *file_list; - GList *lp; - gchar *base_name; - gchar *display_name; + ThunarThumbnailCache *thumbnail_cache; + ThunarApplication *application; + ThunarJobResponse response; + GFileInfo *info; + GError *err = NULL; + GList *file_list; + GList *lp; + gchar *base_name; + gchar *display_name; _thunar_return_val_if_fail (THUNAR_IS_JOB (job), FALSE); _thunar_return_val_if_fail (param_values != NULL, FALSE); @@ -404,6 +408,11 @@ _thunar_io_jobs_unlink (ThunarJob *job, /* we know the total list of files to process */ thunar_job_set_total_files (THUNAR_JOB (job), file_list); + /* take a reference on the thumbnail cache */ + application = thunar_application_get (); + thumbnail_cache = thunar_application_get_thumbnail_cache (application); + g_object_unref (application); + /* remove all the files */ for (lp = file_list; lp != NULL && !exo_job_is_cancelled (EXO_JOB (job)); lp = lp->next) { @@ -415,7 +424,13 @@ _thunar_io_jobs_unlink (ThunarJob *job, again: /* try to delete the file */ - if (!g_file_delete (lp->data, exo_job_get_cancellable (EXO_JOB (job)), &err)) + if (g_file_delete (lp->data, exo_job_get_cancellable (EXO_JOB (job)), &err)) + { + /* notify the thumbnail cache that the corresponding thumbnail can also + * be deleted now */ + thunar_thumbnail_cache_delete_file (thumbnail_cache, lp->data); + } + else { /* query the file info for the display name */ info = g_file_query_info (lp->data, @@ -459,6 +474,9 @@ again: } } + /* release the thumbnail cache */ + g_object_unref (thumbnail_cache); + /* release the file list */ thunar_g_file_list_free (file_list); @@ -646,13 +664,15 @@ _thunar_io_jobs_link (ThunarJob *job, GValueArray *param_values, GError **error) { - GError *err = NULL; - GFile *real_target_file; - GList *new_files_list = NULL; - GList *source_file_list; - GList *sp; - GList *target_file_list; - GList *tp; + ThunarThumbnailCache *thumbnail_cache; + ThunarApplication *application; + GError *err = NULL; + GFile *real_target_file; + GList *new_files_list = NULL; + GList *source_file_list; + GList *sp; + GList *target_file_list; + GList *tp; _thunar_return_val_if_fail (THUNAR_IS_JOB (job), FALSE); _thunar_return_val_if_fail (param_values != NULL, FALSE); @@ -665,6 +685,11 @@ _thunar_io_jobs_link (ThunarJob *job, /* we know the total list of paths to process */ thunar_job_set_total_files (THUNAR_JOB (job), source_file_list); + /* take a reference on the thumbnail cache */ + application = thunar_application_get (); + thumbnail_cache = thunar_application_get_thumbnail_cache (application); + g_object_unref (application); + /* process all files */ for (sp = source_file_list, tp = target_file_list; err == NULL && sp != NULL && tp != NULL; @@ -685,6 +710,12 @@ _thunar_io_jobs_link (ThunarJob *job, { new_files_list = thunar_g_file_list_prepend (new_files_list, real_target_file); + + /* notify the thumbnail cache that we need to copy the original + * thumbnail for the symlink to have one too */ + thunar_thumbnail_cache_copy_file (thumbnail_cache, sp->data, + real_target_file); + } /* release the real target file */ @@ -692,6 +723,9 @@ _thunar_io_jobs_link (ThunarJob *job, } } + /* release the thumbnail cache */ + g_object_unref (thumbnail_cache); + if (err != NULL) { thunar_g_file_list_free (new_files_list); @@ -728,9 +762,11 @@ _thunar_io_jobs_trash (ThunarJob *job, GValueArray *param_values, GError **error) { - GError *err = NULL; - GList *file_list; - GList *lp; + ThunarThumbnailCache *thumbnail_cache; + ThunarApplication *application; + GError *err = NULL; + GList *file_list; + GList *lp; _thunar_return_val_if_fail (THUNAR_IS_JOB (job), FALSE); _thunar_return_val_if_fail (param_values != NULL, FALSE); @@ -742,12 +778,25 @@ _thunar_io_jobs_trash (ThunarJob *job, if (exo_job_set_error_if_cancelled (EXO_JOB (job), error)) return FALSE; + /* take a reference on the thumbnail cache */ + application = thunar_application_get (); + thumbnail_cache = thunar_application_get_thumbnail_cache (application); + g_object_unref (application); + for (lp = file_list; err == NULL && lp != NULL; lp = lp->next) { _thunar_assert (G_IS_FILE (lp->data)); + + /* trash the file or folder */ g_file_trash (lp->data, exo_job_get_cancellable (EXO_JOB (job)), &err); + + /* update the thumbnail cache */ + thunar_thumbnail_cache_cleanup_file (thumbnail_cache, lp->data); } + /* release the thumbnail cache */ + g_object_unref (thumbnail_cache); + if (err != NULL) { g_propagate_error (error, err); diff --git a/thunar/thunar-properties-dialog.c b/thunar/thunar-properties-dialog.c index 9e5a39da6ed8e4172bce3a060532f83d492dca0d..f52c20bd8f84a7c025ff04b39c1ab1f2a9370966 100644 --- a/thunar/thunar-properties-dialog.c +++ b/thunar/thunar-properties-dialog.c @@ -1,21 +1,22 @@ -/* $Id$ */ +/* vi:set et ai sw=2 sts=2 ts=2: */ /*- * Copyright (c) 2005-2007 Benedikt Meurer <benny@xfce.org> - * Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org> + * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org> * - * 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 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. + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H @@ -52,6 +53,7 @@ #include <thunar/thunar-private.h> #include <thunar/thunar-properties-dialog.h> #include <thunar/thunar-size-label.h> +#include <thunar/thunar-thumbnailer.h> @@ -115,6 +117,9 @@ struct _ThunarPropertiesDialog ThunarFile *file; + ThunarThumbnailer *thumbnailer; + guint thumbnail_request; + GtkWidget *notebook; GtkWidget *icon_button; GtkWidget *icon_image; @@ -209,6 +214,10 @@ thunar_properties_dialog_init (ThunarPropertiesDialog *dialog) g_signal_connect_swapped (G_OBJECT (dialog->preferences), "notify::misc-date-style", G_CALLBACK (thunar_properties_dialog_reload), dialog); + /* create a new thumbnailer */ + dialog->thumbnailer = thunar_thumbnailer_new (); + dialog->thumbnail_request = 0; + dialog->provider_factory = thunarx_provider_factory_get_default (); gtk_dialog_add_buttons (GTK_DIALOG (dialog), @@ -514,6 +523,16 @@ thunar_properties_dialog_finalize (GObject *object) g_signal_handlers_disconnect_by_func (dialog->preferences, thunar_properties_dialog_reload, dialog); g_object_unref (dialog->preferences); + /* cancel any pending thumbnailer requests */ + if (dialog->thumbnail_request > 0) + { + thunar_thumbnailer_dequeue (dialog->thumbnailer, dialog->thumbnail_request); + dialog->thumbnail_request = 0; + } + + /* release the thumbnailer */ + g_object_unref (dialog->thumbnailer); + /* release the provider property pages */ g_list_foreach (dialog->provider_pages, (GFunc) g_object_unref, NULL); g_list_free (dialog->provider_pages); @@ -813,6 +832,20 @@ thunar_properties_dialog_update (ThunarPropertiesDialog *dialog) _thunar_return_if_fail (THUNAR_IS_PROPERTIES_DIALOG (dialog)); _thunar_return_if_fail (THUNAR_IS_FILE (dialog->file)); + /* cancel any pending thumbnail requests */ + if (dialog->thumbnail_request > 0) + { + thunar_thumbnailer_dequeue (dialog->thumbnailer, dialog->thumbnail_request); + dialog->thumbnail_request = 0; + } + + if (dialog->file != NULL) + { + /* queue a new thumbnail request */ + thunar_thumbnailer_queue_file (dialog->thumbnailer, dialog->file, + &dialog->thumbnail_request); + } + icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (dialog))); icon_factory = thunar_icon_factory_get_for_icon_theme (icon_theme); diff --git a/thunar/thunar-standard-view.c b/thunar/thunar-standard-view.c index f671f05082519fa479fd53c6250b1233110a43aa..2478d7e87b050862acf9c18389e8eb21694e4beb 100644 --- a/thunar/thunar-standard-view.c +++ b/thunar/thunar-standard-view.c @@ -1,21 +1,22 @@ -/* $Id$ */ +/* vi:set et ai sw=2 sts=2 ts=2: */ /*- * Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org> - * Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org> + * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org> * - * 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 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. + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H @@ -49,6 +50,7 @@ #include <thunar/thunar-standard-view-ui.h> #include <thunar/thunar-templates-action.h> #include <thunar/thunar-text-renderer.h> +#include <thunar/thunar-thumbnailer.h> #if defined(GDK_WINDOWING_X11) #include <gdk/gdkx.h> @@ -260,6 +262,14 @@ static gboolean thunar_standard_view_drag_scroll_timer (gpo static void thunar_standard_view_drag_scroll_timer_destroy (gpointer user_data); static gboolean thunar_standard_view_drag_timer (gpointer user_data); static void thunar_standard_view_drag_timer_destroy (gpointer user_data); +static void thunar_standard_view_cancel_thumbnailing (ThunarStandardView *standard_view); +static void thunar_standard_view_schedule_thumbnail_timeout (ThunarStandardView *standard_view); +static void thunar_standard_view_schedule_thumbnail_idle (ThunarStandardView *standard_view); +static gboolean thunar_standard_view_request_thumbnails (ThunarStandardView *standard_view); +static void thunar_standard_view_scrolled (GtkAdjustment *adjustment, + ThunarStandardView *standard_view); +static void thunar_standard_view_size_allocate (ThunarStandardView *standard_view, + GtkAllocation *allocation); @@ -326,6 +336,12 @@ struct _ThunarStandardViewPrivate /* selected_files support */ GList *selected_files; + /* support for generating thumbnails */ + ThunarThumbnailer *thumbnailer; + guint thumbnail_request; + guint thumbnail_source_id; + gboolean thumbnailing_scheduled; + /* Tree path for restoring the selection after selecting and * deleting an item */ GtkTreePath *selection_before_delete; @@ -531,6 +547,10 @@ thunar_standard_view_init (ThunarStandardView *standard_view) /* grab a reference on the provider factory */ standard_view->priv->provider_factory = thunarx_provider_factory_get_default (); + /* create a thumbnailer */ + standard_view->priv->thumbnailer = thunar_thumbnailer_new (); + standard_view->priv->thumbnailing_scheduled = FALSE; + /* initialize the scrolled window */ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (standard_view), GTK_POLICY_AUTOMATIC, @@ -596,6 +616,10 @@ thunar_standard_view_init (ThunarStandardView *standard_view) * files in our model changes. */ g_signal_connect_swapped (G_OBJECT (standard_view->model), "notify::num-files", G_CALLBACK (thunar_standard_view_update_statusbar_text), standard_view); + + /* connect to size allocation signals for generating thumbnail requests */ + g_signal_connect_after (G_OBJECT (standard_view), "size-allocate", + G_CALLBACK (thunar_standard_view_size_allocate), NULL); } @@ -607,6 +631,7 @@ thunar_standard_view_constructor (GType type, { ThunarStandardView *standard_view; ThunarZoomLevel zoom_level; + GtkAdjustment *adjustment; ThunarColumn sort_column; GtkSortType sort_order; GtkWidget *view; @@ -667,6 +692,14 @@ thunar_standard_view_constructor (GType type, g_signal_connect (G_OBJECT (view), "drag-data-delete", G_CALLBACK (thunar_standard_view_drag_data_delete), object); g_signal_connect (G_OBJECT (view), "drag-end", G_CALLBACK (thunar_standard_view_drag_end), object); + /* connect to scroll events for generating thumbnail requests */ + adjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (standard_view)); + g_signal_connect (adjustment, "value-changed", + G_CALLBACK (thunar_standard_view_scrolled), object); + adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (standard_view)); + g_signal_connect (adjustment, "value-changed", + G_CALLBACK (thunar_standard_view_scrolled), object); + /* done, we have a working object */ return object; } @@ -678,6 +711,9 @@ thunar_standard_view_dispose (GObject *object) { ThunarStandardView *standard_view = THUNAR_STANDARD_VIEW (object); + /* cancel pending thumbnail sources and requests */ + thunar_standard_view_cancel_thumbnailing (standard_view); + /* unregister the "loading" binding */ if (G_UNLIKELY (standard_view->loading_binding != NULL)) exo_binding_unbind (standard_view->loading_binding); @@ -709,6 +745,9 @@ thunar_standard_view_finalize (GObject *object) _thunar_assert (standard_view->ui_manager == NULL); _thunar_assert (standard_view->clipboard == NULL); + /* release the thumbnailer */ + g_object_unref (standard_view->priv->thumbnailer); + /* release the scroll_to_file reference (if any) */ if (G_UNLIKELY (standard_view->priv->scroll_to_file != NULL)) g_object_unref (G_OBJECT (standard_view->priv->scroll_to_file)); @@ -1128,6 +1167,9 @@ thunar_standard_view_set_current_directory (ThunarNavigator *navigator, _thunar_return_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view)); _thunar_return_if_fail (current_directory == NULL || THUNAR_IS_FILE (current_directory)); + /* cancel any pending thumbnail sources and requests */ + thunar_standard_view_cancel_thumbnailing (standard_view); + /* disconnect any previous "loading" binding */ if (G_LIKELY (standard_view->loading_binding != NULL)) exo_binding_unbind (standard_view->loading_binding); @@ -1189,6 +1231,9 @@ thunar_standard_view_set_current_directory (ThunarNavigator *navigator, /* update the "Restore" action */ gtk_action_set_visible (standard_view->priv->action_restore, trashed); + /* schedule a thumbnail timeout */ + thunar_standard_view_schedule_thumbnail_timeout (standard_view); + /* notify all listeners about the new/old current directory */ g_object_notify (G_OBJECT (standard_view), "current-directory"); } @@ -1266,6 +1311,17 @@ thunar_standard_view_set_loading (ThunarStandardView *standard_view, thunar_file_list_free (selected_files); } + /* check if we're done loading and a thumbnail timeout or idle was requested */ + if (!loading && standard_view->priv->thumbnailing_scheduled) + { + /* we've just finished loading. it will probably the user some time to + * understand the contents of the folder before he will start interacting + * with the view. so here we can safely schedule an idle function instead + * of a timeout */ + thunar_standard_view_schedule_thumbnail_idle (standard_view); + standard_view->priv->thumbnailing_scheduled = FALSE; + } + /* notify listeners */ g_object_freeze_notify (G_OBJECT (standard_view)); g_object_notify (G_OBJECT (standard_view), "loading"); @@ -3240,6 +3296,185 @@ thunar_standard_view_drag_timer_destroy (gpointer user_data) +static void +thunar_standard_view_cancel_thumbnailing (ThunarStandardView *standard_view) +{ + _thunar_return_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view)); + + /* check if we have a pending thumbnail timeout/idle handler */ + if (standard_view->priv->thumbnail_source_id > 0) + { + /* cancel this handler */ + g_source_remove (standard_view->priv->thumbnail_source_id); + standard_view->priv->thumbnail_source_id = 0; + } + + /* check if we have a pending thumbnail request */ + if (standard_view->priv->thumbnail_request > 0) + { + /* cancel the request */ + thunar_thumbnailer_dequeue (standard_view->priv->thumbnailer, + standard_view->priv->thumbnail_request); + standard_view->priv->thumbnail_request = 0; + } +} + + + +static void +thunar_standard_view_schedule_thumbnail_timeout (ThunarStandardView *standard_view) +{ + _thunar_return_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view)); + + /* delay creating the idle until the view has finished loading. + * this is done because we only can tell the visible range reliably after + * all items have been added and we've perhaps scrolled to the file remember + * the last time */ + if (thunar_view_get_loading (THUNAR_VIEW (standard_view))) + { + standard_view->priv->thumbnailing_scheduled = TRUE; + return; + } + + /* cancel any pending thumbnail sources and requests */ + thunar_standard_view_cancel_thumbnailing (standard_view); + + /* schedule the timeout handler */ + standard_view->priv->thumbnail_source_id = + g_timeout_add (175, (GSourceFunc) thunar_standard_view_request_thumbnails, + standard_view); +} + + + +static void +thunar_standard_view_schedule_thumbnail_idle (ThunarStandardView *standard_view) +{ + _thunar_return_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view)); + + /* delay creating the idle until the view has finished loading. + * this is done because we only can tell the visible range reliably after + * all items have been added, layouting has finished and we've perhaps + * scrolled to the file remembered the last time */ + if (thunar_view_get_loading (THUNAR_VIEW (standard_view))) + { + standard_view->priv->thumbnailing_scheduled = TRUE; + return; + } + + /* cancel any pending thumbnail sources or requests */ + thunar_standard_view_cancel_thumbnailing (standard_view); + + /* schedule the timeout or idle handler */ + standard_view->priv->thumbnail_source_id = + g_idle_add ((GSourceFunc) thunar_standard_view_request_thumbnails, standard_view); +} + + + +static gboolean +thunar_standard_view_request_thumbnails (ThunarStandardView *standard_view) +{ + GtkTreePath *start_path; + GtkTreePath *end_path; + GtkTreePath *path; + GtkTreeIter iter; + ThunarFile *file; + gboolean valid_iter; + GList *visible_files = NULL; + + _thunar_return_val_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view), FALSE); + + /* reschedule the source if we're still loading the folder */ + if (thunar_view_get_loading (THUNAR_VIEW (standard_view))) + { + g_debug ("weird, this should never happen"); + return TRUE; + } + + /* compute visible item range */ + if ((*THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->get_visible_range) (standard_view, + &start_path, + &end_path)) + { + /* iterate over the range to collect all files */ + valid_iter = gtk_tree_model_get_iter (GTK_TREE_MODEL (standard_view->model), + &iter, start_path); + + while (valid_iter) + { + /* prepend the file to the visible items list */ + file = thunar_list_model_get_file (standard_view->model, &iter); + visible_files = g_list_prepend (visible_files, file); + + /* check if we've reached the end of the visible range */ + path = gtk_tree_model_get_path (GTK_TREE_MODEL (standard_view->model), &iter); + if (gtk_tree_path_compare (path, end_path) != 0) + { + /* try to compute the next visible item */ + valid_iter = + gtk_tree_model_iter_next (GTK_TREE_MODEL (standard_view->model), &iter); + } + else + { + /* we have reached the end, terminate the loop */ + valid_iter = FALSE; + } + + /* release the tree path */ + gtk_tree_path_free (path); + } + + /* queue a thumbnail request */ + thunar_thumbnailer_queue_files (standard_view->priv->thumbnailer, visible_files, + &standard_view->priv->thumbnail_request); + + /* release the file list */ + g_list_foreach (visible_files, (GFunc) g_object_unref, NULL); + g_list_free (visible_files); + + /* release the start and end path */ + gtk_tree_path_free (start_path); + gtk_tree_path_free (end_path); + } + + /* reset the timeout or idle handler ID */ + standard_view->priv->thumbnail_source_id = 0; + + return FALSE; +} + + + +static void +thunar_standard_view_scrolled (GtkAdjustment *adjustment, + ThunarStandardView *standard_view) +{ + _thunar_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); + _thunar_return_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view)); + + /* ignore adjustment changes when the view is still loading */ + if (thunar_view_get_loading (THUNAR_VIEW (standard_view))) + return; + + /* reschedule a thumbnail request timeout */ + thunar_standard_view_schedule_thumbnail_timeout (standard_view); +} + + + +static void +thunar_standard_view_size_allocate (ThunarStandardView *standard_view, + GtkAllocation *allocation) +{ + _thunar_return_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view)); + + /* reschedule a thumbnail request timeout */ + thunar_standard_view_schedule_thumbnail_timeout (standard_view); +} + + + /** * thunar_standard_view_context_menu: * @standard_view : a #ThunarStandardView instance. diff --git a/thunar/thunar-thumbnail-cache-dbus.xml b/thunar/thunar-thumbnail-cache-dbus.xml new file mode 100644 index 0000000000000000000000000000000000000000..92ffa422949cc21390ef32bbb4795e7356ccaf91 --- /dev/null +++ b/thunar/thunar-thumbnail-cache-dbus.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<node name="/org/freedesktop/thumbnails/Cache1"> + <interface name="org.freedesktop.thumbnails.Cache1"> + <method name="Move"> + <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/> + <arg type="as" name="from_uris" direction="in" /> + <arg type="as" name="to_uris" direction="in" /> + </method> + + <method name="Copy"> + <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/> + <arg type="as" name="from_uris" direction="in" /> + <arg type="as" name="to_uris" direction="in" /> + </method> + + <method name="Delete"> + <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/> + <arg type="as" name="uris" direction="in" /> + </method> + + <method name="Cleanup"> + <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/> + <arg type="as" name="base_uris" direction="in" /> + <arg type="u" name="since" direction="in" /> + </method> + </interface> +</node> diff --git a/thunar/thunar-thumbnail-cache.c b/thunar/thunar-thumbnail-cache.c new file mode 100644 index 0000000000000000000000000000000000000000..d1c482ecc443272eae0342ee1026b42d70e09e6b --- /dev/null +++ b/thunar/thunar-thumbnail-cache.c @@ -0,0 +1,692 @@ +/* vi:set et ai sw=2 sts=2 ts=2: */ +/*- + * Copyright (c) 2011 Jannis Pohlmann <jannis@xfce.org> + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_DBUS +#include <dbus/dbus.h> +#include <dbus/dbus-glib.h> + +#include <thunar/thunar-thumbnail-cache-proxy.h> +#endif + +#include <glib.h> +#include <glib-object.h> +#include <gio/gio.h> + +#include <thunar/thunar-private.h> +#include <thunar/thunar-thumbnail-cache.h> + + + +static void thunar_thumbnail_cache_finalize (GObject *object); + + + +struct _ThunarThumbnailCacheClass +{ + GObjectClass __parent__; +}; + +struct _ThunarThumbnailCache +{ + GObject __parent__; + +#ifdef HAVE_DBUS + DBusGProxy *cache_proxy; + + GList *move_source_queue; + GList *move_target_queue; + guint move_queue_idle_id; + + GList *copy_source_queue; + GList *copy_target_queue; + guint copy_queue_idle_id; + + GList *delete_queue; + guint delete_queue_idle_id; + + GList *cleanup_queue; + guint cleanup_queue_idle_id; + + GMutex *lock; +#endif +}; + + + +G_DEFINE_TYPE (ThunarThumbnailCache, thunar_thumbnail_cache, G_TYPE_OBJECT) + + + +static void +thunar_thumbnail_cache_class_init (ThunarThumbnailCacheClass *klass) +{ + GObjectClass *gobject_class; + + /* Determine the parent type class */ + thunar_thumbnail_cache_parent_class = g_type_class_peek_parent (klass); + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = thunar_thumbnail_cache_finalize; +} + + + +static void +thunar_thumbnail_cache_init (ThunarThumbnailCache *cache) +{ +#ifdef HAVE_DBUS + DBusGConnection *connection; + + /* try to connect to D-Bus */ + connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); + if (connection != NULL) + { + /* create a proxy for the thumbnail cache service */ + cache->cache_proxy = + dbus_g_proxy_new_for_name (connection, + "org.freedesktop.thumbnails.Cache1", + "/org/freedesktop/thumbnails/Cache1", + "org.freedesktop.thumbnails.Cache1"); + + /* release the D-Bus connection */ + dbus_g_connection_unref (connection); + } + + /* create a new mutex for accessing the cache from different threads */ + cache->lock = g_mutex_new (); +#endif +} + + + +static void +thunar_thumbnail_cache_finalize (GObject *object) +{ +#ifdef HAVE_DBUS + ThunarThumbnailCache *cache = THUNAR_THUMBNAIL_CACHE (object); + + /* acquire a cache lock */ + g_mutex_lock (cache->lock); + + /* drop the move queue idle and all queued files */ + if (cache->move_queue_idle_id > 0) + g_source_remove (cache->move_queue_idle_id); + g_list_foreach (cache->move_source_queue, (GFunc) g_object_unref, NULL); + g_list_foreach (cache->move_target_queue, (GFunc) g_object_unref, NULL); + g_list_free (cache->move_source_queue); + g_list_free (cache->move_target_queue); + + /* drop the copy queue idle and all queued files */ + if (cache->copy_queue_idle_id > 0) + g_source_remove (cache->copy_queue_idle_id); + g_list_foreach (cache->copy_source_queue, (GFunc) g_object_unref, NULL); + g_list_foreach (cache->copy_target_queue, (GFunc) g_object_unref, NULL); + g_list_free (cache->copy_source_queue); + g_list_free (cache->copy_target_queue); + + /* drop the delete queue idle and all queued files */ + if (cache->delete_queue_idle_id > 0) + g_source_remove (cache->delete_queue_idle_id); + g_list_foreach (cache->delete_queue, (GFunc) g_object_unref, NULL); + g_list_free (cache->delete_queue); + + /* drop the cleanup queue idle and all queued files */ + if (cache->cleanup_queue_idle_id > 0) + g_source_remove (cache->cleanup_queue_idle_id); + g_list_foreach (cache->cleanup_queue, (GFunc) g_object_unref, NULL); + g_list_free (cache->cleanup_queue); + + /* check if we have a valid cache proxy */ + if (cache->cache_proxy != NULL) + { + /* release the cache proxy itself */ + g_object_unref (cache->cache_proxy); + } + + /* release the cache lock */ + g_mutex_unlock (cache->lock); + + /* release the mutex itself */ + g_mutex_free (cache->lock); +#endif + + (*G_OBJECT_CLASS (thunar_thumbnail_cache_parent_class)->finalize) (object); +} + + + +#ifdef HAVE_DBUS +static void +thunar_thumbnail_cache_move_async_reply (DBusGProxy *proxy, + GError *error, + gpointer user_data) +{ + _thunar_return_if_fail (DBUS_IS_G_PROXY (proxy)); +} + + + +static void +thunar_thumbnail_cache_move_async (ThunarThumbnailCache *cache, + const gchar **source_uris, + const gchar **target_uris) +{ + _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache)); + _thunar_return_if_fail (source_uris != NULL); + _thunar_return_if_fail (target_uris != NULL); + + /* request a thumbnail cache update asynchronously */ + thunar_thumbnail_cache_proxy_move_async (cache->cache_proxy, + source_uris, target_uris, + thunar_thumbnail_cache_move_async_reply, + NULL); +} + + + +static void +thunar_thumbnail_cache_copy_async_reply (DBusGProxy *proxy, + GError *error, + gpointer user_data) +{ + _thunar_return_if_fail (DBUS_IS_G_PROXY (proxy)); +} + + + +static void +thunar_thumbnail_cache_copy_async (ThunarThumbnailCache *cache, + const gchar **source_uris, + const gchar **target_uris) +{ + _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache)); + _thunar_return_if_fail (source_uris != NULL); + _thunar_return_if_fail (target_uris != NULL); + + /* request a thumbnail cache update asynchronously */ + thunar_thumbnail_cache_proxy_copy_async (cache->cache_proxy, + source_uris, target_uris, + thunar_thumbnail_cache_copy_async_reply, + NULL); +} + + + +static void +thunar_thumbnail_cache_delete_async_reply (DBusGProxy *proxy, + GError *error, + gpointer user_data) +{ + _thunar_return_if_fail (DBUS_IS_G_PROXY (proxy)); +} + + + +static void +thunar_thumbnail_cache_delete_async (ThunarThumbnailCache *cache, + const gchar **uris) +{ + _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache)); + _thunar_return_if_fail (uris != NULL); + + /* request a thumbnail cache update asynchronously */ + thunar_thumbnail_cache_proxy_delete_async (cache->cache_proxy, uris, + thunar_thumbnail_cache_delete_async_reply, + NULL); +} + + + +static void +thunar_thumbnail_cache_cleanup_async_reply (DBusGProxy *proxy, + GError *error, + gpointer user_data) +{ + _thunar_return_if_fail (DBUS_IS_G_PROXY (proxy)); +} + + + +static void +thunar_thumbnail_cache_cleanup_async (ThunarThumbnailCache *cache, + const gchar *const *base_uris) +{ + _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache)); + _thunar_return_if_fail (base_uris != NULL); + + /* request a thumbnail cache update asynchronously */ + thunar_thumbnail_cache_proxy_cleanup_async (cache->cache_proxy, + (const gchar **)base_uris, 0, + thunar_thumbnail_cache_cleanup_async_reply, + NULL); +} + + + +static gboolean +thunar_thumbnail_cache_process_move_queue (ThunarThumbnailCache *cache) +{ + GList *sp; + GList *tp; + gchar **source_uris; + gchar **target_uris; + guint n_uris; + guint n; + + _thunar_return_val_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache), FALSE); + + /* acquire a cache lock */ + g_mutex_lock (cache->lock); + + /* compute how many URIs there are */ + n_uris = g_list_length (cache->move_source_queue); + + /* allocate a string array for the URIs */ + source_uris = g_new0 (gchar *, n_uris + 1); + target_uris = g_new0 (gchar *, n_uris + 1); + + /* fill URI array with file URIs from the move queue */ + for (n = 0, + sp = g_list_last (cache->move_source_queue), + tp = g_list_last (cache->move_target_queue); + sp != NULL && tp != NULL; + sp = sp->prev, tp = tp->prev, ++n) + { + source_uris[n] = g_file_get_uri (sp->data); + target_uris[n] = g_file_get_uri (tp->data); + + /* release the file objects */ + g_object_unref (sp->data); + g_object_unref (tp->data); + } + + /* NULL-terminate the URI arrays */ + source_uris[n] = NULL; + target_uris[n] = NULL; + + /* asynchronously move the thumbnails */ + thunar_thumbnail_cache_move_async (cache, + (const gchar **)source_uris, + (const gchar **)target_uris); + + /* free the URI arrays */ + g_free (source_uris); + g_free (target_uris); + + /* release the move queue lists */ + g_list_free (cache->move_source_queue); + g_list_free (cache->move_target_queue); + cache->move_source_queue = NULL; + cache->move_target_queue = NULL; + + /* reset the move queue idle ID */ + cache->move_queue_idle_id = 0; + + /* release the cache lock */ + g_mutex_unlock (cache->lock); + + return FALSE; +} + + + +static gboolean +thunar_thumbnail_cache_process_copy_queue (ThunarThumbnailCache *cache) +{ + GList *sp; + GList *tp; + gchar **source_uris; + gchar **target_uris; + guint n_uris; + guint n; + + _thunar_return_val_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache), FALSE); + + /* acquire a cache lock */ + g_mutex_lock (cache->lock); + + /* compute how many URIs there are */ + n_uris = g_list_length (cache->copy_source_queue); + + /* allocate a string array for the URIs */ + source_uris = g_new0 (gchar *, n_uris + 1); + target_uris = g_new0 (gchar *, n_uris + 1); + + /* fill URI array with file URIs from the copy queue */ + for (n = 0, + sp = g_list_last (cache->copy_source_queue), + tp = g_list_last (cache->copy_target_queue); + sp != NULL && tp != NULL; + sp = sp->prev, tp = tp->prev, ++n) + { + source_uris[n] = g_file_get_uri (sp->data); + target_uris[n] = g_file_get_uri (tp->data); + + /* release the file objects */ + g_object_unref (sp->data); + g_object_unref (tp->data); + } + + /* NULL-terminate the URI arrays */ + source_uris[n] = NULL; + target_uris[n] = NULL; + + /* asynchronously copy the thumbnails */ + thunar_thumbnail_cache_copy_async (cache, + (const gchar **)source_uris, + (const gchar **)target_uris); + + /* free the URI arrays */ + g_free (source_uris); + g_free (target_uris); + + /* release the copy queue lists */ + g_list_free (cache->copy_source_queue); + g_list_free (cache->copy_target_queue); + cache->copy_source_queue = NULL; + cache->copy_target_queue = NULL; + + /* reset the copy queue idle ID */ + cache->copy_queue_idle_id = 0; + + /* release the cache lock */ + g_mutex_unlock (cache->lock); + + return FALSE; +} + + + +static gboolean +thunar_thumbnail_cache_process_delete_queue (ThunarThumbnailCache *cache) +{ + GList *lp; + gchar **uris; + guint n_uris; + guint n; + + _thunar_return_val_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache), FALSE); + + /* acquire a cache lock */ + g_mutex_lock (cache->lock); + + /* compute how many URIs there are */ + n_uris = g_list_length (cache->delete_queue); + + /* allocate a string array for the URIs */ + uris = g_new0 (gchar *, n_uris + 1); + + /* fill URI array with file URIs from the delete queue */ + for (lp = g_list_last (cache->delete_queue), n = 0; lp != NULL; lp = lp->prev, ++n) + { + uris[n] = g_file_get_uri (lp->data); + + /* release the file object */ + g_object_unref (lp->data); + } + + /* NULL-terminate the URI array */ + uris[n] = NULL; + + /* asynchronously delete the thumbnails */ + thunar_thumbnail_cache_delete_async (cache, (const gchar **)uris); + + /* free the URI array */ + g_free (uris); + + /* release the delete queue list */ + g_list_free (cache->delete_queue); + cache->delete_queue = NULL; + + /* reset the delete queue idle ID */ + cache->delete_queue_idle_id = 0; + + /* release the cache lock */ + g_mutex_unlock (cache->lock); + + return FALSE; +} + + + +static gboolean +thunar_thumbnail_cache_process_cleanup_queue (ThunarThumbnailCache *cache) +{ + GList *lp; + gchar **uris; + guint n_uris; + guint n; + + _thunar_return_val_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache), FALSE); + + /* acquire a cache lock */ + g_mutex_lock (cache->lock); + + /* compute how many URIs there are */ + n_uris = g_list_length (cache->cleanup_queue); + + /* allocate a string array for the URIs */ + uris = g_new0 (gchar *, n_uris + 1); + + g_debug ("cleanup:"); + + /* fill URI array with file URIs from the cleanup queue */ + for (lp = g_list_last (cache->cleanup_queue), n = 0; lp != NULL; lp = lp->prev, ++n) + { + uris[n] = g_file_get_uri (lp->data); + + g_debug (" %s", uris[n]); + + /* release the file object */ + g_object_unref (lp->data); + } + + /* NULL-terminate the URI array */ + uris[n] = NULL; + + /* asynchronously cleanup the thumbnails */ + thunar_thumbnail_cache_cleanup_async (cache, (const gchar *const *)uris); + + /* free the URI array */ + g_free (uris); + + /* release the cleanup queue list */ + g_list_free (cache->cleanup_queue); + cache->cleanup_queue = NULL; + + /* reset the cleanup queue idle ID */ + cache->cleanup_queue_idle_id = 0; + + /* release the cache lock */ + g_mutex_unlock (cache->lock); + + return FALSE; +} +#endif /* HAVE_DBUS */ + + + +ThunarThumbnailCache * +thunar_thumbnail_cache_new (void) +{ + return g_object_new (THUNAR_TYPE_THUMBNAIL_CACHE, NULL); +} + + + +void +thunar_thumbnail_cache_move_file (ThunarThumbnailCache *cache, + GFile *source_file, + GFile *target_file) +{ + _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache)); + _thunar_return_if_fail (G_IS_FILE (source_file)); + _thunar_return_if_fail (G_IS_FILE (target_file)); + +#ifdef HAVE_DBUS + /* acquire a cache lock */ + g_mutex_lock (cache->lock); + + /* check if we have a valid proxy for the cache service */ + if (cache->cache_proxy != NULL) + { + /* cancel any pending timeout to process the move queue */ + if (cache->move_queue_idle_id > 0) + { + g_source_remove (cache->move_queue_idle_id); + cache->move_queue_idle_id = 0; + } + + /* add the files to the move queue */ + cache->move_source_queue = g_list_prepend (cache->move_source_queue, + g_object_ref (source_file)); + cache->move_target_queue = g_list_prepend (cache->move_target_queue, + g_object_ref (target_file)); + + /* process the move queue in a 250ms timeout */ + cache->move_queue_idle_id = + g_timeout_add (250, (GSourceFunc) thunar_thumbnail_cache_process_move_queue, + cache); + } + + /* release the cache lock */ + g_mutex_unlock (cache->lock); +#endif +} + + + +void +thunar_thumbnail_cache_copy_file (ThunarThumbnailCache *cache, + GFile *source_file, + GFile *target_file) +{ + _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache)); + _thunar_return_if_fail (G_IS_FILE (source_file)); + _thunar_return_if_fail (G_IS_FILE (target_file)); + +#ifdef HAVE_DBUS + /* acquire a cache lock */ + g_mutex_lock (cache->lock); + + /* check if we have a valid proxy for the cache service */ + if (cache->cache_proxy != NULL) + { + /* cancel any pending timeout to process the copy queue */ + if (cache->copy_queue_idle_id > 0) + { + g_source_remove (cache->copy_queue_idle_id); + cache->copy_queue_idle_id = 0; + } + + /* add the files to the copy queues */ + cache->copy_source_queue = g_list_prepend (cache->copy_source_queue, + g_object_ref (source_file)); + cache->copy_target_queue = g_list_prepend (cache->copy_target_queue, + g_object_ref (target_file)); + + /* process the copy queue in a 250ms timeout */ + cache->copy_queue_idle_id = + g_timeout_add (500, (GSourceFunc) thunar_thumbnail_cache_process_copy_queue, + cache); + } + + /* release the cache lock */ + g_mutex_unlock (cache->lock); +#endif +} + + + +void +thunar_thumbnail_cache_delete_file (ThunarThumbnailCache *cache, + GFile *file) +{ + _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache)); + _thunar_return_if_fail (G_IS_FILE (file)); + +#ifdef HAVE_DBUS + /* acquire a cache lock */ + g_mutex_lock (cache->lock); + + /* check if we have a valid proxy for the cache service */ + if (cache->cache_proxy) + { + /* cancel any pending timeout to process the delete queue */ + if (cache->delete_queue_idle_id > 0) + { + g_source_remove (cache->delete_queue_idle_id); + cache->delete_queue_idle_id = 0; + } + + /* add the file to the delete queue */ + cache->delete_queue = g_list_prepend (cache->delete_queue, g_object_ref (file)); + + /* process the delete queue in a 250ms timeout */ + cache->delete_queue_idle_id = + g_timeout_add (500, (GSourceFunc) thunar_thumbnail_cache_process_delete_queue, + cache); + } + + /* release the cache lock */ + g_mutex_unlock (cache->lock); +#endif +} + + + +void +thunar_thumbnail_cache_cleanup_file (ThunarThumbnailCache *cache, + GFile *file) +{ + _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache)); + _thunar_return_if_fail (G_IS_FILE (file)); + +#ifdef HAVE_DBUS + /* acquire a cache lock */ + g_mutex_lock (cache->lock); + + /* check if we have a valid proxy for the cache service */ + if (cache->cache_proxy) + { + /* cancel any pending timeout to process the cleanup queue */ + if (cache->cleanup_queue_idle_id > 0) + { + g_source_remove (cache->cleanup_queue_idle_id); + cache->cleanup_queue_idle_id = 0; + } + + /* add the file to the cleanup queue */ + cache->cleanup_queue = g_list_prepend (cache->cleanup_queue, g_object_ref (file)); + + /* process the cleanup queue in a 250ms timeout */ + cache->cleanup_queue_idle_id = + g_timeout_add (1000, (GSourceFunc) thunar_thumbnail_cache_process_cleanup_queue, + cache); + } + + /* release the cache lock */ + g_mutex_unlock (cache->lock); +#endif +} diff --git a/thunar/thunar-thumbnail-cache.h b/thunar/thunar-thumbnail-cache.h new file mode 100644 index 0000000000000000000000000000000000000000..dc32fc8090fe0aedfa143f1b05908d6c9aef227a --- /dev/null +++ b/thunar/thunar-thumbnail-cache.h @@ -0,0 +1,56 @@ +/* vi:set et ai sw=2 sts=2 ts=2: */ +/*- + * Copyright (c) 2011 Jannis Pohlmann <jannis@xfce.org> + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __THUNAR_THUMBNAIL_CACHE_H__ +#define __THUNAR_THUMBNAIL_CACHE_H__ + +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define THUNAR_TYPE_THUMBNAIL_CACHE (thunar_thumbnail_cache_get_type ()) +#define THUNAR_THUMBNAIL_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_THUMBNAIL_CACHE, ThunarThumbnailCache)) +#define THUNAR_THUMBNAIL_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_THUMBNAIL_CACHE, ThunarThumbnailCacheClass)) +#define THUNAR_IS_THUMBNAIL_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_THUMBNAIL_CACHE)) +#define THUNAR_IS_THUMBNAIL_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_THUMBNAIL_CACHE) +#define THUNAR_THUMBNAIL_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_THUMBNAIL_CACHE, ThunarThumbnailCacheClass)) + +typedef struct _ThunarThumbnailCachePrivate ThunarThumbnailCachePrivate; +typedef struct _ThunarThumbnailCacheClass ThunarThumbnailCacheClass; +typedef struct _ThunarThumbnailCache ThunarThumbnailCache; + +GType thunar_thumbnail_cache_get_type (void) G_GNUC_CONST; + +ThunarThumbnailCache *thunar_thumbnail_cache_new (void) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; + +void thunar_thumbnail_cache_move_file (ThunarThumbnailCache *cache, + GFile *source_file, + GFile *target_file); +void thunar_thumbnail_cache_copy_file (ThunarThumbnailCache *cache, + GFile *source_file, + GFile *target_file); +void thunar_thumbnail_cache_delete_file (ThunarThumbnailCache *cache, + GFile *file); +void thunar_thumbnail_cache_cleanup_file (ThunarThumbnailCache *cache, + GFile *file); + +G_END_DECLS + +#endif /* !__THUNAR_THUMBNAIL_CACHE_H__ */ diff --git a/thunar/thunar-thumbnailer-dbus.xml b/thunar/thunar-thumbnailer-dbus.xml index e6b39f7397ce9ca988fbd026bf7f72f2f65ea9ab..d0d2ada0010ba6b2102dab8a1f61c773a61b94f8 100644 --- a/thunar/thunar-thumbnailer-dbus.xml +++ b/thunar/thunar-thumbnailer-dbus.xml @@ -11,7 +11,7 @@ <arg type="u" name="handle" direction="out" /> </method> - <method name="Unqueue"> + <method name="Dequeue"> <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/> <arg type="u" name="handle" direction="in" /> </method> diff --git a/thunar/thunar-thumbnailer.c b/thunar/thunar-thumbnailer.c index 8146d29066289ace8349637ab01d23b599602f8e..32d6218a15d6b538cbd7fdd6206813a53efcb451 100644 --- a/thunar/thunar-thumbnailer.c +++ b/thunar/thunar-thumbnailer.c @@ -1,6 +1,6 @@ /* vi:set et ai sw=2 sts=2 ts=2: */ /*- - * Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org> + * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -47,58 +47,21 @@ * Please note that all D-Bus calls are performed asynchronously. * * - * Queue - * ===== - * - * ThunarThumbnailer maintains a wait queue to group individual thumbnail requests. - * The wait queue is processed at most every 100ms. This is done to reduce the - * overall D-Bus noise when dealing with a lot of requests. The more thumbnails are - * requested in a 100ms time slot, the less D-Bus methods are sent. - * - * Let N be the number of requests made for individual files in a 100ms slot. - * Compared to sending out one requests per file (which generates 4*N D-Bus messages, - * 1 Queue, 1 Started, 1 Ready/Error and 1 Finished for each of the N files), the wait - * queue technique only causes 3+N D-Bus messages to be sent (1 Queue, 1 Started, - * N Ready/Error and 1 Finished). This can be further improved on the service side - * if the D-Bus thumbnailer groups Ready/Error messages (which of course has drawbacks - * with regards to the overall thumbnailing responsiveness). - * - * Before a URI is added to the wait queue, it is checked whether it isn't already - * 1) in the wait queue, 2) part of a pending or active request or 3) part of a - * finished request which has an idle function pending. - * - * When a request call is finally sent out, an internal request ID is created and + * When a request call is sent out, an internal request ID is created and * associated with the corresponding DBusGProxyCall via the request_call_mapping hash - * table. It also remembers the URIs for the internal request ID in the - * request_uris_mapping hash table. + * table. * * The D-Bus reply handler then checks if there was an delivery error or * not. If the request method was sent successfully, the handle returned by the * D-Bus thumbnailer is associated bidirectionally with the internal request ID via - * the request_handle_mapping and handle_request_mappings. If the request could - * not be sent at all, the URIs array is dropped from request_uris_mapping. In - * both cases, the association of the internal request ID with the DBusGProxyCall - * is removed from request_call_mapping. + * the request_handle_mapping and handle_request_mappings. In both cases, the + * association of the internal request ID with the DBusGProxyCall is removed from + * request_call_mapping. * - * These hash tables play a major role in the Started, Finished, Error and Ready + * These hash tables play a major role in the Finished, Error and Ready * signal handlers. * * - * Started - * ======= - * - * When a Started signal is emitted by the D-Bus thumbnailer, ThunarThumbnailer - * receives the handle and looks up the corresponding internal request ID. If - * it exists (which it should), it schedules an idle function to handle the - * signal in the application's main loop. - * - * The idle function then looks up the URIs array for the request ID from the - * request_uris_mapping. For each of these URIs the corresponding ThunarFile - * is looked up from the file cache (which represents files currently being - * used somewhere in the UI), and if the ThunarFile exists in the cache it's - * thumb state is set to _LOADING (unless it's already marked as _READY). - * - * * Ready / Error * ============= * @@ -112,12 +75,12 @@ * * The Finished signal handler looks up the internal request ID based on * the D-Bus thumbnailer handle. It then drops all corresponding information - * from handle_request_mapping, request_handle_mapping and request_uris_mapping. + * from handle_request_mapping and request_handle_mapping. */ -#if HAVE_DBUS +#ifdef HAVE_DBUS typedef enum { THUNAR_THUMBNAILER_IDLE_ERROR, @@ -153,20 +116,13 @@ static void thunar_thumbnailer_thumbnailer_ready (DBusGPr guint32 handle, const gchar **uris, ThunarThumbnailer *thumbnailer); -static void thunar_thumbnailer_thumbnailer_started (DBusGProxy *proxy, - guint handle, - ThunarThumbnailer *thumbnailer); -static gpointer thunar_thumbnailer_queue_async (ThunarThumbnailer *thumbnailer, +static guint thunar_thumbnailer_queue_async (ThunarThumbnailer *thumbnailer, gchar **uris, const gchar **mime_hints); static gboolean thunar_thumbnailer_error_idle (gpointer user_data); static gboolean thunar_thumbnailer_ready_idle (gpointer user_data); -static gboolean thunar_thumbnailer_started_idle (gpointer user_data); static void thunar_thumbnailer_call_free (ThunarThumbnailerCall *call); static void thunar_thumbnailer_idle_free (gpointer data); -static ThunarThumbnailerItem *thunar_thumbnailer_item_new (GFile *file, - const gchar *mime_hint); -static void thunar_thumbnailer_item_free (gpointer data); #endif @@ -184,10 +140,6 @@ struct _ThunarThumbnailer /* proxies to communicate with D-Bus services */ DBusGProxy *thumbnailer_proxy; - /* wait queue used to delay (and thereby group) thumbnail requests */ - GList *wait_queue; - guint wait_queue_idle_id; - /* hash table to map D-Bus service handles to ThunarThumbnailer requests */ GHashTable *handle_request_mapping; @@ -197,9 +149,6 @@ struct _ThunarThumbnailer /* hash table to map ThunarThumbnailer requests to DBusGProxyCalls */ GHashTable *request_call_mapping; - /* hash table to map ThunarThumbnailer requests to URI arrays */ - GHashTable *request_uris_mapping; - GMutex *lock; /* cached arrays of URI schemes and MIME types for which thumbnails @@ -208,7 +157,7 @@ struct _ThunarThumbnailer gchar **supported_types; /* last ThunarThumbnailer request ID */ - gpointer last_request; + guint last_request; /* IDs of idle functions */ GList *idles; @@ -244,9 +193,6 @@ struct _ThunarThumbnailerItem -#ifdef HAVE_DBUS -static DBusGProxy *thunar_thumbnailer_proxy; -#endif @@ -274,9 +220,8 @@ thunar_thumbnailer_init (ThunarThumbnailer *thumbnailer) thumbnailer->lock = g_mutex_new (); thumbnailer->supported_schemes = NULL; thumbnailer->supported_types = NULL; - thumbnailer->last_request = GUINT_TO_POINTER (0); + thumbnailer->last_request = 0; thumbnailer->idles = NULL; - thumbnailer->wait_queue_idle_id = 0; /* try to connect to D-Bus */ connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); @@ -294,9 +239,6 @@ thunar_thumbnailer_init (ThunarThumbnailer *thumbnailer) g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); thumbnailer->request_call_mapping = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); - thumbnailer->request_uris_mapping = - g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, - (GDestroyNotify) g_strfreev); } /* release the D-Bus connection if we have one */ @@ -319,14 +261,6 @@ thunar_thumbnailer_finalize (GObject *object) /* acquire the thumbnailer lock */ g_mutex_lock (thumbnailer->lock); - /* clear the request queue */ - g_list_foreach (thumbnailer->wait_queue, (GFunc) thunar_thumbnailer_item_free, NULL); - g_list_free (thumbnailer->wait_queue); - - /* remove the request queue processing idle handler */ - if (thumbnailer->wait_queue_idle_id > 0) - g_source_remove (thumbnailer->wait_queue_idle_id); - /* abort all pending idle functions */ for (lp = thumbnailer->idles; lp != NULL; lp = lp->next) { @@ -348,16 +282,15 @@ thunar_thumbnailer_finalize (GObject *object) g_hash_table_unref (thumbnailer->request_call_mapping); #if 0 - /* unqueue all pending requests */ + /* dequeue all pending requests */ list = g_hash_table_get_keys (thumbnailer->handle_request_mapping); for (lp = list; lp != NULL; lp = lp->next) - thunar_thumbnailer_unqueue_internal (thumbnailer, GPOINTER_TO_UINT (lp->data)); + thunar_thumbnailer_dequeue_internal (thumbnailer, GPOINTER_TO_UINT (lp->data)); g_list_free (list); #endif g_hash_table_unref (thumbnailer->handle_request_mapping); g_hash_table_unref (thumbnailer->request_handle_mapping); - g_hash_table_unref (thumbnailer->request_uris_mapping); /* disconnect from the thumbnailer proxy */ g_signal_handlers_disconnect_matched (thumbnailer->thumbnailer_proxy, @@ -396,51 +329,37 @@ thunar_thumbnailer_init_thumbnailer_proxy (ThunarThumbnailer *thumbnailer, return; } - /* create the thumbnailer proxy shared by all ThunarThumbnailers on demand */ - if (thunar_thumbnailer_proxy == NULL) - { - /* create the shared thumbnailer proxy */ - thunar_thumbnailer_proxy = - dbus_g_proxy_new_for_name (connection, - "org.freedesktop.thumbnails.Thumbnailer1", - "/org/freedesktop/thumbnails/Thumbnailer1", - "org.freedesktop.thumbnails.Thumbnailer1"); - - /* make sure to set it to NULL when the last reference is dropped */ - g_object_add_weak_pointer (G_OBJECT (thunar_thumbnailer_proxy), - (gpointer) &thunar_thumbnailer_proxy); - - thumbnailer->thumbnailer_proxy = thunar_thumbnailer_proxy; - - /* TODO this should actually be VOID:UINT,BOXED,INT,STRING */ - dbus_g_object_register_marshaller (_thunar_marshal_VOID__UINT_BOXED_UINT_STRING, - G_TYPE_NONE, - G_TYPE_UINT, - G_TYPE_STRV, - G_TYPE_UINT, - G_TYPE_STRING, - G_TYPE_INVALID); - - dbus_g_object_register_marshaller ((GClosureMarshal) _thunar_marshal_VOID__UINT_BOXED, - G_TYPE_NONE, - G_TYPE_UINT, - G_TYPE_STRV, - G_TYPE_INVALID); - - dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Error", - G_TYPE_UINT, G_TYPE_STRV, G_TYPE_UINT, G_TYPE_STRING, - G_TYPE_INVALID); - dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Finished", - G_TYPE_UINT, G_TYPE_INVALID); - dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Ready", - G_TYPE_UINT, G_TYPE_STRV, G_TYPE_INVALID); - dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Started", - G_TYPE_UINT, G_TYPE_INVALID); - } - else - { - thumbnailer->thumbnailer_proxy = g_object_ref (thunar_thumbnailer_proxy); - } + /* create the thumbnailer proxy */ + thumbnailer->thumbnailer_proxy = + dbus_g_proxy_new_for_name (connection, + "org.freedesktop.thumbnails.Thumbnailer1", + "/org/freedesktop/thumbnails/Thumbnailer1", + "org.freedesktop.thumbnails.Thumbnailer1"); + + /* TODO this should actually be VOID:UINT,BOXED,INT,STRING */ + dbus_g_object_register_marshaller (_thunar_marshal_VOID__UINT_BOXED_UINT_STRING, + G_TYPE_NONE, + G_TYPE_UINT, + G_TYPE_STRV, + G_TYPE_UINT, + G_TYPE_STRING, + G_TYPE_INVALID); + + dbus_g_object_register_marshaller ((GClosureMarshal) _thunar_marshal_VOID__UINT_BOXED, + G_TYPE_NONE, + G_TYPE_UINT, + G_TYPE_STRV, + G_TYPE_INVALID); + + dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Error", + G_TYPE_UINT, G_TYPE_STRV, G_TYPE_UINT, G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Finished", + G_TYPE_UINT, G_TYPE_INVALID); + dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Ready", + G_TYPE_UINT, G_TYPE_STRV, G_TYPE_INVALID); + dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Started", + G_TYPE_UINT, G_TYPE_INVALID); dbus_g_proxy_connect_signal (thumbnailer->thumbnailer_proxy, "Error", G_CALLBACK (thunar_thumbnailer_thumbnailer_error), @@ -451,9 +370,6 @@ thunar_thumbnailer_init_thumbnailer_proxy (ThunarThumbnailer *thumbnailer, dbus_g_proxy_connect_signal (thumbnailer->thumbnailer_proxy, "Ready", G_CALLBACK (thunar_thumbnailer_thumbnailer_ready), thumbnailer, NULL); - dbus_g_proxy_connect_signal (thumbnailer->thumbnailer_proxy, "Started", - G_CALLBACK (thunar_thumbnailer_thumbnailer_started), - thumbnailer, NULL); } @@ -576,12 +492,11 @@ thunar_thumbnailer_thumbnailer_finished (DBusGProxy *proxy, GUINT_TO_POINTER (handle)); /* check if we have a request for this handle */ - if (request != NULL) + if (GPOINTER_TO_UINT (request) > 0) { /* the request is finished, drop all the information about it */ g_hash_table_remove (thumbnailer->handle_request_mapping, request); g_hash_table_remove (thumbnailer->request_handle_mapping, request); - g_hash_table_remove (thumbnailer->request_uris_mapping, request); } } @@ -621,44 +536,6 @@ thunar_thumbnailer_thumbnailer_ready (DBusGProxy *proxy, -static void -thunar_thumbnailer_thumbnailer_started (DBusGProxy *proxy, - guint handle, - ThunarThumbnailer *thumbnailer) -{ - ThunarThumbnailerIdle *idle; - gpointer request; - - _thunar_return_if_fail (DBUS_IS_G_PROXY (proxy)); - _thunar_return_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer)); - - /* look up the request for this D-Bus service handle */ - request = g_hash_table_lookup (thumbnailer->handle_request_mapping, - GUINT_TO_POINTER (handle)); - - /* check if we have a request for this handle */ - if (request != NULL) - { - /* allocate a new idle struct */ - idle = g_slice_new0 (ThunarThumbnailerIdle); - idle->type = THUNAR_THUMBNAILER_IDLE_STARTED; - idle->thumbnailer = g_object_ref (thumbnailer); - - /* remember the request because we need it in the idle function */ - idle->data.request = request; - - /* remember the idle struct because we might have to remove it in finalize() */ - thumbnailer->idles = g_list_prepend (thumbnailer->idles, idle); - - /* call the started idle function when we have the time */ - idle->id = g_idle_add_full (G_PRIORITY_LOW, - thunar_thumbnailer_started_idle, idle, - thunar_thumbnailer_idle_free); - } -} - - - static void thunar_thumbnailer_queue_async_reply (DBusGProxy *proxy, guint handle, @@ -667,9 +544,6 @@ thunar_thumbnailer_queue_async_reply (DBusGProxy *proxy, { ThunarThumbnailerCall *call = user_data; ThunarThumbnailer *thumbnailer = THUNAR_THUMBNAILER (call->thumbnailer); -#ifndef NDEBUG - gchar **uris; -#endif _thunar_return_if_fail (DBUS_IS_G_PROXY (proxy)); _thunar_return_if_fail (call != NULL); @@ -677,20 +551,7 @@ thunar_thumbnailer_queue_async_reply (DBusGProxy *proxy, g_mutex_lock (thumbnailer->lock); - if (error != NULL) - { -#ifndef NDEBUG - /* get the URIs array for this request */ - uris = g_hash_table_lookup (thumbnailer->request_uris_mapping, call->request); - - /* the array should always exist, otherwise there's a bug in the program */ - _thunar_assert (uris != NULL); -#endif - - /* the request is "finished", forget about its URIs */ - g_hash_table_remove (thumbnailer->request_uris_mapping, call->request); - } - else + if (error == NULL) { /* remember that this request and D-Bus handle belong together */ g_hash_table_insert (thumbnailer->request_handle_mapping, @@ -709,7 +570,7 @@ thunar_thumbnailer_queue_async_reply (DBusGProxy *proxy, -static gpointer +static guint thunar_thumbnailer_queue_async (ThunarThumbnailer *thumbnailer, gchar **uris, const gchar **mime_hints) @@ -725,23 +586,20 @@ thunar_thumbnailer_queue_async (ThunarThumbnailer *thumbnailer, _thunar_return_val_if_fail (DBUS_IS_G_PROXY (thumbnailer->thumbnailer_proxy), 0); /* compute the next request ID, making sure it's never 0 */ - request_no = GPOINTER_TO_UINT (thumbnailer->last_request) + 1; + request_no = thumbnailer->last_request + 1; request_no = MAX (request_no, 1); /* remember the ID for the next request */ - thumbnailer->last_request = GUINT_TO_POINTER (request_no); + thumbnailer->last_request = request_no; - /* use the new request ID for this request */ - request = thumbnailer->last_request; + /* use the newly generated ID for this request */ + request = GUINT_TO_POINTER (request_no); /* allocate a new call struct for the async D-Bus call */ thumbnailer_call = g_slice_new0 (ThunarThumbnailerCall); thumbnailer_call->request = request; thumbnailer_call->thumbnailer = g_object_ref (thumbnailer); - /* remember the URIs for this request */ - g_hash_table_insert (thumbnailer->request_uris_mapping, request, uris); - /* queue thumbnails for the given URIs asynchronously */ call = thunar_thumbnailer_proxy_queue_async (thumbnailer->thumbnailer_proxy, (const gchar **)uris, mime_hints, @@ -753,7 +611,7 @@ thunar_thumbnailer_queue_async (ThunarThumbnailer *thumbnailer, g_hash_table_insert (thumbnailer->request_call_mapping, request, call); /* return the request ID used for this request */ - return request; + return request_no; } @@ -836,224 +694,6 @@ thunar_thumbnailer_ready_idle (gpointer user_data) -static gboolean -thunar_thumbnailer_started_idle (gpointer user_data) -{ - ThunarThumbnailerIdle *idle = user_data; - const gchar **uris; - ThunarFile *file; - GFile *gfile; - guint n; - - _thunar_return_val_if_fail (idle != NULL, FALSE); - _thunar_return_val_if_fail (idle->type == THUNAR_THUMBNAILER_IDLE_STARTED, FALSE); - - g_mutex_lock (idle->thumbnailer->lock); - - /* look up the URIs that belong to this request */ - uris = g_hash_table_lookup (idle->thumbnailer->request_uris_mapping, - idle->data.request); - - /* iterate over all URIs if there are any */ - for (n = 0; uris != NULL && uris[n] != NULL; ++n) - { - /* look up the corresponding ThunarFile from the cache */ - gfile = g_file_new_for_uri (uris[n]); - file = thunar_file_cache_lookup (gfile); - g_object_unref (gfile); - - /* check if we have a file in the cache */ - if (file != NULL) - { - /* set the thumbnail state to loading unless we already have a thumbnail. - * This is to prevent race conditions with the other idle functions */ - if (thunar_file_get_thumb_state (file) != THUNAR_FILE_THUMB_STATE_READY) - thunar_file_set_thumb_state (file, THUNAR_FILE_THUMB_STATE_LOADING); - } - } - - - /* remove the idle struct */ - idle->thumbnailer->idles = g_list_remove (idle->thumbnailer->idles, idle); - - g_mutex_unlock (idle->thumbnailer->lock); - - /* remove the idle source, which also destroys the idle struct */ - return FALSE; -} - - - -static gboolean -thunar_thumbnailer_file_is_in_wait_queue (ThunarThumbnailer *thumbnailer, - ThunarFile *file) -{ - ThunarThumbnailerItem *item; - gboolean in_wait_queue = FALSE; - GList *lp; - - g_mutex_lock (thumbnailer->lock); - - for (lp = thumbnailer->wait_queue; !in_wait_queue && lp != NULL; lp = lp->next) - { - item = lp->data; - - if (g_file_equal (item->file, thunar_file_get_file (file))) - in_wait_queue = TRUE; - } - - g_mutex_unlock (thumbnailer->lock); - - return in_wait_queue; -} - - - -static gboolean -thunar_thumbnailer_process_wait_queue (ThunarThumbnailer *thumbnailer) -{ - ThunarThumbnailerItem *item; - gpointer request; - GList *lp; - gchar **mime_hints; - gchar **uris; - guint n_items; - guint n; - - _thunar_return_val_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer), FALSE); - - g_mutex_lock (thumbnailer->lock); - - /* determine how many URIs are in the wait queue */ - n_items = g_list_length (thumbnailer->wait_queue); - - /* allocate arrays for URIs and mime hints */ - uris = g_new0 (gchar *, n_items + 1); - mime_hints = g_new0 (gchar *, n_items + 1); - - /* fill URI and MIME hint arrays with items from the wait queue */ - for (lp = g_list_last (thumbnailer->wait_queue), n = 0; lp != NULL; lp = lp->prev, ++n) - { - /* fetch the next item from the queue */ - item = lp->data; - - /* save URI and MIME hint in the arrays */ - uris[n] = g_file_get_uri (item->file); - mime_hints[n] = item->mime_hint; - - /* destroy the GFile and the queue item. The MIME hints are free'd later */ - g_object_unref (item->file); - g_slice_free (ThunarThumbnailerItem, item); - } - - /* NULL-terminate both arrays */ - uris[n] = NULL; - mime_hints[n] = NULL; - - /* queue a thumbnail request for the URIs from the wait queue */ - request = thunar_thumbnailer_queue_async (thumbnailer, uris, - (const gchar **)mime_hints); - - /* free mime hints array */ - g_strfreev (mime_hints); - - /* clear the wait queue */ - g_list_free (thumbnailer->wait_queue); - thumbnailer->wait_queue = NULL; - - /* reset the wait queue idle ID */ - thumbnailer->wait_queue_idle_id = 0; - - g_mutex_unlock (thumbnailer->lock); - - return FALSE; -} - - - -static gboolean -thunar_thumbnailer_file_is_queued (ThunarThumbnailer *thumbnailer, - ThunarFile *file) -{ - gboolean is_queued = FALSE; - GList *values; - GList *lp; - gchar **uris; - gchar *uri; - guint n; - - /* get a list with all URI arrays of already queued requests */ - values = g_hash_table_get_values (thumbnailer->request_uris_mapping); - - /* if we have none, the file cannot be queued ... or can it? ;) */ - if (values == NULL) - return FALSE; - - /* determine the URI for this file */ - uri = thunar_file_dup_uri (file); - - /* iterate over all URI arrays */ - for (lp = values; !is_queued && lp != NULL; lp = lp->next) - { - uris = lp->data; - - /* check if the file is included in the URI array of the current request */ - for (n = 0; !is_queued && uris != NULL && uris[n] != NULL; ++n) - if (g_utf8_collate (uri, uris[n]) == 0) - is_queued = TRUE; - } - - /* free the file URI */ - g_free (uri); - - /* free the URI array list */ - g_list_free (values); - - return is_queued; -} - - - -static gboolean -thunar_thumbnailer_file_is_ready (ThunarThumbnailer *thumbnailer, - ThunarFile *file) -{ - ThunarThumbnailerIdle *idle; - gboolean is_ready = FALSE; - GList *lp; - gchar *uri; - guint n; - - /* determine the URI or this file */ - uri = thunar_file_dup_uri (file); - - /* iterate over all idle structs */ - for (lp = thumbnailer->idles; !is_ready && lp != NULL; lp = lp->next) - { - /* skip invalid idles */ - if (lp->data != NULL) - continue; - - idle = lp->data; - - /* skip non-ready idles and idles without any URIs */ - if (idle->type != THUNAR_THUMBNAILER_IDLE_READY || idle->data.uris == NULL) - continue; - - /* check if the file is included in this ready idle */ - for (n = 0; !is_ready && idle->data.uris[n] != NULL; ++n) - if (g_utf8_collate (uri, idle->data.uris[n]) == 0) - is_ready = TRUE; - } - - /* free the file URI */ - g_free (uri); - - return is_ready; -} - - - static void thunar_thumbnailer_call_free (ThunarThumbnailerCall *call) { @@ -1088,35 +728,6 @@ thunar_thumbnailer_idle_free (gpointer data) /* free the struct */ g_slice_free (ThunarThumbnailerIdle, idle); } - - - -static ThunarThumbnailerItem * -thunar_thumbnailer_item_new (GFile *file, - const gchar *mime_hint) -{ - ThunarThumbnailerItem *item; - - _thunar_return_val_if_fail (G_IS_FILE (file), NULL); - _thunar_return_val_if_fail (mime_hint != NULL && mime_hint != '\0', NULL); - - item = g_slice_new0 (ThunarThumbnailerItem); - item->file = g_object_ref (file); - item->mime_hint = g_strdup (mime_hint); - - return item; -} - - -static void -thunar_thumbnailer_item_free (gpointer data) -{ - ThunarThumbnailerItem *item = data; - - g_object_unref (item->file); - g_free (item->mime_hint); - g_slice_free (ThunarThumbnailerItem, item); -} #endif /* HAVE_DBUS */ @@ -1142,7 +753,8 @@ thunar_thumbnailer_new (void) gboolean thunar_thumbnailer_queue_file (ThunarThumbnailer *thumbnailer, - ThunarFile *file) + ThunarFile *file, + guint *request) { GList files; @@ -1155,22 +767,24 @@ thunar_thumbnailer_queue_file (ThunarThumbnailer *thumbnailer, files.prev = NULL; /* queue a thumbnail request for the file */ - return thunar_thumbnailer_queue_files (thumbnailer, &files); + return thunar_thumbnailer_queue_files (thumbnailer, &files, request); } gboolean thunar_thumbnailer_queue_files (ThunarThumbnailer *thumbnailer, - GList *files) + GList *files, + guint *request) { + gboolean success = FALSE; #ifdef HAVE_DBUS - ThunarThumbnailerItem *item; -#endif - gboolean success = FALSE; -#ifdef HAVE_DBUS - GList *lp; - GList *supported_files = NULL; + const gchar **mime_hints; + gchar **uris; + GList *lp; + GList *supported_files = NULL; + guint n; + guint n_items; #endif _thunar_return_val_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer), FALSE); @@ -1194,44 +808,49 @@ thunar_thumbnailer_queue_files (ThunarThumbnailer *thumbnailer, * about to be queued (wait queue), nor already queued, nor already * processed (and awaiting to be refreshed) */ for (lp = g_list_last (files); lp != NULL; lp = lp->prev) - if (thunar_thumbnailer_file_is_supported (thumbnailer, lp->data)) - { - if (!thunar_thumbnailer_file_is_in_wait_queue (thumbnailer, lp->data) - && !thunar_thumbnailer_file_is_queued (thumbnailer, lp->data) - && !thunar_thumbnailer_file_is_ready (thumbnailer, lp->data)) - { - supported_files = g_list_prepend (supported_files, lp->data); - } - } + { + if (thunar_thumbnailer_file_is_supported (thumbnailer, lp->data)) + supported_files = g_list_prepend (supported_files, lp->data); + } + + /* determine how many URIs are in the wait queue */ + n_items = g_list_length (supported_files); /* check if we have any supported files */ - if (supported_files != NULL) + if (n_items > 0) { - for (lp = supported_files; lp != NULL; lp = lp->next) - { - g_mutex_lock (thumbnailer->lock); - - /* allocate a thumbnailer item for the wait queue */ - item = thunar_thumbnailer_item_new (thunar_file_get_file (lp->data), - thunar_file_get_content_type (lp->data)); + /* allocate arrays for URIs and mime hints */ + uris = g_new0 (gchar *, n_items + 1); + mime_hints = g_new0 (const gchar *, n_items + 1); - /* add the item to the wait queue */ - thumbnailer->wait_queue = g_list_prepend (thumbnailer->wait_queue, item); + /* fill URI and MIME hint arrays with items from the wait queue */ + for (lp = g_list_last (supported_files), n = 0; lp != NULL; lp = lp->prev, ++n) + { + /* set the thumbnail state to loading */ + thunar_file_set_thumb_state (lp->data, THUNAR_FILE_THUMB_STATE_LOADING); - g_mutex_unlock (thumbnailer->lock); + /* save URI and MIME hint in the arrays */ + uris[n] = thunar_file_dup_uri (lp->data); + mime_hints[n] = thunar_file_get_content_type (lp->data); } + /* NULL-terminate both arrays */ + uris[n] = NULL; + mime_hints[n] = NULL; + g_mutex_lock (thumbnailer->lock); - if (thumbnailer->wait_queue_idle_id == 0) - { - thumbnailer->wait_queue_idle_id = - g_timeout_add (100, (GSourceFunc) thunar_thumbnailer_process_wait_queue, - thumbnailer); - } + /* queue a thumbnail request for the URIs from the wait queue */ + if (request != NULL) + *request = thunar_thumbnailer_queue_async (thumbnailer, uris, mime_hints); + else + thunar_thumbnailer_queue_async (thumbnailer, uris, mime_hints); g_mutex_unlock (thumbnailer->lock); - + + /* free mime hints array */ + g_free (mime_hints); + /* free the list of supported files */ g_list_free (supported_files); @@ -1244,35 +863,42 @@ thunar_thumbnailer_queue_files (ThunarThumbnailer *thumbnailer, } -#if 0 -static void -thunar_thumbnailer_unqueue (ThunarThumbnailer *thumbnailer, - gpointer request) +void +thunar_thumbnailer_dequeue (ThunarThumbnailer *thumbnailer, + guint request) { #ifdef HAVE_DBUS + gpointer request_ptr; gpointer handle; #endif _thunar_return_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer)); #ifdef HAVE_DBUS + /* convert the number to a pointer */ + request_ptr = GUINT_TO_POINTER (request); + /* acquire the thumbnailer lock */ g_mutex_lock (thumbnailer->lock); + /* check if we have a valid thumbnailer proxy */ if (thumbnailer->thumbnailer_proxy != NULL) { - handle = g_hash_table_lookup (thumbnailer->request_handle_mapping, request); - - thunar_thumbnailer_proxy_unqueue (thumbnailer->thumbnailer_proxy, - GPOINTER_TO_UINT (handle), NULL); + /* check if there is a pending tumbler request handle for this request */ + handle = g_hash_table_lookup (thumbnailer->request_handle_mapping, request_ptr); + if (GPOINTER_TO_UINT (handle) > 0) + { + /* Dequeue the request */ + thunar_thumbnailer_proxy_dequeue (thumbnailer->thumbnailer_proxy, + GPOINTER_TO_UINT (handle), NULL); - g_hash_table_remove (thumbnailer->handle_request_mapping, handle); - g_hash_table_remove (thumbnailer->request_handle_mapping, request); - g_hash_table_remove (thumbnailer->request_uris_mapping, request); + /* drop all the request information */ + g_hash_table_remove (thumbnailer->handle_request_mapping, handle); + g_hash_table_remove (thumbnailer->request_handle_mapping, request_ptr); + } } /* release the thumbnailer lock */ g_mutex_unlock (thumbnailer->lock); #endif } -#endif diff --git a/thunar/thunar-thumbnailer.h b/thunar/thunar-thumbnailer.h index 83fa4ce6d2eed58e30a4e72377c1bc8939e37d5c..81caf8328ba31ed0df66e0d78e77f768d0b06cbd 100644 --- a/thunar/thunar-thumbnailer.h +++ b/thunar/thunar-thumbnailer.h @@ -1,20 +1,21 @@ -/* $Id$ */ +/* vi:set et ai sw=2 sts=2 ts=2: */ /*- - * Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org> + * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org> * - * 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 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. + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __THUNAR_THUMBNAILER_H__ @@ -22,7 +23,7 @@ #include <thunar/thunar-file.h> -G_BEGIN_DECLS; +G_BEGIN_DECLS typedef struct _ThunarThumbnailerClass ThunarThumbnailerClass; typedef struct _ThunarThumbnailer ThunarThumbnailer; @@ -34,15 +35,19 @@ typedef struct _ThunarThumbnailer ThunarThumbnailer; #define THUNAR_IS_THUMBNAILER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_THUMBNAILER)) #define THUNAR_THUMBNAILER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_THUMBNAILER, ThunarThumbnailerClass)) -GType thunar_thumbnailer_get_type (void) G_GNUC_CONST; +GType thunar_thumbnailer_get_type (void) G_GNUC_CONST; -ThunarThumbnailer *thunar_thumbnailer_new (void) G_GNUC_MALLOC; +ThunarThumbnailer *thunar_thumbnailer_new (void) G_GNUC_MALLOC; -gboolean thunar_thumbnailer_queue_file (ThunarThumbnailer *generator, - ThunarFile *file); -gboolean thunar_thumbnailer_queue_files (ThunarThumbnailer *generator, - GList *files); +gboolean thunar_thumbnailer_queue_file (ThunarThumbnailer *thumbnailer, + ThunarFile *file, + guint *request); +gboolean thunar_thumbnailer_queue_files (ThunarThumbnailer *thumbnailer, + GList *files, + guint *request); +void thunar_thumbnailer_dequeue (ThunarThumbnailer *thumbnailer, + guint request); -G_END_DECLS; +G_END_DECLS #endif /* !__THUNAR_THUMBNAILER_H__ */ diff --git a/thunar/thunar-transfer-job.c b/thunar/thunar-transfer-job.c index 364bd4ec624c7bfa25901ec4a1e6e5554cb3a877..f77839f396f854f28aa3d8f9bc940f50739a3d2d 100644 --- a/thunar/thunar-transfer-job.c +++ b/thunar/thunar-transfer-job.c @@ -1,22 +1,22 @@ -/* vi:set sw=2 sts=2 ts=2 et ai: */ +/* vi:set et ai sw=2 sts=2 ts=2: */ /*- * Copyright (c) 2005-2007 Benedikt Meurer <benny@xfce.org> * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org> * - * 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 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. + * 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 + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H @@ -25,11 +25,13 @@ #include <gio/gio.h> +#include <thunar/thunar-application.h> #include <thunar/thunar-gio-extensions.h> #include <thunar/thunar-io-scan-directory.h> #include <thunar/thunar-io-jobs-util.h> #include <thunar/thunar-job.h> #include <thunar/thunar-private.h> +#include <thunar/thunar-thumbnail-cache.h> #include <thunar/thunar-transfer-job.h> @@ -481,11 +483,13 @@ thunar_transfer_job_copy_node (ThunarTransferJob *job, GList **target_file_list_return, GError **error) { - ThunarJobResponse response; - GFileInfo *info; - GError *err = NULL; - GFile *real_target_file = NULL; - gchar *base_name; + ThunarThumbnailCache *thumbnail_cache; + ThunarApplication *application; + ThunarJobResponse response; + GFileInfo *info; + GError *err = NULL; + GFile *real_target_file = NULL; + gchar *base_name; _thunar_return_if_fail (THUNAR_IS_TRANSFER_JOB (job)); _thunar_return_if_fail (node != NULL && G_IS_FILE (node->source_file)); @@ -498,6 +502,11 @@ thunar_transfer_job_copy_node (ThunarTransferJob *job, * wrt restoring files from the trash. Other transfer_nodes will be called with target_parent_file. */ + /* take a reference on the thumbnail cache */ + application = thunar_application_get (); + thumbnail_cache = thunar_application_get_thumbnail_cache (application); + g_object_unref (application); + for (; err == NULL && node != NULL; node = node->next) { /* guess the target file for this node (unless already provided) */ @@ -536,6 +545,11 @@ retry_copy: /* node->source_file == real_target_file means to skip the file */ if (G_LIKELY (node->source_file != real_target_file)) { + /* notify the thumbnail cache of the copy operation */ + thunar_thumbnail_cache_copy_file (thumbnail_cache, + node->source_file, + real_target_file); + /* check if we have children to copy */ if (node->children != NULL) { @@ -558,22 +572,37 @@ retry_copy: /* add the real target file to the return list */ if (G_LIKELY (target_file_list_return != NULL)) - *target_file_list_return = thunar_g_file_list_prepend (*target_file_list_return, real_target_file); + { + *target_file_list_return = + thunar_g_file_list_prepend (*target_file_list_return, + real_target_file); + } retry_remove: /* try to remove the source directory if we are on copy+remove fallback for move */ - if (job->type == THUNAR_TRANSFER_JOB_MOVE && - !g_file_delete (node->source_file, exo_job_get_cancellable (EXO_JOB (job)), &err)) + if (job->type == THUNAR_TRANSFER_JOB_MOVE) { - /* ask the user to retry */ - response = thunar_job_ask_skip (THUNAR_JOB (job), "%s", err->message); + if (g_file_delete (node->source_file, + exo_job_get_cancellable (EXO_JOB (job)), + &err)) + { + /* notify the thumbnail cache of the delete operation */ + thunar_thumbnail_cache_delete_file (thumbnail_cache, + node->source_file); + } + else + { + /* ask the user to retry */ + response = thunar_job_ask_skip (THUNAR_JOB (job), "%s", + err->message); - /* reset the error */ - g_clear_error (&err); + /* reset the error */ + g_clear_error (&err); - /* check whether to retry */ - if (G_UNLIKELY (response == THUNAR_JOB_RESPONSE_RETRY)) - goto retry_remove; + /* check whether to retry */ + if (G_UNLIKELY (response == THUNAR_JOB_RESPONSE_RETRY)) + goto retry_remove; + } } } @@ -604,6 +633,9 @@ retry_remove: g_object_unref (info); } + /* release the thumbnail cache */ + g_object_unref (thumbnail_cache); + /* propagate error if we failed or the job was cancelled */ if (G_UNLIKELY (err != NULL)) g_propagate_error (error, err); @@ -614,20 +646,22 @@ static gboolean thunar_transfer_job_execute (ExoJob *job, GError **error) { - ThunarTransferNode *node; - ThunarJobResponse response; - ThunarTransferJob *transfer_job = THUNAR_TRANSFER_JOB (job); - GFileInfo *info; - gboolean parent_exists; - GError *err = NULL; - GList *new_files_list = NULL; - GList *snext; - GList *sp; - GList *tnext; - GList *tp; - GFile *target_parent; - gchar *base_name; - gchar *parent_display_name; + ThunarThumbnailCache *thumbnail_cache; + ThunarTransferNode *node; + ThunarApplication *application; + ThunarJobResponse response; + ThunarTransferJob *transfer_job = THUNAR_TRANSFER_JOB (job); + GFileInfo *info; + gboolean parent_exists; + GError *err = NULL; + GList *new_files_list = NULL; + GList *snext; + GList *sp; + GList *tnext; + GList *tp; + GFile *target_parent; + gchar *base_name; + gchar *parent_display_name; _thunar_return_val_if_fail (THUNAR_IS_TRANSFER_JOB (job), FALSE); _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE); @@ -637,6 +671,11 @@ thunar_transfer_job_execute (ExoJob *job, exo_job_info_message (job, _("Collecting files...")); + /* take a reference on the thumbnail cache */ + application = thunar_application_get (); + thumbnail_cache = thunar_application_get_thumbnail_cache (application); + g_object_unref (application); + for (sp = transfer_job->source_node_list, tp = transfer_job->target_file_list; sp != NULL && tp != NULL && err == NULL; sp = snext, tp = tnext) @@ -742,6 +781,11 @@ thunar_transfer_job_execute (ExoJob *job, exo_job_get_cancellable (job), NULL, NULL, &err)) { + /* notify the thumbnail cache of the move operation */ + thunar_thumbnail_cache_move_file (thumbnail_cache, + node->source_file, + tp->data); + /* add the target file to the new files list */ new_files_list = thunar_g_file_list_prepend (new_files_list, tp->data); @@ -779,6 +823,9 @@ thunar_transfer_job_execute (ExoJob *job, g_object_unref (info); } + /* release the thumbnail cache */ + g_object_unref (thumbnail_cache); + /* continue if there were no errors yet */ if (G_LIKELY (err == NULL)) {