From 61e5ac642b9155aa79d4cc1b63e446efcbb6621e Mon Sep 17 00:00:00 2001 From: Reuben Green Date: Sat, 16 May 2020 20:43:07 +0100 Subject: [PATCH 1/5] added preferences to represent per-directory in thunar-preferences, added a checkbox in the preferences dialog added setter and getter functions for attributes to thunar-file --- thunar/thunar-file.c | 53 ++++++++++++++++++++++++++++++ thunar/thunar-file.h | 6 ++++ thunar/thunar-preferences-dialog.c | 25 ++++++++++++++ thunar/thunar-preferences.c | 13 ++++++++ 4 files changed, 97 insertions(+) diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c index c8ae011f..71c2a9b3 100644 --- a/thunar/thunar-file.c +++ b/thunar/thunar-file.c @@ -4507,3 +4507,56 @@ thunar_file_list_to_thunar_g_file_list (GList *file_list) return list; } + +/* + * The implementation of the following two functions is a hack to get this + * patch to work to demonstrate the concept. I have not used GFile attributes + * before, and I could not find any good example code to copy, so these functions + * are probably badly implemented. Also, in the final fix, these functions should + * be better integrated into the existing file attribute system in thunar and + * thunarx - any help or advice on how to do this would be most welcome! + */ + +gchar* +thunar_file_get_view_setting (ThunarFile *file, + const gchar *setting_name) +{ + GFileInfo *info; + gchar *attr_name; + + _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); + + /* add attributes here */ + if (g_strcmp0 (setting_name, "XXX") == 0) + attr_name = "metadata::thunar::XXX"; + else + return NULL; + + info = g_file_query_info (file->gfile, attr_name, G_FILE_QUERY_INFO_NONE, NULL, NULL); + + if (g_file_info_has_attribute (info, attr_name)) + return g_strdup (g_file_info_get_attribute_string (info, attr_name)); + else + return NULL; +} + + + +void +thunar_file_set_view_setting (ThunarFile *file, + const gchar *setting_name, + const gchar *setting_value) +{ + gchar *attr_name; + + _thunar_return_if_fail (THUNAR_IS_FILE (file)); + + /* add attributes here */ + if (g_strcmp0 (setting_name, "XXX") == 0) + attr_name = "metadata::thunar::XXX"; + else + return; + + g_file_info_set_attribute_string (file->info, attr_name, setting_value); + g_file_set_attributes_from_info (file->gfile, file->info, G_FILE_QUERY_INFO_NONE, NULL, NULL); +} diff --git a/thunar/thunar-file.h b/thunar/thunar-file.h index c9488d22..337719db 100644 --- a/thunar/thunar-file.h +++ b/thunar/thunar-file.h @@ -267,6 +267,12 @@ GList *thunar_file_list_to_thunar_g_file_list (GList gboolean thunar_file_is_desktop (const ThunarFile *file); +gchar* thunar_file_get_view_setting (ThunarFile *file, + const gchar *setting_name); +void thunar_file_set_view_setting (ThunarFile *file, + const gchar *setting_name, + const gchar *setting_value); + /** * thunar_file_is_root: * @file : a #ThunarFile. diff --git a/thunar/thunar-preferences-dialog.c b/thunar/thunar-preferences-dialog.c index 1d2e48f5..f28d04ac 100644 --- a/thunar/thunar-preferences-dialog.c +++ b/thunar/thunar-preferences-dialog.c @@ -294,6 +294,31 @@ thunar_preferences_dialog_init (ThunarPreferencesDialog *dialog) gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, TRUE, 0); gtk_widget_show (frame); + label = gtk_label_new (_("Per directory settings")); + gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ()); + gtk_frame_set_label_widget (GTK_FRAME (frame), label); + gtk_widget_show (label); + + grid = gtk_grid_new (); + gtk_grid_set_column_spacing (GTK_GRID (grid), 12); + gtk_grid_set_row_spacing (GTK_GRID (grid), 6); + gtk_widget_set_margin_top (GTK_WIDGET (grid), 6); + gtk_widget_set_margin_start (GTK_WIDGET (grid), 12); + gtk_container_add (GTK_CONTAINER (frame), grid); + gtk_widget_show (grid); + + button = gtk_check_button_new_with_mnemonic (_("Use _per directory settings")); + exo_mutual_binding_new (G_OBJECT (dialog->preferences), "misc-per-directory", G_OBJECT (button), "active"); + gtk_widget_set_tooltip_text (button, + _("Select this option to save settings for each directory")); + gtk_widget_set_hexpand (button, TRUE); + gtk_grid_attach (GTK_GRID (grid), button, 0, 0, 1, 1); + gtk_widget_show (button); + + frame = g_object_new (GTK_TYPE_FRAME, "border-width", 0, "shadow-type", GTK_SHADOW_NONE, NULL); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, TRUE, 0); + gtk_widget_show (frame); + label = gtk_label_new (_("Default View")); gtk_label_set_attributes (GTK_LABEL (label), thunar_pango_attr_list_bold ()); gtk_frame_set_label_widget (GTK_FRAME (frame), label); diff --git a/thunar/thunar-preferences.c b/thunar/thunar-preferences.c index 732d9a2d..7089975b 100644 --- a/thunar/thunar-preferences.c +++ b/thunar/thunar-preferences.c @@ -72,6 +72,7 @@ enum PROP_LAST_WINDOW_HEIGHT, PROP_LAST_WINDOW_WIDTH, PROP_LAST_WINDOW_FULLSCREEN, + PROP_MISC_PER_DIRECTORY, PROP_MISC_ALWAYS_SHOW_TABS, PROP_MISC_VOLUME_MANAGEMENT, PROP_MISC_CASE_SENSITIVE, @@ -346,6 +347,18 @@ thunar_preferences_class_init (ThunarPreferencesClass *klass) FALSE, EXO_PARAM_READWRITE); + /** + * ThunarPreferences:misc-per-directory: + * + * Whether to use per directory settings. + **/ + preferences_props[PROP_MISC_PER_DIRECTORY] = + g_param_spec_boolean ("misc-per-directory", + "MiscPerDirectory", + NULL, + FALSE, + EXO_PARAM_READWRITE); + /** * ThunarPreferences:last-side-pane: * -- 2.26.2 From 686fc3ee8abce0a2ef8f7a4ceabf214983feaeda Mon Sep 17 00:00:00 2001 From: Reuben Green Date: Sat, 16 May 2020 20:27:32 +0100 Subject: [PATCH 2/5] added support for per-directory settings in thunar-window, got it all plumbed in, added a handler for changes to the preference to thunar-window --- thunar/thunar-window.c | 47 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/thunar/thunar-window.c b/thunar/thunar-window.c index 7c4d0d76..567fc004 100644 --- a/thunar/thunar-window.c +++ b/thunar/thunar-window.c @@ -69,6 +69,7 @@ enum PROP_0, PROP_CURRENT_DIRECTORY, PROP_ZOOM_LEVEL, + PROP_PER_DIRECTORY, }; /* Signal identifiers */ @@ -211,6 +212,8 @@ static gboolean thunar_window_save_geometry_timer (gpointer static void thunar_window_save_geometry_timer_destroy(gpointer user_data); static void thunar_window_set_zoom_level (ThunarWindow *window, ThunarZoomLevel zoom_level); +static void thunar_window_set_per_directory (ThunarWindow *window, + gboolean per_directory); static void thunar_window_update_window_icon (ThunarWindow *window); static gboolean thunar_window_create_file_menu (ThunarWindow *window, GdkEventCrossing *event, @@ -325,6 +328,8 @@ struct _ThunarWindow gboolean show_hidden; + gboolean per_directory; + /* support to remember window geometry */ guint save_geometry_timer_id; @@ -465,6 +470,19 @@ thunar_window_class_init (ThunarWindowClass *klass) THUNAR_ZOOM_LEVEL_100_PERCENT, EXO_PARAM_READWRITE)); + /** + * ThunarWindow:per-directory: + * + * Whether to use per directory settings. + **/ + g_object_class_install_property (gobject_class, + PROP_PER_DIRECTORY, + g_param_spec_boolean ("per-directory", + "per-directory", + "per-directory", + FALSE, + EXO_PARAM_READWRITE)); + /** * ThunarWindow::reload: * @window : a #ThunarWindow instance. @@ -875,6 +893,9 @@ G_GNUC_END_IGNORE_DEPRECATIONS thunar_window_install_sidepane (window, type); g_free (last_side_pane); + /* synchronise the "per-directory" property with the global "misc-per-directory" property */ + exo_binding_new (G_OBJECT (window->preferences), "misc-per-directory", G_OBJECT (window), "per-directory"); + /* setup a new statusbar */ window->statusbar = thunar_statusbar_new (); gtk_widget_set_hexpand (window->statusbar, TRUE); @@ -1381,6 +1402,10 @@ thunar_window_set_property (GObject *object, thunar_window_set_zoom_level (window, g_value_get_enum (value)); break; + case PROP_PER_DIRECTORY: + thunar_window_set_per_directory (window, g_value_get_boolean (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -3494,6 +3519,28 @@ thunar_window_set_zoom_level (ThunarWindow *window, +/** + * thunar_window_set_per_directory: + * @window : a #ThunarWindow instance. + * @per_directory : whether to use per-directory settings in @window. + * + * Toggles the use of per-directory settings in @window according to @per_directory. + **/ +void +thunar_window_set_per_directory (ThunarWindow *window, + gboolean per_directory) +{ + GType type = G_TYPE_NONE; + gchar *type_name; + + _thunar_return_if_fail (THUNAR_IS_WINDOW (window)); + + /* save the setting */ + window->per_directory = per_directory; +} + + + /** * thunar_window_get_current_directory: * @window : a #ThunarWindow instance. -- 2.26.2 From 0cfeffa37bd39f76fdba3de6fccb1016faaf8ae2 Mon Sep 17 00:00:00 2001 From: Reuben Green Date: Wed, 13 May 2020 20:59:50 +0100 Subject: [PATCH 3/5] implemented per-directory view types --- thunar/thunar-application.c | 2 +- thunar/thunar-file.c | 10 +- thunar/thunar-window.c | 360 ++++++++++++++++++++++++++---------- thunar/thunar-window.h | 2 +- 4 files changed, 271 insertions(+), 103 deletions(-) diff --git a/thunar/thunar-application.c b/thunar/thunar-application.c index 7c6600bf..40fd8bee 100644 --- a/thunar/thunar-application.c +++ b/thunar/thunar-application.c @@ -1318,7 +1318,7 @@ thunar_application_open_window (ThunarApplication *application, list = g_list_last (list); if (directory != NULL) - thunar_window_notebook_insert (THUNAR_WINDOW (list->data), directory); + thunar_window_notebook_open_new_tab (THUNAR_WINDOW (list->data), directory); /* bring the window to front */ gtk_window_present (list->data); diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c index 71c2a9b3..b7d9019e 100644 --- a/thunar/thunar-file.c +++ b/thunar/thunar-file.c @@ -4526,9 +4526,8 @@ thunar_file_get_view_setting (ThunarFile *file, _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); - /* add attributes here */ - if (g_strcmp0 (setting_name, "XXX") == 0) - attr_name = "metadata::thunar::XXX"; + if (g_strcmp0 (setting_name, "view-type") == 0) + attr_name = "metadata::thunar::view-type"; else return NULL; @@ -4551,9 +4550,8 @@ thunar_file_set_view_setting (ThunarFile *file, _thunar_return_if_fail (THUNAR_IS_FILE (file)); - /* add attributes here */ - if (g_strcmp0 (setting_name, "XXX") == 0) - attr_name = "metadata::thunar::XXX"; + if (g_strcmp0 (setting_name, "view-type") == 0) + attr_name = "metadata::thunar::view-type"; else return; diff --git a/thunar/thunar-window.c b/thunar/thunar-window.c index 567fc004..597df198 100644 --- a/thunar/thunar-window.c +++ b/thunar/thunar-window.c @@ -135,6 +135,11 @@ static gpointer thunar_window_notebook_create_window (GtkWidget gint x, gint y, ThunarWindow *window); +static GtkWidget*thunar_window_notebook_insert (ThunarWindow *window, + ThunarFile *directory, + GType view_type, + gint position, + ThunarHistory *history); static void thunar_window_bookmark_add_menu_item (GFile *g_file, const gchar *name, gint line_num, @@ -255,6 +260,8 @@ static gboolean thunar_window_check_bookmark_key_activation (ThunarWindow gpointer user_data); static void thunar_window_set_current_directory_gfile (ThunarWindow *window, GFile *current_directory); +static GType thunar_window_view_type_for_directory (ThunarWindow *window, + ThunarFile *directory); @@ -727,7 +734,7 @@ thunar_window_init (ThunarWindow *window) exo_binding_new (G_OBJECT (window), "current-directory", G_OBJECT (window->launcher), "current-directory"); g_signal_connect_swapped (G_OBJECT (window->launcher), "change-directory", G_CALLBACK (thunar_window_set_current_directory), window); - g_signal_connect_swapped (G_OBJECT (window->launcher), "open-new-tab", G_CALLBACK (thunar_window_notebook_insert), window); + g_signal_connect_swapped (G_OBJECT (window->launcher), "open-new-tab", G_CALLBACK (thunar_window_notebook_open_new_tab), window); thunar_launcher_append_accelerators (window->launcher, window->accel_group); /* determine the default window size from the preferences */ @@ -837,7 +844,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS window->location_bar = thunar_location_bar_new (); g_object_bind_property (G_OBJECT (window), "current-directory", G_OBJECT (window->location_bar), "current-directory", G_BINDING_SYNC_CREATE); g_signal_connect_swapped (G_OBJECT (window->location_bar), "change-directory", G_CALLBACK (thunar_window_set_current_directory), window); - g_signal_connect_swapped (G_OBJECT (window->location_bar), "open-new-tab", G_CALLBACK (thunar_window_notebook_insert), window); + g_signal_connect_swapped (G_OBJECT (window->location_bar), "open-new-tab", G_CALLBACK (thunar_window_notebook_open_new_tab), window); g_signal_connect_swapped (G_OBJECT (window->location_bar), "reload-requested", G_CALLBACK (thunar_window_handle_reload_request), window); g_signal_connect_swapped (G_OBJECT (window->location_bar), "entry-done", G_CALLBACK (thunar_window_update_location_bar_visible), window); @@ -1763,7 +1770,8 @@ thunar_window_notebook_switch_page (GtkWidget *notebook, window->view = page; window->view_type = G_TYPE_FROM_INSTANCE (page); - if (window->view_type != G_TYPE_NONE) + /* remember the last view type if per-directory settings are not enabled */ + if (!window->per_directory && window->view_type != G_TYPE_NONE) g_object_set (G_OBJECT (window->preferences), "last-view", g_type_name (window->view_type), NULL); /* connect to the new history */ @@ -1840,7 +1848,7 @@ thunar_window_notebook_page_added (GtkWidget *notebook, g_signal_connect (G_OBJECT (page), "notify::loading", G_CALLBACK (thunar_window_notify_loading), window); g_signal_connect_swapped (G_OBJECT (page), "start-open-location", G_CALLBACK (thunar_window_start_open_location), window); g_signal_connect_swapped (G_OBJECT (page), "change-directory", G_CALLBACK (thunar_window_set_current_directory), window); - g_signal_connect_swapped (G_OBJECT (page), "open-new-tab", G_CALLBACK (thunar_window_notebook_insert), window); + g_signal_connect_swapped (G_OBJECT (page), "open-new-tab", G_CALLBACK (thunar_window_notebook_open_new_tab), window); /* update tab visibility */ thunar_window_notebook_show_tabs (window); @@ -2020,38 +2028,32 @@ thunar_window_notebook_create_window (GtkWidget *notebook, -void -thunar_window_notebook_insert (ThunarWindow *window, - ThunarFile *directory) +static GtkWidget* +thunar_window_notebook_insert (ThunarWindow *window, + ThunarFile *directory, + GType view_type, + gint position, + ThunarHistory *history) { - ThunarHistory *history = NULL; GtkWidget *view; - gint page_num; GtkWidget *label; GtkWidget *label_box; GtkWidget *button; GtkWidget *icon; - _thunar_return_if_fail (THUNAR_IS_WINDOW (window)); - _thunar_return_if_fail (THUNAR_IS_FILE (directory)); - _thunar_return_if_fail (window->view_type != G_TYPE_NONE); - - /* leave if no directory is set */ - if (directory == NULL) - return; + _thunar_return_val_if_fail (THUNAR_IS_WINDOW (window), NULL); + _thunar_return_val_if_fail (THUNAR_IS_FILE (directory), NULL); + _thunar_return_val_if_fail (view_type != G_TYPE_NONE, NULL); + _thunar_return_val_if_fail (history == NULL || THUNAR_IS_HISTORY (history), NULL); /* allocate and setup a new view */ - view = g_object_new (window->view_type, "current-directory", directory, "accel-group", window->accel_group, NULL); + view = g_object_new (view_type, "current-directory", directory, "accel-group", window->accel_group, NULL); thunar_view_set_show_hidden (THUNAR_VIEW (view), window->show_hidden); gtk_widget_show (view); - /* save the history of the origin view */ - if (THUNAR_IS_STANDARD_VIEW (window->view)) - { - history = thunar_standard_view_copy_history (THUNAR_STANDARD_VIEW (window->view)); - if (history != NULL) - thunar_standard_view_set_history (THUNAR_STANDARD_VIEW (view), history); - } + /* set the history of the view if a history is provided */ + if (history != NULL) + thunar_standard_view_set_history (THUNAR_STANDARD_VIEW (view), history); label_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); @@ -2083,18 +2085,43 @@ thunar_window_notebook_insert (ThunarWindow *window, gtk_widget_show (icon); /* insert the new page */ - page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (window->notebook)); - page_num = gtk_notebook_insert_page (GTK_NOTEBOOK (window->notebook), view, label_box, page_num + 1); - - /* switch to the new tab*/ - gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), page_num); + gtk_notebook_insert_page (GTK_NOTEBOOK (window->notebook), view, label_box, position); /* set tab child properties */ gtk_container_child_set (GTK_CONTAINER (window->notebook), view, "tab-expand", TRUE, NULL); gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (window->notebook), view, TRUE); gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (window->notebook), view, TRUE); - /* take focus on the view */ + return view; +} + + + +void +thunar_window_notebook_open_new_tab (ThunarWindow *window, + ThunarFile *directory) +{ + ThunarHistory *history = NULL; + GtkWidget *view; + gint page_num; + GType view_type; + + /* save the history of the current view */ + if (THUNAR_IS_STANDARD_VIEW (window->view)) + history = thunar_standard_view_copy_history (THUNAR_STANDARD_VIEW (window->view)); + + /* find the correct view type */ + view_type = thunar_window_view_type_for_directory (window, directory); + + /* insert the new view */ + page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (window->notebook)); + view = thunar_window_notebook_insert (window, directory, view_type, page_num + 1, history); + + /* switch to the new view */ + page_num = gtk_notebook_page_num (GTK_NOTEBOOK (window->notebook), view); + gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), page_num); + + /* take focus on the new view */ gtk_widget_grab_focus (view); } @@ -2220,7 +2247,7 @@ thunar_window_install_sidepane (ThunarWindow *window, gtk_widget_set_size_request (window->sidepane, 0, -1); exo_binding_new (G_OBJECT (window), "current-directory", G_OBJECT (window->sidepane), "current-directory"); g_signal_connect_swapped (G_OBJECT (window->sidepane), "change-directory", G_CALLBACK (thunar_window_set_current_directory), window); - g_signal_connect_swapped (G_OBJECT (window->sidepane), "open-new-tab", G_CALLBACK (thunar_window_notebook_insert), window); + g_signal_connect_swapped (G_OBJECT (window->sidepane), "open-new-tab", G_CALLBACK (thunar_window_notebook_open_new_tab), window); context = gtk_widget_get_style_context (window->sidepane); gtk_style_context_add_class (context, "sidebar"); gtk_paned_pack1 (GTK_PANED (window->paned), window->sidepane, FALSE, FALSE); @@ -2391,8 +2418,8 @@ static void thunar_window_action_open_new_tab (ThunarWindow *window, GtkWidget *menu_item) { - /* insert new tab with current directory as default */ - thunar_window_notebook_insert (window, thunar_window_get_current_directory (window)); + /* open new tab with current directory as default */ + thunar_window_notebook_open_new_tab (window, thunar_window_get_current_directory (window)); } @@ -2701,72 +2728,97 @@ thunar_window_action_compact_view (ThunarWindow *window) static void -thunar_window_action_view_changed (ThunarWindow *window, - GType view_type) +thunar_window_replace_view (ThunarWindow *window, + GtkWidget *view, + GType view_type) { ThunarFile *file = NULL; ThunarFile *current_directory = NULL; - GtkWidget *old_view; - ThunarHistory *history; + GtkWidget *new_view; + ThunarHistory *history = NULL; GList *selected_files = NULL; + gint page_num; + gboolean is_current_view; - /* drop the previous view (if any) */ - old_view = window->view; - if (G_LIKELY (window->view != NULL)) + _thunar_return_if_fail (view_type != G_TYPE_NONE); + + /* is the view we are replacing the active view? + * (note that this will be true if both view and window->view are NULL) */ + is_current_view = (view == window->view); + + /* save some settings from the old view for the new view */ + if (view != NULL) { - if (window->signal_handler_id_history_changed != 0) + /* disconnect from previous history if the old view is the active view */ + if (is_current_view && window->signal_handler_id_history_changed != 0) { - history = thunar_standard_view_get_history (THUNAR_STANDARD_VIEW (window->view)); + history = thunar_standard_view_get_history (THUNAR_STANDARD_VIEW (view)); g_signal_handler_disconnect (history, window->signal_handler_id_history_changed); window->signal_handler_id_history_changed = 0; } - /* get first visible file in the previous view */ - if (!thunar_view_get_visible_range (THUNAR_VIEW (window->view), &file, NULL)) + /* get first visible file in the old view */ + if (!thunar_view_get_visible_range (THUNAR_VIEW (view), &file, NULL)) file = NULL; - /* store the active directory */ - current_directory = thunar_navigator_get_current_directory (THUNAR_NAVIGATOR (window->view)); + /* store the active directory from the old view */ + current_directory = thunar_navigator_get_current_directory (THUNAR_NAVIGATOR (view)); if (current_directory != NULL) g_object_ref (current_directory); - /* remember the file selection */ - selected_files = thunar_g_file_list_copy (thunar_component_get_selected_files (THUNAR_COMPONENT (old_view))); + /* remember the file selection from the old view */ + selected_files = thunar_g_file_list_copy (thunar_component_get_selected_files (THUNAR_COMPONENT (view))); + + /* save the history of the current view */ + history = NULL; + if (THUNAR_IS_STANDARD_VIEW (view)) + history = thunar_standard_view_copy_history (THUNAR_STANDARD_VIEW (view)); } - window->view_type = view_type; - /* always open a new directory */ + if (is_current_view) + window->view_type = view_type; + + /* if we have not got a current directory from the old view, use the window's current directory */ if (current_directory == NULL && window->current_directory != NULL) current_directory = g_object_ref (window->current_directory); - /* allocate a new view of the requested type */ - if (G_LIKELY (window->view_type != G_TYPE_NONE)) - { - /* create new page */ - if (current_directory != NULL) - thunar_window_notebook_insert (window, current_directory); + _thunar_assert (current_directory != NULL); - /* 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); - } + /* find where to insert the new view */ + if (view != NULL) + page_num = gtk_notebook_page_num (GTK_NOTEBOOK (window->notebook), view); else + page_num = -1; + + /* insert the new view */ + new_view = thunar_window_notebook_insert (window, current_directory, view_type, page_num + 1, history); + + /* if we are replacing the active view, make the new view the active view */ + if (is_current_view) { - /* this should not happen under normal conditions */ - window->view = NULL; + /* switch to the new view */ + page_num = gtk_notebook_page_num (GTK_NOTEBOOK (window->notebook), new_view); + gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), page_num); + + /* take focus on the new view */ + gtk_widget_grab_focus (new_view); } + /* scroll to the previously visible file in the old view */ + if (G_UNLIKELY (file != NULL)) + thunar_view_scroll_to_file (THUNAR_VIEW (new_view), file, FALSE, TRUE, 0.0f, 0.0f); + /* destroy the old view */ - if (old_view != NULL) - gtk_widget_destroy (old_view); + if (view != NULL) + gtk_widget_destroy (view); /* restore the file selection */ - thunar_component_set_selected_files (THUNAR_COMPONENT (window->view), selected_files); + thunar_component_set_selected_files (THUNAR_COMPONENT (new_view), selected_files); thunar_g_file_list_free (selected_files); - /* remember the setting */ - if (gtk_widget_get_visible (GTK_WIDGET (window)) && window->view_type != G_TYPE_NONE) - g_object_set (G_OBJECT (window->preferences), "last-view", g_type_name (window->view_type), NULL); + /* remember the last view type if this is the active view and per-directory settings are not enabled */ + if (is_current_view && !window->per_directory && gtk_widget_get_visible (GTK_WIDGET (window)) && view_type != G_TYPE_NONE) + g_object_set (G_OBJECT (window->preferences), "last-view", g_type_name (view_type), NULL); /* release the file references */ if (G_UNLIKELY (file != NULL)) @@ -2774,8 +2826,28 @@ thunar_window_action_view_changed (ThunarWindow *window, if (G_UNLIKELY (current_directory != NULL)) g_object_unref (G_OBJECT (current_directory)); - history = thunar_standard_view_get_history (THUNAR_STANDARD_VIEW (window->view)); - window->signal_handler_id_history_changed = g_signal_connect_swapped (G_OBJECT (history), "history-changed", G_CALLBACK (thunar_window_history_changed), window); + /* connect to the new history if this is the active view */ + if (is_current_view) + { + history = thunar_standard_view_get_history (THUNAR_STANDARD_VIEW (new_view)); + window->signal_handler_id_history_changed = g_signal_connect_swapped (G_OBJECT (history), + "history-changed", + G_CALLBACK (thunar_window_history_changed), + window); + } +} + + + +static void +thunar_window_action_view_changed (ThunarWindow *window, + GType view_type) +{ + thunar_window_replace_view (window, window->view, view_type); + + /* if per-directory settings are enabled, save the view type for this directory */ + if (window->per_directory) + thunar_file_set_view_setting (window->current_directory, "view-type", g_type_name (view_type)); } @@ -3530,13 +3602,53 @@ void thunar_window_set_per_directory (ThunarWindow *window, gboolean per_directory) { - GType type = G_TYPE_NONE; - gchar *type_name; + GList *tabs, *lp; + ThunarFile *directory; + GType view_type; + gint page_num; + gchar *type_name; _thunar_return_if_fail (THUNAR_IS_WINDOW (window)); + /* reset to the default view type if we are turning per-directory settings off */ + if (!per_directory && window->per_directory) + { + /* determine the default view type */ + g_object_get (G_OBJECT (window->preferences), "default-view", &type_name, NULL); + view_type = g_type_from_name (type_name); + g_free (type_name); + + /* set the last view type */ + if (!g_type_is_a (view_type, G_TYPE_NONE) && !g_type_is_a (view_type, G_TYPE_INVALID)) + g_object_set (G_OBJECT (window->preferences), "last-view", g_type_name (view_type), NULL); + } + /* save the setting */ window->per_directory = per_directory; + + /* get all of the window's tabs */ + tabs = gtk_container_get_children (GTK_CONTAINER (window->notebook)); + + /* replace each tab with a tab of the correct view type */ + for (lp = tabs; lp != NULL; lp = lp->next) + { + if (!THUNAR_IS_STANDARD_VIEW (lp->data)) + continue; + + directory = thunar_navigator_get_current_directory (lp->data); + + if (!THUNAR_IS_FILE (directory)) + continue; + + /* find the correct position and view type for the new view */ + page_num = gtk_notebook_page_num (GTK_NOTEBOOK (window->notebook), lp->data); + view_type = thunar_window_view_type_for_directory (window, directory); + + /* replace the old view with a new one */ + thunar_window_replace_view (window, lp->data, view_type); + } + + g_list_free (tabs); } @@ -3569,9 +3681,6 @@ void thunar_window_set_current_directory (ThunarWindow *window, ThunarFile *current_directory) { - GType type; - gchar *type_name; - _thunar_return_if_fail (THUNAR_IS_WINDOW (window)); _thunar_return_if_fail (current_directory == NULL || THUNAR_IS_FILE (current_directory)); @@ -3587,39 +3696,50 @@ thunar_window_set_current_directory (ThunarWindow *window, g_object_unref (G_OBJECT (window->current_directory)); } - /* activate the new directory */ - window->current_directory = current_directory; - /* connect to the new directory */ if (G_LIKELY (current_directory != NULL)) { - /* take a reference on the file and connect the "changed"/"destroy" signals */ - g_signal_connect (G_OBJECT (current_directory), "changed", G_CALLBACK (thunar_window_current_directory_changed), window); + GType type; + gchar *type_name; + gint num_pages; + + /* take a reference on the file */ g_object_ref (G_OBJECT (current_directory)); - /* create a new view if the window is new */ - if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook)) == 0) + num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook)); + + /* if the window is new, get the default-view type and set it as the last-view type (if it is a valid type) + * so that it will be used as the initial view type for directories with no saved per-directory view type */ + if (num_pages == 0) { - /* determine the default view */ + /* determine the default view type */ g_object_get (G_OBJECT (window->preferences), "default-view", &type_name, NULL); type = g_type_from_name (type_name); g_free (type_name); - /* determine the last selected view if the last selected view preference is not selected */ - if (g_type_is_a (type, G_TYPE_NONE) || g_type_is_a (type, G_TYPE_INVALID)) - { - g_object_get (G_OBJECT (window->preferences), "last-view", &type_name, NULL); - type = g_type_from_name (type_name); - g_free (type_name); - } + /* set the last view type to the default view type if there is a default view type */ + if (!g_type_is_a (type, G_TYPE_NONE) && !g_type_is_a (type, G_TYPE_INVALID)) + g_object_set (G_OBJECT (window->preferences), "last-view", g_type_name (type), NULL); + } + + type = thunar_window_view_type_for_directory (window, current_directory); - /* fallback, in case nothing was set */ - if (g_type_is_a (type, G_TYPE_NONE) || g_type_is_a (type, G_TYPE_INVALID)) - type = THUNAR_TYPE_ICON_VIEW; + if (num_pages == 0) /* create a new view if the window is new */ + { + window->current_directory = current_directory; + thunar_window_replace_view (window, window->view, type); + } + else /* change the view type if necessary, and set the current directory */ + { + if (window->view != NULL && window->view_type != type) + thunar_window_replace_view (window, window->view, type); - thunar_window_action_view_changed (window, type); + window->current_directory = current_directory; } + /* connect the "changed"/"destroy" signals */ + g_signal_connect (G_OBJECT (current_directory), "changed", G_CALLBACK (thunar_window_current_directory_changed), window); + /* update window icon and title */ thunar_window_current_directory_changed (current_directory, window); @@ -3632,6 +3752,8 @@ thunar_window_set_current_directory (ThunarWindow *window, thunar_window_history_changed (window); gtk_widget_set_sensitive (window->location_toolbar_item_parent, !thunar_g_file_is_root (thunar_file_get_file (current_directory))); } + else + window->current_directory = NULL; /* tell everybody that we have a new "current-directory", * we do this first so other widgets display the new @@ -3763,7 +3885,7 @@ thunar_window_set_directories (ThunarWindow *window, if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook)) == 0) thunar_window_set_current_directory (window, directory); else - thunar_window_notebook_insert (window, directory); + thunar_window_notebook_open_new_tab (window, directory); } g_object_unref (G_OBJECT (directory)); @@ -3902,3 +4024,51 @@ thunar_window_history_clicked (GtkWidget *button, return FALSE; } + + + +/** + * thunar_window_view_type_for_directory: + * @window : a #ThunarWindow instance. + * @directory : #ThunarFile representing the directory + * + * Return value: the #GType representing the view type which + * @window would use to display @directory. + **/ +GType +thunar_window_view_type_for_directory (ThunarWindow *window, + ThunarFile *directory) +{ + GType type = G_TYPE_NONE; + gchar *type_name; + + _thunar_return_val_if_fail (THUNAR_IS_WINDOW (window), G_TYPE_NONE); + _thunar_return_val_if_fail (THUNAR_IS_FILE (directory), G_TYPE_NONE); + + /* if the directory has a saved view type and per-directory view types are enabled, we use it */ + if (window->per_directory) + { + type_name = thunar_file_get_view_setting (directory, "view-type"); + if (type_name != NULL) + { + type = g_type_from_name (type_name); + g_free (type_name); + } + } + + /* if there is no saved view type for the directory or per-directory view types are not enabled, + * we use the last view type */ + if (g_type_is_a (type, G_TYPE_NONE) || g_type_is_a (type, G_TYPE_INVALID)) + { + /* determine the last view type */ + g_object_get (G_OBJECT (window->preferences), "last-view", &type_name, NULL); + type = g_type_from_name (type_name); + g_free (type_name); + } + + /* fallback view type, in case nothing was set */ + if (g_type_is_a (type, G_TYPE_NONE) || g_type_is_a (type, G_TYPE_INVALID)) + type = THUNAR_TYPE_ICON_VIEW; + + return type; +} diff --git a/thunar/thunar-window.h b/thunar/thunar-window.h index 5b595d6f..831bdb46 100644 --- a/thunar/thunar-window.h +++ b/thunar/thunar-window.h @@ -110,7 +110,7 @@ gboolean thunar_window_set_directories (Thu void thunar_window_update_directories (ThunarWindow *window, ThunarFile *old_directory, ThunarFile *new_directory); -void thunar_window_notebook_insert (ThunarWindow *window, +void thunar_window_notebook_open_new_tab (ThunarWindow *window, ThunarFile *directory); gboolean thunar_window_has_shortcut_sidepane (ThunarWindow *window); GtkWidget* thunar_window_get_sidepane (ThunarWindow *window); -- 2.26.2 From 6c104933752e388d54fa1772b98fcc08a1865f95 Mon Sep 17 00:00:00 2001 From: Reuben Green Date: Sat, 16 May 2020 15:02:43 +0100 Subject: [PATCH 4/5] added preferences to represent per-directory to thunar-standard-view, got it all plumbed in, added a handler for changes to the preference --- thunar/thunar-standard-view.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/thunar/thunar-standard-view.c b/thunar/thunar-standard-view.c index 2f2637e4..8072063d 100644 --- a/thunar/thunar-standard-view.c +++ b/thunar/thunar-standard-view.c @@ -72,6 +72,7 @@ enum PROP_SHOW_HIDDEN, PROP_STATUSBAR_TEXT, PROP_ZOOM_LEVEL, + PROP_PER_DIRECTORY, PROP_THUMBNAIL_DRAW_FRAMES, PROP_ACCEL_GROUP, N_PROPERTIES @@ -132,6 +133,8 @@ static ThunarZoomLevel thunar_standard_view_get_zoom_level (Thu static void thunar_standard_view_set_zoom_level (ThunarView *view, ThunarZoomLevel zoom_level); static void thunar_standard_view_reset_zoom_level (ThunarView *view); +static void thunar_standard_view_set_per_directory (ThunarStandardView *standard_view, + gboolean per_directory); static void thunar_standard_view_reload (ThunarView *view, gboolean reload_info); static gboolean thunar_standard_view_get_visible_range (ThunarView *view, @@ -276,6 +279,9 @@ struct _ThunarStandardViewPrivate /* zoom-level support */ ThunarZoomLevel zoom_level; + /* per-directory settings */ + gboolean per_directory; + /* scroll_to_file support */ GHashTable *scroll_to_files; @@ -436,6 +442,18 @@ thunar_standard_view_class_init (ThunarStandardViewClass *klass) NULL, EXO_PARAM_READABLE); + /** + * ThunarStandardView:per-directory: + * + * Whether to use per directory settings. + **/ + standard_view_props[PROP_PER_DIRECTORY] = + g_param_spec_boolean ("per-directory", + "per-directory", + "per-directory", + FALSE, + EXO_PARAM_READWRITE); + /** * ThunarStandardView:thumbnail-draw-frames: * @@ -703,6 +721,9 @@ thunar_standard_view_constructor (GType type, g_signal_connect (adjustment, "value-changed", G_CALLBACK (thunar_standard_view_scrolled), object); + /* synchronise the "per-directory" property with the global "misc-per-directory" property */ + exo_binding_new (G_OBJECT (standard_view->preferences), "misc-per-directory", G_OBJECT (standard_view), "per-directory"); + /* done, we have a working object */ return object; } @@ -918,6 +939,10 @@ thunar_standard_view_set_property (GObject *object, thunar_view_set_zoom_level (THUNAR_VIEW (object), g_value_get_enum (value)); break; + case PROP_PER_DIRECTORY: + thunar_standard_view_set_per_directory (standard_view, g_value_get_boolean (value)); + break; + case PROP_THUMBNAIL_DRAW_FRAMES: g_object_set (G_OBJECT (standard_view->icon_factory), "thumbnail-draw-frames", g_value_get_boolean (value), NULL); thunar_standard_view_reload(THUNAR_VIEW (object), TRUE); @@ -1529,6 +1554,16 @@ thunar_standard_view_reset_zoom_level (ThunarView *view) +void +thunar_standard_view_set_per_directory (ThunarStandardView *standard_view, + gboolean per_directory) +{ + /* save the setting */ + standard_view->priv->per_directory = per_directory; +} + + + static void thunar_standard_view_reload (ThunarView *view, gboolean reload_info) -- 2.26.2 From 5ba921ffd9331a7386e834f43dc9cbafad95c45a Mon Sep 17 00:00:00 2001 From: Reuben Green Date: Sat, 16 May 2020 15:17:42 +0100 Subject: [PATCH 5/5] implemented per-directory sort column and sort order --- thunar/thunar-enum-types.c | 42 +++++++++++++ thunar/thunar-enum-types.h | 5 +- thunar/thunar-file.c | 8 +++ thunar/thunar-standard-view.c | 112 +++++++++++++++++++++++++++++++--- 4 files changed, 159 insertions(+), 8 deletions(-) diff --git a/thunar/thunar-enum-types.c b/thunar/thunar-enum-types.c index 63b48218..789301e7 100644 --- a/thunar/thunar-enum-types.c +++ b/thunar/thunar-enum-types.c @@ -121,6 +121,48 @@ thunar_column_get_type (void) +const gchar* +thunar_column_string_from_value (ThunarColumn value) +{ + GEnumClass *enum_class; + GEnumValue *enum_value; + + enum_class = g_type_class_ref (THUNAR_TYPE_COLUMN); + enum_value = g_enum_get_value (enum_class, value); + + g_type_class_unref (enum_class); + + if (enum_value != NULL) + return enum_value->value_name; + else + return NULL; +} + + + +gboolean +thunar_column_value_from_string (const gchar *value_string, + gint *value) +{ + GEnumClass *enum_class; + GEnumValue *enum_value; + + enum_class = g_type_class_ref (THUNAR_TYPE_COLUMN); + enum_value = g_enum_get_value_by_name (enum_class, value_string); + + g_type_class_unref (enum_class); + + if (enum_value != NULL) + { + *value = enum_value->value; + return TRUE; + } + else + return FALSE; +} + + + GType thunar_icon_size_get_type (void) { diff --git a/thunar/thunar-enum-types.h b/thunar/thunar-enum-types.h index 17a792d4..17fd6cf1 100644 --- a/thunar/thunar-enum-types.h +++ b/thunar/thunar-enum-types.h @@ -116,7 +116,10 @@ typedef enum THUNAR_N_VISIBLE_COLUMNS = THUNAR_COLUMN_FILE, } ThunarColumn; -GType thunar_column_get_type (void) G_GNUC_CONST; +GType thunar_column_get_type (void) G_GNUC_CONST; +const gchar* thunar_column_string_from_value (ThunarColumn value); +gboolean thunar_column_value_from_string (const gchar *value_string, + gint *value); #define THUNAR_TYPE_ICON_SIZE (thunar_icon_size_get_type ()) diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c index b7d9019e..afba9243 100644 --- a/thunar/thunar-file.c +++ b/thunar/thunar-file.c @@ -4528,6 +4528,10 @@ thunar_file_get_view_setting (ThunarFile *file, if (g_strcmp0 (setting_name, "view-type") == 0) attr_name = "metadata::thunar::view-type"; + else if (g_strcmp0 (setting_name, "sort-column") == 0) + attr_name = "metadata::thunar::sort-column"; + else if (g_strcmp0 (setting_name, "sort-order") == 0) + attr_name = "metadata::thunar::sort-order"; else return NULL; @@ -4552,6 +4556,10 @@ thunar_file_set_view_setting (ThunarFile *file, if (g_strcmp0 (setting_name, "view-type") == 0) attr_name = "metadata::thunar::view-type"; + else if (g_strcmp0 (setting_name, "sort-column") == 0) + attr_name = "metadata::thunar::sort-column"; + else if (g_strcmp0 (setting_name, "sort-order") == 0) + attr_name = "metadata::thunar::sort-order"; else return; diff --git a/thunar/thunar-standard-view.c b/thunar/thunar-standard-view.c index 8072063d..2bc21761 100644 --- a/thunar/thunar-standard-view.c +++ b/thunar/thunar-standard-view.c @@ -133,6 +133,8 @@ static ThunarZoomLevel thunar_standard_view_get_zoom_level (Thu static void thunar_standard_view_set_zoom_level (ThunarView *view, ThunarZoomLevel zoom_level); static void thunar_standard_view_reset_zoom_level (ThunarView *view); +static void thunar_standard_view_apply_per_directory (ThunarStandardView *standard_view, + ThunarFile *directory); static void thunar_standard_view_set_per_directory (ThunarStandardView *standard_view, gboolean per_directory); static void thunar_standard_view_reload (ThunarView *view, @@ -1300,6 +1302,10 @@ thunar_standard_view_set_current_directory (ThunarNavigator *navigator, /* store the directory in the history */ thunar_navigator_set_current_directory (THUNAR_NAVIGATOR (standard_view->priv->history), current_directory); + /* if per-directory settings are enabled, set the sort column and sort order*/ + if (standard_view->priv->per_directory) + thunar_standard_view_apply_per_directory (standard_view, current_directory); + /* We drop the model from the view as a simple optimization to speed up * the process of disconnecting the model data from the view. */ @@ -1554,12 +1560,82 @@ thunar_standard_view_reset_zoom_level (ThunarView *view) -void +static void +thunar_standard_view_apply_per_directory (ThunarStandardView *standard_view, + ThunarFile *directory) +{ + gchar *sort_column_name; + gchar *sort_order_name; + gint sort_column; + GtkSortType sort_order; + + /* get the current sort column and sort order */ + gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (standard_view->model), &sort_column, &sort_order); + + /* get the stored per-directory settings (if any) */ + sort_column_name = thunar_file_get_view_setting (directory, "sort-column"); + sort_order_name = thunar_file_get_view_setting (directory, "sort-order"); + + /* convert the sort column name to a value */ + if (sort_column_name != NULL) + thunar_column_value_from_string (sort_column_name, &sort_column); + + /* convert the sort order name to a value */ + if (sort_order_name != NULL) + { + if (g_strcmp0 (sort_order_name, "GTK_SORT_ASCENDING") == 0) + sort_order = GTK_SORT_ASCENDING; + if (g_strcmp0 (sort_order_name, "GTK_SORT_DESCENDING") == 0) + sort_order = GTK_SORT_DESCENDING; + } + + /* thunar_standard_view_sort_column_changed saves the per-directory settings to the directory, but we do not + * want that behaviour here so we disconnect the signal before calling gtk_tree_sortable_set_sort_column_id */ + g_signal_handlers_disconnect_by_func (G_OBJECT (standard_view->model), + G_CALLBACK (thunar_standard_view_sort_column_changed), + standard_view); + + /* apply the sort column and sort order */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (standard_view->model), sort_column, sort_order); + + /* keep the currently selected files selected after the change */ + thunar_component_restore_selection (THUNAR_COMPONENT (standard_view)); + + /* reconnect the signal */ + g_signal_connect (G_OBJECT (standard_view->model), + "sort-column-changed", + G_CALLBACK (thunar_standard_view_sort_column_changed), + standard_view); +} + + + +static void thunar_standard_view_set_per_directory (ThunarStandardView *standard_view, gboolean per_directory) { /* save the setting */ standard_view->priv->per_directory = per_directory; + + /* if there is no current directory then return */ + if (standard_view->priv->current_directory == NULL) + return; + + /* apply the appropriate settings */ + if (per_directory) + { + /* apply the per-directory settings (if any) */ + thunar_standard_view_apply_per_directory (standard_view, standard_view->priv->current_directory); + } + else + { + gint sort_column; + GtkSortType sort_order; + + /* apply the last sort column and sort order */ + g_object_get (G_OBJECT (standard_view->preferences), "last-sort-column", &sort_column, "last-sort-order", &sort_order, NULL); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (standard_view->model), sort_column, sort_order); + } } @@ -3043,14 +3119,36 @@ thunar_standard_view_sort_column_changed (GtkTreeSortable *tree_sortable, /* keep the currently selected files selected after the change */ thunar_component_restore_selection (THUNAR_COMPONENT (standard_view)); - /* determine the new sort column and sort order */ + /* determine the new sort column and sort order, and save them */ if (gtk_tree_sortable_get_sort_column_id (tree_sortable, &sort_column, &sort_order)) { - /* remember the new values as default */ - g_object_set (G_OBJECT (standard_view->preferences), - "last-sort-column", sort_column, - "last-sort-order", sort_order, - NULL); + if (!standard_view->priv->per_directory) + { + /* remember the new values as default */ + g_object_set (G_OBJECT (standard_view->preferences), + "last-sort-column", sort_column, + "last-sort-order", sort_order, + NULL); + } + else + { + const gchar *sort_column_name; + const gchar *sort_order_name; + + /* save the sort column name */ + sort_column_name = thunar_column_string_from_value (sort_column); + if (sort_column_name != NULL) + thunar_file_set_view_setting (standard_view->priv->current_directory, "sort-column", sort_column_name); + + /* convert the sort order to a string */ + if (sort_order == GTK_SORT_ASCENDING) + sort_order_name = "GTK_SORT_ASCENDING"; + if (sort_order == GTK_SORT_DESCENDING) + sort_order_name = "GTK_SORT_DESCENDING"; + + /* save the sort order */ + thunar_file_set_view_setting (standard_view->priv->current_directory, "sort-order", sort_order_name); + } } } -- 2.26.2