diff --git a/ChangeLog b/ChangeLog index 8db7e5f82f166f838094dbddb8f51f97fb3fd3fc..d734fbe0b671366a0e6f206bf9b622e76c403bf0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2006-03-08 Benedikt Meurer <benny@xfce.org> + + * thunar/thunar-view.{c,h}: Add new methods get_visible_range() and + scroll_to_file(). + * thunar/thunar-standard-view.c: Implement get_visible_range() and + scroll_to_file(). Drop the scroll_offsets, will be handled in + ThunarWindow. + * thunar/thunar-window.{c,h}: Remember the first visible file whenever + leaving a directory and scroll to it when entering the directory + again. + * thunar/thunar-application.{c,h}: Return the created window from the + thunar_application_open_window() method. + * thunar/thunar-dbus-service-infos.xml, thunar/thunar-dbus-service.c: + Add DisplayFolderAndSelect() to the org.xfce.FileManager interface. + File managers that cannot scroll and select to a given file can + silently ignore the filename parameter and handle it like an + invocation of DisplayFolder(). Bug #1553. + 2006-03-08 Benedikt Meurer <benny@xfce.org> * thunar/thunar-shortcuts-view.c: Fix shortcut rename. diff --git a/thunar/thunar-application.c b/thunar/thunar-application.c index 8c7a2722a40070d0cb354408ecf5b3d3b6e812e8..82936f5b745193b56e6c52a1f5cddce1eb6a46a1 100644 --- a/thunar/thunar-application.c +++ b/thunar/thunar-application.c @@ -50,41 +50,41 @@ enum -static void thunar_application_class_init (ThunarApplicationClass *klass); -static void thunar_application_init (ThunarApplication *application); -static void thunar_application_finalize (GObject *object); -static void thunar_application_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static void thunar_application_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void thunar_application_collect_and_launch (ThunarApplication *application, - GtkWidget *widget, - const gchar *icon_name, - const gchar *title, - Launcher launcher, - GList *source_path_list, - ThunarVfsPath *target_path, - GClosure *new_files_closure); -static void thunar_application_launch (ThunarApplication *application, - GtkWidget *widget, - const gchar *icon_name, - const gchar *title, - Launcher launcher, - GList *source_path_list, - GList *target_path_list, - GClosure *new_files_closure); -static void thunar_application_open_window_with_role (ThunarApplication *application, - const gchar *role, - ThunarFile *directory, - GdkScreen *screen); -static void thunar_application_window_destroyed (GtkWidget *window, - ThunarApplication *application); -static gboolean thunar_application_show_dialogs (gpointer user_data); -static void thunar_application_show_dialogs_destroy (gpointer user_data); +static void thunar_application_class_init (ThunarApplicationClass *klass); +static void thunar_application_init (ThunarApplication *application); +static void thunar_application_finalize (GObject *object); +static void thunar_application_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void thunar_application_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void thunar_application_collect_and_launch (ThunarApplication *application, + GtkWidget *widget, + const gchar *icon_name, + const gchar *title, + Launcher launcher, + GList *source_path_list, + ThunarVfsPath *target_path, + GClosure *new_files_closure); +static void thunar_application_launch (ThunarApplication *application, + GtkWidget *widget, + const gchar *icon_name, + const gchar *title, + Launcher launcher, + GList *source_path_list, + GList *target_path_list, + GClosure *new_files_closure); +static GtkWidget *thunar_application_open_window_with_role (ThunarApplication *application, + const gchar *role, + ThunarFile *directory, + GdkScreen *screen); +static void thunar_application_window_destroyed (GtkWidget *window, + ThunarApplication *application); +static gboolean thunar_application_show_dialogs (gpointer user_data); +static void thunar_application_show_dialogs_destroy (gpointer user_data); @@ -353,7 +353,7 @@ thunar_application_launch (ThunarApplication *application, -static void +static GtkWidget* thunar_application_open_window_with_role (ThunarApplication *application, const gchar *role, ThunarFile *directory, @@ -378,6 +378,8 @@ thunar_application_open_window_with_role (ThunarApplication *application, /* change the directory */ thunar_window_set_current_directory (THUNAR_WINDOW (window), directory); + + return window; } @@ -569,29 +571,34 @@ thunar_application_take_window (ThunarApplication *application, /** * thunar_application_open_window: - * @application : a #ThunarApplication. - * @directory : the directory to open. - * @screen : the #GdkScreen on which to open the window or %NULL - * to open on the default screen. + * @application : a #ThunarApplication. + * @directory : the directory to open. + * @screen : the #GdkScreen on which to open the window or %NULL + * to open on the default screen. * * Opens a new #ThunarWindow for @application, displaying the * given @directory. + * + * Return value: the newly allocated #ThunarWindow. **/ -void +GtkWidget* thunar_application_open_window (ThunarApplication *application, ThunarFile *directory, GdkScreen *screen) { - gchar *role; + GtkWidget *window; + gchar *role; - g_return_if_fail (THUNAR_IS_APPLICATION (application)); - g_return_if_fail (THUNAR_IS_FILE (directory)); - g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen)); + g_return_val_if_fail (THUNAR_IS_APPLICATION (application), NULL); + g_return_val_if_fail (THUNAR_IS_FILE (directory), NULL); + g_return_val_if_fail (screen == NULL || GDK_IS_SCREEN (screen), NULL); /* generate a unique role for the new window (for session management) */ role = g_strdup_printf ("Thunar-%u-%u", (guint) time (NULL), (guint) g_random_int ()); - thunar_application_open_window_with_role (application, role, directory, screen); + window = thunar_application_open_window_with_role (application, role, directory, screen); g_free (role); + + return window; } diff --git a/thunar/thunar-application.h b/thunar/thunar-application.h index 5e29215cfda75ced771de72a241959481d68814e..df97d29500a9d4f20b75ce27e278a199738e3616 100644 --- a/thunar/thunar-application.h +++ b/thunar/thunar-application.h @@ -50,7 +50,7 @@ gboolean thunar_application_has_windows (ThunarApplication *appl void thunar_application_take_window (ThunarApplication *application, GtkWindow *window); -void thunar_application_open_window (ThunarApplication *application, +GtkWidget *thunar_application_open_window (ThunarApplication *application, ThunarFile *directory, GdkScreen *screen); diff --git a/thunar/thunar-dbus-service-infos.xml b/thunar/thunar-dbus-service-infos.xml index 4959fc1ff69a6ae8431ebeb9a41da33f3cae2e41..5062ef31581504fe818e7e04e45436d6e7cb6daa 100644 --- a/thunar/thunar-dbus-service-infos.xml +++ b/thunar/thunar-dbus-service-infos.xml @@ -43,6 +43,29 @@ </method> + <!-- + DisplayFolderAndSelect (uri : STRING, filename : STRING, display : STRING) : VOID + + uri : either a file:-URI or an absolute path. + filename : the name of the file in the folder which should + be selected by the file manager after loading the + folder. The file manager will also scroll the view + to ensure that the file is visible. The filename + must be a name relative to the folder URI. + display : the screen on which to display the folder or "" + to use the default screen of the file manager. + + Note to implementors: Not all file managers may be able to + implement this method properly for whatever reasons. If you + cannot implement it properly, handle this method like an + invocation of DisplayFolder() and ignore the filename. + --> + <method name="DisplayFolderAndSelect"> + <arg direction="in" name="uri" type="s" /> + <arg direction="in" name="filename" type="s" /> + <arg direction="in" name="display" type="s" /> + </method> + <!-- DisplayFileProperties (uri : STRING, display : STRING) : VOID diff --git a/thunar/thunar-dbus-service.c b/thunar/thunar-dbus-service.c index 46616d596c4622a77a3d1ffbc0134334551e559a..ae267a09298469d84b47f17d70462c27355f7b33 100644 --- a/thunar/thunar-dbus-service.c +++ b/thunar/thunar-dbus-service.c @@ -21,6 +21,13 @@ #include <config.h> #endif +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + #include <dbus/dbus-glib-bindings.h> #include <dbus/dbus-glib-lowlevel.h> #include <dbus/dbus.h> @@ -49,6 +56,11 @@ static gboolean thunar_dbus_service_display_folder (ThunarDBusServi const gchar *uri, const gchar *display, GError **error); +static gboolean thunar_dbus_service_display_folder_and_select (ThunarDBusService *dbus_service, + const gchar *uri, + const gchar *filename, + const gchar *display, + GError **error); static gboolean thunar_dbus_service_display_file_properties (ThunarDBusService *dbus_service, const gchar *uri, const gchar *display, @@ -244,6 +256,64 @@ thunar_dbus_service_display_folder (ThunarDBusService *dbus_service, +static gboolean +thunar_dbus_service_display_folder_and_select (ThunarDBusService *dbus_service, + const gchar *uri, + const gchar *filename, + const gchar *display, + GError **error) +{ + ThunarApplication *application; + ThunarVfsPath *path; + ThunarFile *file; + ThunarFile *folder; + GdkScreen *screen; + GtkWidget *window; + + /* verify that filename is valid */ + if (G_UNLIKELY (filename == NULL || *filename == '\0' || strchr (filename, '/') != NULL)) + { + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, _("Invalid filename \"%s\""), filename); + return FALSE; + } + + /* parse uri and display parameters */ + if (!thunar_dbus_service_parse_uri_and_display (dbus_service, uri, display, &folder, &screen, error)) + return FALSE; + + /* popup a new window for the folder */ + application = thunar_application_get (); + window = thunar_application_open_window (application, folder, screen); + g_object_unref (G_OBJECT (application)); + + /* determine the path for the filename relative to the folder */ + path = thunar_vfs_path_relative (thunar_file_get_path (folder), filename); + if (G_LIKELY (path != NULL)) + { + /* try to determine the file for the path */ + file = thunar_file_get_for_path (path, NULL); + if (G_LIKELY (file != NULL)) + { + /* tell the window to scroll to the given file and select it */ + thunar_window_scroll_to_file (THUNAR_WINDOW (window), file, TRUE, TRUE, 0.5f, 0.5f); + + /* release the file reference */ + g_object_unref (G_OBJECT (file)); + } + + /* release the path */ + thunar_vfs_path_unref (path); + } + + /* cleanup */ + g_object_unref (G_OBJECT (screen)); + g_object_unref (G_OBJECT (folder)); + + return TRUE; +} + + + static gboolean thunar_dbus_service_display_file_properties (ThunarDBusService *dbus_service, const gchar *uri, diff --git a/thunar/thunar-standard-view.c b/thunar/thunar-standard-view.c index 9ba3e83d37e7025541cfa81efe37a74e133df0a4..287453cedd0502fb385e7a5cfde94d48cc107856 100644 --- a/thunar/thunar-standard-view.c +++ b/thunar/thunar-standard-view.c @@ -125,6 +125,15 @@ static void thunar_standard_view_set_zoom_level (Thu ThunarZoomLevel zoom_level); static void thunar_standard_view_reset_zoom_level (ThunarView *view); static void thunar_standard_view_reload (ThunarView *view); +static gboolean thunar_standard_view_get_visible_range (ThunarView *view, + ThunarFile **start_file, + ThunarFile **end_file); +static void thunar_standard_view_scroll_to_file (ThunarView *view, + ThunarFile *file, + gboolean select, + gboolean use_align, + gfloat row_align, + gfloat col_align); static gboolean thunar_standard_view_delete_selected_files (ThunarStandardView *standard_view); static GdkDragAction thunar_standard_view_get_dest_actions (ThunarStandardView *standard_view, GdkDragContext *context, @@ -280,8 +289,12 @@ struct _ThunarStandardViewPrivate */ GClosure *new_files_closure; - /* scroll offsets (-> GtkTreePath indices) per VFS path */ - GHashTable *scroll_offsets; + /* scroll-to-file support */ + ThunarFile *scroll_to_file; + guint scroll_to_select : 1; + guint scroll_to_use_align : 1; + gfloat scroll_to_row_align; + gfloat scroll_to_col_align; }; @@ -508,6 +521,8 @@ thunar_standard_view_view_init (ThunarViewIface *iface) iface->set_zoom_level = thunar_standard_view_set_zoom_level; iface->reset_zoom_level = thunar_standard_view_reset_zoom_level; iface->reload = thunar_standard_view_reload; + iface->get_visible_range = thunar_standard_view_get_visible_range; + iface->scroll_to_file = thunar_standard_view_scroll_to_file; } @@ -519,10 +534,6 @@ thunar_standard_view_init (ThunarStandardView *standard_view) standard_view->priv->drag_scroll_timer_id = -1; standard_view->priv->drag_timer_id = -1; - /* setup the scroll paths offset table */ - standard_view->priv->scroll_offsets = g_hash_table_new_full (thunar_vfs_path_hash, thunar_vfs_path_equal, - (GDestroyNotify) thunar_vfs_path_unref, NULL); - /* grab a reference on the preferences */ standard_view->preferences = thunar_preferences_get (); @@ -700,6 +711,10 @@ thunar_standard_view_finalize (GObject *object) g_assert (standard_view->ui_manager == NULL); g_assert (standard_view->clipboard == NULL); + /* 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)); + /* release our reference on the provider factory */ g_object_unref (G_OBJECT (standard_view->priv->provider_factory)); @@ -736,9 +751,6 @@ thunar_standard_view_finalize (GObject *object) /* release our list of currently selected files */ thunar_file_list_free (standard_view->selected_files); - /* destroy the scroll offset table */ - g_hash_table_destroy (standard_view->priv->scroll_offsets); - /* free the statusbar text (if any) */ g_free (standard_view->statusbar_text); @@ -1091,34 +1103,10 @@ thunar_standard_view_set_current_directory (ThunarNavigator *navigator, { ThunarStandardView *standard_view = THUNAR_STANDARD_VIEW (navigator); ThunarFolder *folder; - GtkTreePath *path; - ThunarFile *file; - gint offset; g_return_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view)); g_return_if_fail (current_directory == NULL || THUNAR_IS_FILE (current_directory)); - /* determine the (previous) current directory */ - file = thunar_navigator_get_current_directory (navigator); - if (G_LIKELY (file != NULL)) - { - /* determine the first visible tree path */ - if ((*THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->get_visible_range) (standard_view, &path, NULL)) - { - /* determine the scroll offset from the tree path */ - offset = gtk_tree_path_get_indices (path)[0] + 1; - gtk_tree_path_free (path); - } - else - { - /* just use the first available path */ - offset = 0; - } - - /* remember the scroll offset for the directory */ - g_hash_table_replace (standard_view->priv->scroll_offsets, thunar_vfs_path_ref (thunar_file_get_path (file)), GINT_TO_POINTER (offset)); - } - /* disconnect any previous "loading" binding */ if (G_LIKELY (standard_view->loading_binding != NULL)) exo_binding_unbind (standard_view->loading_binding); @@ -1130,6 +1118,10 @@ thunar_standard_view_set_current_directory (ThunarNavigator *navigator, return; } + /* scroll to top-left when changing folder */ + gtk_adjustment_set_value (gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (standard_view)), 0.0); + gtk_adjustment_set_value (gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (standard_view)), 0.0); + /* We drop the model from the view as a simple optimization to speed up * the process of disconnecting the model data from the view. */ @@ -1169,9 +1161,7 @@ static void thunar_standard_view_set_loading (ThunarStandardView *standard_view, gboolean loading) { - GtkTreePath *path; - ThunarFile *file; - gint offset; + ThunarFile *file; loading = !!loading; @@ -1182,28 +1172,29 @@ thunar_standard_view_set_loading (ThunarStandardView *standard_view, /* apply the new state */ standard_view->loading = loading; - /* check if this state change was due to a "loading complete" event */ - if (G_LIKELY (!loading)) + /* check if we're done loading and have a scheduled scroll_to_file */ + if (G_UNLIKELY (!loading && standard_view->priv->scroll_to_file != NULL)) { - /* determine the current directory */ - file = thunar_navigator_get_current_directory (THUNAR_NAVIGATOR (standard_view)); - if (G_LIKELY (file != NULL)) - { - /* determine the scroll offset for this folder */ - offset = GPOINTER_TO_INT (g_hash_table_lookup (standard_view->priv->scroll_offsets, thunar_file_get_path (file))); - if (G_LIKELY (offset > 0)) - { - /* scroll to the path for the offset */ - path = gtk_tree_path_new_from_indices (offset - 1, -1); - (*THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->scroll_to_path) (standard_view, path, TRUE, 0.0f, 0.0f); - gtk_tree_path_free (path); - } - } + /* remember and reset the scroll_to_file reference */ + file = standard_view->priv->scroll_to_file; + standard_view->priv->scroll_to_file = NULL; + + /* and try again */ + thunar_view_scroll_to_file (THUNAR_VIEW (standard_view), file, + standard_view->priv->scroll_to_select, + standard_view->priv->scroll_to_use_align, + standard_view->priv->scroll_to_row_align, + standard_view->priv->scroll_to_col_align); + + /* cleanup */ + g_object_unref (G_OBJECT (file)); } /* notify listeners */ + g_object_freeze_notify (G_OBJECT (standard_view)); g_object_notify (G_OBJECT (standard_view), "loading"); g_object_notify (G_OBJECT (standard_view), "statusbar-text"); + g_object_thaw_notify (G_OBJECT (standard_view)); } @@ -1312,6 +1303,105 @@ thunar_standard_view_reload (ThunarView *view) +static gboolean +thunar_standard_view_get_visible_range (ThunarView *view, + ThunarFile **start_file, + ThunarFile **end_file) +{ + ThunarStandardView *standard_view = THUNAR_STANDARD_VIEW (view); + GtkTreePath *start_path; + GtkTreePath *end_path; + GtkTreeIter iter; + + /* determine the visible range as tree paths */ + if ((*THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->get_visible_range) (standard_view, &start_path, &end_path)) + { + /* determine the file for the start path */ + if (G_LIKELY (start_file != NULL)) + { + gtk_tree_model_get_iter (GTK_TREE_MODEL (standard_view->model), &iter, start_path); + *start_file = thunar_list_model_get_file (standard_view->model, &iter); + } + + /* determine the file for the end path */ + if (G_LIKELY (end_file != NULL)) + { + gtk_tree_model_get_iter (GTK_TREE_MODEL (standard_view->model), &iter, end_path); + *end_file = thunar_list_model_get_file (standard_view->model, &iter); + } + + /* release the tree paths */ + gtk_tree_path_free (start_path); + gtk_tree_path_free (end_path); + + return TRUE; + } + + return FALSE; +} + + + +static void +thunar_standard_view_scroll_to_file (ThunarView *view, + ThunarFile *file, + gboolean select, + gboolean use_align, + gfloat row_align, + gfloat col_align) +{ + ThunarStandardView *standard_view = THUNAR_STANDARD_VIEW (view); + GList files; + GList *paths; + + /* release the previous 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)); + standard_view->priv->scroll_to_file = NULL; + } + + /* check if we're still loading */ + if (thunar_view_get_loading (view)) + { + /* remember a reference for the new file and settings */ + standard_view->priv->scroll_to_file = g_object_ref (G_OBJECT (file)); + standard_view->priv->scroll_to_select = select; + standard_view->priv->scroll_to_use_align = use_align; + standard_view->priv->scroll_to_row_align = row_align; + standard_view->priv->scroll_to_col_align = col_align; + } + else + { + /* fake a file list */ + files.data = file; + files.next = NULL; + files.prev = NULL; + + /* determine the path for the file */ + paths = thunar_list_model_get_paths_for_files (standard_view->model, &files); + if (G_LIKELY (paths != NULL)) + { + /* scroll to the path */ + (*THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->scroll_to_path) (standard_view, paths->data, use_align, row_align, col_align); + + /* check if we should also alter the selection */ + if (G_UNLIKELY (select)) + { + /* select only the file in question */ + (*THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->unselect_all) (standard_view); + (*THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->select_path) (standard_view, paths->data); + } + + /* cleanup */ + g_list_foreach (paths, (GFunc) gtk_tree_path_free, NULL); + g_list_free (paths); + } + } +} + + + static gboolean thunar_standard_view_delete_selected_files (ThunarStandardView *standard_view) { diff --git a/thunar/thunar-view.c b/thunar/thunar-view.c index b5e79a98c428a702c732ae6abd50e67fd75f99b1..df7bfb9c48dd880b0ed8810129bef7c2c960cd56 100644 --- a/thunar/thunar-view.c +++ b/thunar/thunar-view.c @@ -268,3 +268,58 @@ thunar_view_reload (ThunarView *view) +/** + * thunar_view_get_visible_range: + * @view : a #ThunarView instance. + * @start_file : return location for start of region, or %NULL. + * @end_file : return location for end of region, or %NULL. + * + * Sets @start_file and @end_file to be the first and last visible + * #ThunarFile. + * + * The files should be freed with g_object_unref() when no + * longer needed. + * + * Return value: %TRUE if valid files were placed in @start_file + * and @end_file. + **/ +gboolean +thunar_view_get_visible_range (ThunarView *view, + ThunarFile **start_file, + ThunarFile **end_file) +{ + g_return_val_if_fail (THUNAR_IS_VIEW (view), FALSE); + return (*THUNAR_VIEW_GET_IFACE (view)->get_visible_range) (view, start_file, end_file); +} + + + +/** + * thunar_view_scroll_to_file: + * @view : a #ThunarView instance. + * @file : the #ThunarFile to scroll to. + * @select : %TRUE to also select the @file in the @view. + * @use_align : whether to use alignment arguments. + * @row_align : the vertical alignment. + * @col_align : the horizontal alignment. + * + * Tells @view to scroll to the @file. If @view is currently + * loading, it'll remember to scroll to @file later when + * the contents are loaded. + **/ +void +thunar_view_scroll_to_file (ThunarView *view, + ThunarFile *file, + gboolean select, + gboolean use_align, + gfloat row_align, + gfloat col_align) +{ + g_return_if_fail (THUNAR_IS_VIEW (view)); + g_return_if_fail (THUNAR_IS_FILE (file)); + g_return_if_fail (row_align >= 0.0f && row_align <= 1.0f); + g_return_if_fail (col_align >= 0.0f && col_align <= 1.0f); + (*THUNAR_VIEW_GET_IFACE (view)->scroll_to_file) (view, file, select, use_align, row_align, col_align); +} + + diff --git a/thunar/thunar-view.h b/thunar/thunar-view.h index 8783752b1c1a3ddba425a78cf54d7a149f1c4701..63b7232f3f6617b96dab2a2dc81435a05e77e04c 100644 --- a/thunar/thunar-view.h +++ b/thunar/thunar-view.h @@ -52,6 +52,17 @@ struct _ThunarViewIface void (*reset_zoom_level) (ThunarView *view); void (*reload) (ThunarView *view); + + gboolean (*get_visible_range) (ThunarView *view, + ThunarFile **start_file, + ThunarFile **end_file); + + void (*scroll_to_file) (ThunarView *view, + ThunarFile *file, + gboolean select, + gboolean use_align, + gfloat row_align, + gfloat col_align); }; GType thunar_view_get_type (void) G_GNUC_CONST; @@ -70,6 +81,17 @@ void thunar_view_reset_zoom_level (ThunarView *view); void thunar_view_reload (ThunarView *view); +gboolean thunar_view_get_visible_range (ThunarView *view, + ThunarFile **start_file, + ThunarFile **end_file); + +void thunar_view_scroll_to_file (ThunarView *view, + ThunarFile *file, + gboolean select, + gboolean use_align, + gfloat row_align, + gfloat col_align); + G_END_DECLS; #endif /* !__THUNAR_VIEW_H__ */ diff --git a/thunar/thunar-window.c b/thunar/thunar-window.c index 9ee04bde4c85f821e5645a63296d87a271652022..590e9a22ff41851fbe7c95cdbdd6619d0ca69d19 100644 --- a/thunar/thunar-window.c +++ b/thunar/thunar-window.c @@ -212,6 +212,9 @@ struct _ThunarWindow /* support to remember window geometry */ gint save_geometry_timer_id; + + /* scroll_to_file support */ + GHashTable *scroll_to_files; }; @@ -447,6 +450,9 @@ thunar_window_init (ThunarWindow *window) /* grab a reference on the preferences */ window->preferences = thunar_preferences_get (); + /* allocate the scroll_to_files mapping */ + window->scroll_to_files = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, g_object_unref); + /* allocate a closure for the menu_item_selected() callback */ window->menu_item_selected_closure = g_cclosure_new_object (G_CALLBACK (thunar_window_menu_item_selected), G_OBJECT (window)); g_closure_ref (window->menu_item_selected_closure); @@ -683,6 +689,9 @@ thunar_window_finalize (GObject *object) /* release the preferences reference */ g_object_unref (G_OBJECT (window->preferences)); + /* release the scroll_to_files hash table */ + g_hash_table_destroy (window->scroll_to_files); + (*G_OBJECT_CLASS (thunar_window_parent_class)->finalize) (object); } @@ -979,11 +988,24 @@ thunar_window_action_open_new_window (GtkAction *action, ThunarWindow *window) { ThunarApplication *application; + ThunarFile *start_file; + GtkWidget *new_window; + /* popup a new window */ application = thunar_application_get (); - thunar_application_open_window (application, window->current_directory, - gtk_widget_get_screen (GTK_WIDGET (window))); + new_window = thunar_application_open_window (application, window->current_directory, + gtk_widget_get_screen (GTK_WIDGET (window))); g_object_unref (G_OBJECT (application)); + + /* determine the first visible file in the current window */ + if (window->view != NULL && thunar_view_get_visible_range (THUNAR_VIEW (window->view), &start_file, NULL)) + { + /* scroll the new window to the same file */ + thunar_window_scroll_to_file (THUNAR_WINDOW (new_window), start_file, FALSE, TRUE, 0.0f, 0.0f); + + /* release the file reference */ + g_object_unref (G_OBJECT (start_file)); + } } @@ -1279,11 +1301,16 @@ thunar_window_action_view_changed (GtkRadioAction *action, GtkRadioAction *current, ThunarWindow *window) { - GType type; + ThunarFile *file = NULL; + GType type; /* drop the previous view (if any) */ if (G_LIKELY (window->view != NULL)) { + /* get first visible file in the previous view */ + if (!thunar_view_get_visible_range (THUNAR_VIEW (window->view), &file, NULL)) + file = NULL; + /* destroy and disconnect the previous view */ gtk_widget_destroy (window->view); @@ -1322,6 +1349,10 @@ thunar_window_action_view_changed (GtkRadioAction *action, /* connect to the statusbar (if any) */ if (G_LIKELY (window->statusbar != NULL)) exo_binding_new (G_OBJECT (window->view), "statusbar-text", G_OBJECT (window->statusbar), "text"); + + /* scroll to the previously visible file in the old view */ + if (G_UNLIKELY (file != NULL)) + thunar_view_scroll_to_file (THUNAR_VIEW (window->view), file, FALSE, TRUE, 0.0f, 0.0f); } else { @@ -1331,6 +1362,10 @@ thunar_window_action_view_changed (GtkRadioAction *action, /* remember the setting */ g_object_set (G_OBJECT (window->preferences), "last-view", g_type_name (type), NULL); + + /* release the file reference */ + if (G_UNLIKELY (file != NULL)) + g_object_unref (G_OBJECT (file)); } @@ -1837,6 +1872,8 @@ void thunar_window_set_current_directory (ThunarWindow *window, ThunarFile *current_directory) { + ThunarFile *file; + g_return_if_fail (THUNAR_IS_WINDOW (window)); g_return_if_fail (current_directory == NULL || THUNAR_IS_FILE (current_directory)); @@ -1847,6 +1884,13 @@ thunar_window_set_current_directory (ThunarWindow *window, /* disconnect from the previously active directory */ if (G_LIKELY (window->current_directory != NULL)) { + /* determine the fist visible file in the previous directory */ + if (window->view != NULL && thunar_view_get_visible_range (THUNAR_VIEW (window->view), &file, NULL)) + { + /* add the file to our internal mapping of directories to scroll files */ + g_hash_table_replace (window->scroll_to_files, g_object_ref (G_OBJECT (window->current_directory)), file); + } + g_signal_handlers_disconnect_by_func (G_OBJECT (window->current_directory), thunar_window_current_directory_changed, window); g_object_unref (G_OBJECT (window->current_directory)); } @@ -1876,6 +1920,15 @@ thunar_window_set_current_directory (ThunarWindow *window, * state already while the folder view is loading. */ g_object_notify (G_OBJECT (window), "current-directory"); + + /* check if we have a valid current directory */ + if (G_LIKELY (window->current_directory != NULL)) + { + /* check if we have a scroll_to_file for the new directory and scroll to the file */ + file = g_hash_table_lookup (window->scroll_to_files, window->current_directory); + if (G_LIKELY (file != NULL)) + thunar_window_scroll_to_file (window, file, FALSE, TRUE, 0.0f, 0.0f); + } } @@ -1927,3 +1980,33 @@ thunar_window_set_zoom_level (ThunarWindow *window, } + +/** + * thunar_window_scroll_to_file: + * @window : a #ThunarWindow instance. + * @file : a #ThunarFile. + * @select : if %TRUE the @file will also be selected. + * @use_align : %TRUE to use the alignment arguments. + * @row_align : the vertical alignment. + * @col_align : the horizontal alignment. + * + * Tells the @window to scroll to the @file + * in the current view. + **/ +void +thunar_window_scroll_to_file (ThunarWindow *window, + ThunarFile *file, + gboolean select, + gboolean use_align, + gfloat row_align, + gfloat col_align) +{ + g_return_if_fail (THUNAR_IS_WINDOW (window)); + g_return_if_fail (THUNAR_IS_FILE (file)); + + /* verify that we have a valid view */ + if (G_LIKELY (window->view != NULL)) + thunar_view_scroll_to_file (THUNAR_VIEW (window->view), file, select, use_align, row_align, col_align); +} + + diff --git a/thunar/thunar-window.h b/thunar/thunar-window.h index 9355942b0929826d155286d095462864dd6505e6..04c61a05927b59c5f5d670fb78eb8dac1be54b25 100644 --- a/thunar/thunar-window.h +++ b/thunar/thunar-window.h @@ -47,6 +47,13 @@ ThunarZoomLevel thunar_window_get_zoom_level (ThunarWindow *window); void thunar_window_set_zoom_level (ThunarWindow *window, ThunarZoomLevel zoom_level); +void thunar_window_scroll_to_file (ThunarWindow *window, + ThunarFile *file, + gboolean select, + gboolean use_align, + gfloat row_align, + gfloat col_align); + G_END_DECLS; #endif /* !__THUNAR_WINDOW_H__ */