From 4a36de471beb142047d3fa80391babef8db8cc04 Mon Sep 17 00:00:00 2001 From: Amrit Borah <elessar1802@gmail.com> Date: Wed, 3 Aug 2022 21:52:46 +0000 Subject: [PATCH] Add posibillity to set custom color to specific files (Issue: #160) - Can be enabled/disabled in the 'view' menu - Custom colors can be set in the files 'properties' dialog - It is possible to pick a custom background color and a custom text color per file - Usage of gvfs metadata to store the file specific color information - Introduces a custom text renderer to get the job done MR: !226 --- thunar/Makefile.am | 2 + thunar/thunar-abstract-icon-view.c | 9 +- thunar/thunar-application.c | 2 + thunar/thunar-details-view.c | 54 ++++- thunar/thunar-file.c | 35 +++ thunar/thunar-file.h | 2 + thunar/thunar-icon-renderer.c | 73 ++++++ thunar/thunar-icon-renderer.h | 3 + thunar/thunar-preferences.c | 14 ++ thunar/thunar-properties-dialog.c | 373 +++++++++++++++++++++++++++++ thunar/thunar-standard-view.c | 105 +++++++- thunar/thunar-standard-view.h | 7 + thunar/thunar-text-renderer.c | 276 +++++++++++++++++++++ thunar/thunar-text-renderer.h | 43 ++++ thunar/thunar-util.c | 185 ++++++++++++++ thunar/thunar-util.h | 5 + thunar/thunar-window.c | 28 +++ thunar/thunar-window.h | 1 + thunarx/thunarx-file-info.h | 3 +- 19 files changed, 1206 insertions(+), 14 deletions(-) create mode 100644 thunar/thunar-text-renderer.c create mode 100644 thunar/thunar-text-renderer.h diff --git a/thunar/Makefile.am b/thunar/Makefile.am index 65572ea97..db4c66b88 100644 --- a/thunar/Makefile.am +++ b/thunar/Makefile.am @@ -107,6 +107,8 @@ thunar_SOURCES = \ thunar-icon-factory.h \ thunar-icon-renderer.c \ thunar-icon-renderer.h \ + thunar-text-renderer.c \ + thunar-text-renderer.h \ thunar-icon-view.c \ thunar-icon-view.h \ thunar-image.c \ diff --git a/thunar/thunar-abstract-icon-view.c b/thunar/thunar-abstract-icon-view.c index 545f07d28..dfaacce3f 100644 --- a/thunar/thunar-abstract-icon-view.c +++ b/thunar/thunar-abstract-icon-view.c @@ -177,7 +177,7 @@ thunar_abstract_icon_view_init (ThunarAbstractIconView *abstract_icon_view) exo_icon_view_set_selection_mode (EXO_ICON_VIEW (view), GTK_SELECTION_MULTIPLE); /* add the abstract icon renderer */ - g_object_set (G_OBJECT (THUNAR_STANDARD_VIEW (abstract_icon_view)->icon_renderer), "follow-state", TRUE, NULL); + g_object_set (G_OBJECT (THUNAR_STANDARD_VIEW (abstract_icon_view)->icon_renderer), "follow-state", TRUE, "rounded-corners", TRUE, NULL); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (view), THUNAR_STANDARD_VIEW (abstract_icon_view)->icon_renderer, FALSE); gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (view), THUNAR_STANDARD_VIEW (abstract_icon_view)->icon_renderer, "file", THUNAR_COLUMN_FILE); @@ -185,6 +185,7 @@ thunar_abstract_icon_view_init (ThunarAbstractIconView *abstract_icon_view) /* add the name renderer */ /*FIXME text prelit*/ /*g_object_set (G_OBJECT (THUNAR_STANDARD_VIEW (abstract_icon_view)->name_renderer), "follow-state", TRUE, NULL);*/ + g_object_set (G_OBJECT (THUNAR_STANDARD_VIEW (abstract_icon_view)->name_renderer), "rounded-corners", TRUE, NULL); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (view), THUNAR_STANDARD_VIEW (abstract_icon_view)->name_renderer, TRUE); gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (view), THUNAR_STANDARD_VIEW (abstract_icon_view)->name_renderer, "text", THUNAR_COLUMN_NAME); @@ -641,8 +642,6 @@ thunar_abstract_icon_view_zoom_level_changed (ThunarAbstractIconView *abstract_i { _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (abstract_icon_view)); - /* we use the same trick as with ThunarDetailsView here, simply because its simple :-) */ - gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (gtk_bin_get_child (GTK_BIN (abstract_icon_view))), - THUNAR_STANDARD_VIEW (abstract_icon_view)->icon_renderer, - NULL, NULL, NULL); + /* this should reload the window without unsetting the cell_layout_data_funcs (needed for highlighting) */ + thunar_view_reload (THUNAR_VIEW (abstract_icon_view), TRUE); } diff --git a/thunar/thunar-application.c b/thunar/thunar-application.c index 1faffc8a0..debf20f28 100644 --- a/thunar/thunar-application.c +++ b/thunar/thunar-application.c @@ -660,6 +660,8 @@ thunar_application_load_css (void) ".shortcuts-pane { border-top-style: solid; }" /* make border thicker during DnD */ ".standard-view { border-left-width: 0px; border-right-width: 0px; }" + /* for the example box in properties dialog > highlight tab */ + "#example { border-radius: 10px; }" ".standard-view:drop(active) { border-width: 2px; }", -1, NULL); screen = gdk_screen_get_default (); gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER (css_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); diff --git a/thunar/thunar-details-view.c b/thunar/thunar-details-view.c index 5395dd8cc..d2cd93b38 100644 --- a/thunar/thunar-details-view.c +++ b/thunar/thunar-details-view.c @@ -112,6 +112,8 @@ static void thunar_details_view_disconnect_accelerators (ThunarStandardV static void thunar_details_view_append_menu_items (ThunarStandardView *standard_view, GtkMenu *menu, GtkAccelGroup *accel_group); +static void thunar_details_view_highlight_option_changed(ThunarDetailsView *details_view); + struct _ThunarDetailsViewClass @@ -140,6 +142,7 @@ struct _ThunarDetailsView /* event source id for thunar_details_view_zoom_level_changed_reload_fixed_height */ guint idle_id; + GtkCellRenderer *renderers[THUNAR_N_VISIBLE_COLUMNS]; }; @@ -250,10 +253,26 @@ thunar_details_view_init (ThunarDetailsView *details_view) /* allocate the shared right-aligned text renderer */ right_aligned_renderer = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, "xalign", 1.0f, NULL); + + /* this is required in order to disable foreground & background colors on text renderers when the feature is disabled */ + g_object_bind_property (G_OBJECT (THUNAR_STANDARD_VIEW (details_view)->preferences), "misc-highlighting-enabled", + G_OBJECT (right_aligned_renderer), "foreground-set", + G_BINDING_SYNC_CREATE); + g_object_bind_property (G_OBJECT (THUNAR_STANDARD_VIEW (details_view)->preferences), "misc-highlighting-enabled", + G_OBJECT (right_aligned_renderer), "background-set", + G_BINDING_SYNC_CREATE); g_object_ref_sink (G_OBJECT (right_aligned_renderer)); /* allocate the shared left-aligned text renderer */ left_aligned_renderer = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, "xalign", 0.0f, NULL); + + /* this is required in order to disable foreground & background colors on text renderers when the feature is disabled */ + g_object_bind_property (G_OBJECT (THUNAR_STANDARD_VIEW (details_view)->preferences), "misc-highlighting-enabled", + G_OBJECT (left_aligned_renderer), "foreground-set", + G_BINDING_SYNC_CREATE); + g_object_bind_property (G_OBJECT (THUNAR_STANDARD_VIEW (details_view)->preferences), "misc-highlighting-enabled", + G_OBJECT (left_aligned_renderer), "background-set", + G_BINDING_SYNC_CREATE); g_object_ref_sink (G_OBJECT (left_aligned_renderer)); /* allocate the tree view columns */ @@ -286,14 +305,12 @@ thunar_details_view_init (ThunarDetailsView *details_view) gtk_tree_view_column_set_attributes (details_view->columns[column], THUNAR_STANDARD_VIEW (details_view)->name_renderer, "text", THUNAR_COLUMN_NAME, NULL); - - /* add some spacing between the icon and the name */ - gtk_tree_view_column_set_spacing (details_view->columns[column], 2); } else { /* size is right aligned, everything else is left aligned */ renderer = (column == THUNAR_COLUMN_SIZE || column == THUNAR_COLUMN_SIZE_IN_BYTES) ? right_aligned_renderer : left_aligned_renderer; + details_view->renderers[column] = renderer; /* add the renderer */ gtk_tree_view_column_pack_start (details_view->columns[column], renderer, TRUE); @@ -334,6 +351,10 @@ thunar_details_view_init (ThunarDetailsView *details_view) gtk_tree_view_column_set_expand (details_view->columns[THUNAR_COLUMN_NAME], TRUE); } + g_signal_connect_swapped (THUNAR_STANDARD_VIEW (details_view)->preferences, "notify::misc-highlighting-enabled", + G_CALLBACK (thunar_details_view_highlight_option_changed), details_view); + thunar_details_view_highlight_option_changed (details_view); + /* release the shared text renderers */ g_object_unref (G_OBJECT (right_aligned_renderer)); g_object_unref (G_OBJECT (left_aligned_renderer)); @@ -402,6 +423,9 @@ thunar_details_view_finalize (GObject *object) if (details_view->idle_id) g_source_remove (details_view->idle_id); + g_signal_handlers_disconnect_by_func (G_OBJECT (THUNAR_STANDARD_VIEW (details_view)->preferences), + thunar_details_view_highlight_option_changed, details_view); + (*G_OBJECT_CLASS (thunar_details_view_parent_class)->finalize) (object); } @@ -1148,3 +1172,27 @@ thunar_details_view_set_location_column_visible (ThunarDetailsView *details_ thunar_column_model_set_column_visible (details_view->column_model, THUNAR_COLUMN_LOCATION, visible); } + + +static void +thunar_details_view_highlight_option_changed (ThunarDetailsView *details_view) +{ + GtkTreeCellDataFunc function = NULL; + gboolean show_highlight; + gint column; + + g_object_get (G_OBJECT (THUNAR_STANDARD_VIEW (details_view)->preferences), "misc-highlighting-enabled", &show_highlight, NULL); + + if (show_highlight) + function = (GtkTreeCellDataFunc) THUNAR_STANDARD_VIEW_GET_CLASS (details_view)->cell_layout_data_func; + + /* set the data functions for the respective renderers */ + for (column = 0; column < THUNAR_N_VISIBLE_COLUMNS; column++) + { + if (column == THUNAR_COLUMN_NAME) + continue; + gtk_tree_view_column_set_cell_data_func (GTK_TREE_VIEW_COLUMN (details_view->columns[column]), + GTK_CELL_RENDERER (details_view->renderers[column]), + function, NULL, NULL); + } +} diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c index 9e2c850d4..b09bd05ab 100644 --- a/thunar/thunar-file.c +++ b/thunar/thunar-file.c @@ -4776,6 +4776,41 @@ thunar_file_set_metadata_setting (ThunarFile *file, +/** + * thunar_file_clear_metadata_setting: + * @file : a #ThunarFile instance. + * @setting_name : the name of the setting to clear + * + * Clear the metadata setting @setting_name of @file + **/ +void +thunar_file_clear_metadata_setting (ThunarFile *file, + const gchar *setting_name) +{ + gchar *attr_name; + + _thunar_return_if_fail (THUNAR_IS_FILE (file)); + + if (file->info == NULL) + return; + + /* convert the setting name to an attribute name */ + attr_name = g_strdup_printf ("metadata::thunar-%s", setting_name); + + if (!g_file_info_has_attribute (file->info, attr_name)) + { + g_free (attr_name); + return; + } + + g_file_info_remove_attribute (file->info, attr_name); + g_file_set_attribute (file->gfile, attr_name, G_FILE_ATTRIBUTE_TYPE_INVALID, + NULL, G_FILE_QUERY_INFO_NONE, NULL, NULL); + g_free (attr_name); +} + + + /** * thunar_file_clear_directory_specific_settings: * @file : a #ThunarFile instance. diff --git a/thunar/thunar-file.h b/thunar/thunar-file.h index 459dfa0b1..9e644bf48 100644 --- a/thunar/thunar-file.h +++ b/thunar/thunar-file.h @@ -287,6 +287,8 @@ void thunar_file_set_metadata_setting (ThunarFile const gchar *setting_name, const gchar *setting_value, gboolean async); +void thunar_file_clear_metadata_setting (ThunarFile *file, + const gchar *setting_name); void thunar_file_clear_directory_specific_settings (ThunarFile *file); gboolean thunar_file_has_directory_specific_settings (ThunarFile *file); diff --git a/thunar/thunar-icon-renderer.c b/thunar/thunar-icon-renderer.c index 836e1d555..94f519542 100644 --- a/thunar/thunar-icon-renderer.c +++ b/thunar/thunar-icon-renderer.c @@ -27,6 +27,7 @@ #include <thunar/thunar-icon-factory.h> #include <thunar/thunar-icon-renderer.h> #include <thunar/thunar-private.h> +#include <thunar/thunar-util.h> @@ -38,6 +39,9 @@ enum PROP_EMBLEMS, PROP_FOLLOW_STATE, PROP_SIZE, + PROP_HIGHLIGHT_COLOR, + PROP_ROUNDED_CORNERS, + PROP_HIGHLIGHTING_ENABLED, }; @@ -154,6 +158,46 @@ thunar_icon_renderer_class_init (ThunarIconRendererClass *klass) THUNAR_TYPE_ICON_SIZE, THUNAR_ICON_SIZE_32, G_PARAM_CONSTRUCT | EXO_PARAM_READWRITE)); + + + + /** + * ThunarIconRenderer:highlight-color: + * + * The color with which the cell should be highlighted. + **/ + g_object_class_install_property (gobject_class, + PROP_HIGHLIGHT_COLOR, + g_param_spec_string ("highlight-color", "highlight-color", "highlight-color", + NULL, + EXO_PARAM_READWRITE)); + + + + /** + * ThunarIconRenderer:rounded-corners: + * + * Determines if the cell should be clipped to rounded-corners. + * Useful when highlighting is enabled & a highlight color is set. + **/ + g_object_class_install_property (gobject_class, + PROP_ROUNDED_CORNERS, + g_param_spec_boolean ("rounded-corners", "rounded-corners", "rounded-corners", + FALSE, + EXO_PARAM_READWRITE)); + + + + /** + * ThunarIconRenderer:highlighting-enabled: + * + * Determines if the cell background should be drawn with highlight color. + **/ + g_object_class_install_property (gobject_class, + PROP_HIGHLIGHTING_ENABLED, + g_param_spec_boolean ("highlighting-enabled", "highlighting-enabled", "highlighting-enabled", + FALSE, + EXO_PARAM_READWRITE)); } @@ -177,6 +221,7 @@ thunar_icon_renderer_finalize (GObject *object) g_object_unref (G_OBJECT (icon_renderer->drop_file)); if (G_LIKELY (icon_renderer->file != NULL)) g_object_unref (G_OBJECT (icon_renderer->file)); + g_free (icon_renderer->highlight_color); (*G_OBJECT_CLASS (thunar_icon_renderer_parent_class)->finalize) (object); } @@ -213,6 +258,18 @@ thunar_icon_renderer_get_property (GObject *object, g_value_set_enum (value, icon_renderer->size); break; + case PROP_HIGHLIGHT_COLOR: + g_value_set_string (value, icon_renderer->highlight_color); + break; + + case PROP_ROUNDED_CORNERS: + g_value_set_boolean (value, icon_renderer->rounded_corners); + break; + + case PROP_HIGHLIGHTING_ENABLED: + g_value_set_boolean (value, icon_renderer->highlighting_enabled); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -255,6 +312,19 @@ thunar_icon_renderer_set_property (GObject *object, icon_renderer->size = g_value_get_enum (value); break; + case PROP_HIGHLIGHT_COLOR: + g_free (icon_renderer->highlight_color); + icon_renderer->highlight_color = g_value_dup_string (value); + break; + + case PROP_ROUNDED_CORNERS: + icon_renderer->rounded_corners = g_value_get_boolean (value); + break; + + case PROP_HIGHLIGHTING_ENABLED: + icon_renderer->highlighting_enabled = g_value_get_boolean (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -405,6 +475,9 @@ thunar_icon_renderer_render (GtkCellRenderer *renderer, g_object_get (renderer, "is-expanded", &is_expanded, NULL); + if (THUNAR_ICON_RENDERER (renderer)->highlighting_enabled) + thunar_util_clip_view_background (renderer, cr, background_area, widget, flags); + /* determine the icon state */ icon_state = (icon_renderer->drop_file != icon_renderer->file) ? is_expanded diff --git a/thunar/thunar-icon-renderer.h b/thunar/thunar-icon-renderer.h index 6186495a6..99f42659c 100644 --- a/thunar/thunar-icon-renderer.h +++ b/thunar/thunar-icon-renderer.h @@ -49,6 +49,9 @@ struct _ThunarIconRenderer gboolean emblems; gboolean follow_state; ThunarIconSize size; + gchar *highlight_color; + gboolean rounded_corners; + gboolean highlighting_enabled; }; GType thunar_icon_renderer_get_type (void) G_GNUC_CONST; diff --git a/thunar/thunar-preferences.c b/thunar/thunar-preferences.c index 1829d276c..ae9e0a298 100644 --- a/thunar/thunar-preferences.c +++ b/thunar/thunar-preferences.c @@ -121,6 +121,7 @@ enum PROP_MISC_SWITCH_TO_NEW_TAB, PROP_MISC_VERTICAL_SPLIT_PANE, PROP_MISC_COMPACT_VIEW_MAX_CHARS, + PROP_MISC_HIGHLIGHTING_ENABLED, N_PROPERTIES, }; @@ -1105,6 +1106,19 @@ thunar_preferences_class_init (ThunarPreferencesClass *klass) 50, EXO_PARAM_READWRITE); + /** + * ThunarPreferences:misc-highlighting-enabled + * + * If true file highlighting feature across the various views is enabled. + * Can be toggled using the View > Show File Highlight. + **/ + preferences_props[PROP_MISC_HIGHLIGHTING_ENABLED] = + g_param_spec_boolean ("misc-highlighting-enabled", + "MiscHighlightingEnabled", + NULL, + thunar_g_vfs_metadata_is_supported (), + EXO_PARAM_READWRITE); + /* install all properties */ g_object_class_install_properties (gobject_class, N_PROPERTIES, preferences_props); } diff --git a/thunar/thunar-properties-dialog.c b/thunar/thunar-properties-dialog.c index a016777c9..38bc55773 100644 --- a/thunar/thunar-properties-dialog.c +++ b/thunar/thunar-properties-dialog.c @@ -76,6 +76,15 @@ enum LAST_SIGNAL, }; +/* Notebook Pages */ +enum +{ + NOTEBOOK_PAGE_GENERAL, + NOTEBOOK_PAGE_EMBLEMS, + NOTEBOOK_PAGE_PERMISSIONS, + NOTEBOOK_PAGE_HIGHLIGHT +}; + static void thunar_properties_dialog_dispose (GObject *object); @@ -101,6 +110,17 @@ static void thunar_properties_dialog_icon_button_clicked (GtkWidget static void thunar_properties_dialog_update (ThunarPropertiesDialog *dialog); static void thunar_properties_dialog_update_providers (ThunarPropertiesDialog *dialog); static GList *thunar_properties_dialog_get_files (ThunarPropertiesDialog *dialog); +static void thunar_properties_dialog_reset_highlight (ThunarPropertiesDialog *dialog); +static void thunar_properties_dialog_apply_highlight (ThunarPropertiesDialog *dialog); +static void thunar_properties_dialog_set_foreground (ThunarPropertiesDialog *dialog); +static void thunar_properties_dialog_set_background (ThunarPropertiesDialog *dialog); +static void thunar_properties_dialog_update_apply_button (ThunarPropertiesDialog *dialog); +static void thunar_properties_dialog_colorize_example_box (ThunarPropertiesDialog *dialog, + const gchar *background, + const gchar *foreground); +static void thunar_properties_dialog_color_editor_changed (ThunarPropertiesDialog *dialog); +static void thunar_properties_dialog_color_editor_close (ThunarPropertiesDialog *dialog); +static void thunar_properties_dialog_notebook_page_changed(ThunarPropertiesDialog *dialog); struct _ThunarPropertiesDialogClass @@ -151,6 +171,14 @@ struct _ThunarPropertiesDialog GtkWidget *permissions_chooser; GtkWidget *content_label; GtkWidget *content_value_label; + GtkWidget *color_chooser; + GtkWidget *example_box; + GtkWidget *highlight_buttons; + GtkWidget *highlight_apply_button; + GtkWidget *editor_button; + + gchar *foreground_color; + gchar *background_color; }; @@ -237,6 +265,9 @@ thunar_properties_dialog_init (ThunarPropertiesDialog *dialog) GtkWidget *spacer; guint row = 0; GtkWidget *image; + GtkWidget *button; + GtkWidget *infobar; + GtkWidget *frame; /* acquire a reference on the preferences and monitor the "misc-date-style" and "misc-file-size-binary" settings */ @@ -663,6 +694,140 @@ thunar_properties_dialog_init (ThunarPropertiesDialog *dialog) gtk_notebook_append_page (GTK_NOTEBOOK (dialog->notebook), dialog->permissions_chooser, label); gtk_widget_show (dialog->permissions_chooser); gtk_widget_show (label); + + /* + Highlight Color Chooser + */ + grid = gtk_grid_new (); + label = gtk_label_new (_("Highlight")); + gtk_widget_set_halign (grid, GTK_ALIGN_CENTER); + gtk_grid_set_column_spacing (GTK_GRID (grid), 12); + gtk_grid_set_row_spacing (GTK_GRID (grid), 6); + gtk_container_set_border_width (GTK_CONTAINER (grid), 12); + gtk_notebook_append_page (GTK_NOTEBOOK (dialog->notebook), grid, label); + gtk_widget_show (label); + gtk_widget_show (grid); + + row = 0; + + chooser = gtk_color_chooser_widget_new (); + dialog->color_chooser = chooser; + g_signal_connect_swapped (G_OBJECT (chooser), "notify::show-editor", + G_CALLBACK (thunar_properties_dialog_color_editor_changed), dialog); + g_signal_connect_swapped (G_OBJECT (dialog->notebook), "switch-page", + G_CALLBACK (thunar_properties_dialog_notebook_page_changed), dialog); + gtk_grid_attach (GTK_GRID (grid), chooser, 0, row, 1, 1); + gtk_widget_set_vexpand (chooser, TRUE); + gtk_widget_show (chooser); + + row++; + + /* check if gvfs metadata is supported */ + if (G_UNLIKELY (!thunar_g_vfs_metadata_is_supported ())) + { + frame = g_object_new (GTK_TYPE_FRAME, "border-width", 0, "shadow-type", GTK_SHADOW_NONE, NULL); + gtk_grid_attach (GTK_GRID (grid), frame, 0, row, 1, 1); + gtk_widget_set_sensitive (dialog->color_chooser, FALSE); + gtk_widget_show (frame); + + label = gtk_label_new (_("Missing dependencies")); + 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); + + infobar = gtk_info_bar_new (); + gtk_container_set_border_width (GTK_CONTAINER (infobar), 12); + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), _("It looks like <a href=\"https://wiki.gnome.org/Projects/gvfs\">gvfs</a> is not available.\n" + "This feature will not work. " + "<a href=\"https://docs.xfce.org/xfce/thunar/unix-filesystem#gnome_virtual_file_system\">[Read more]</a>")); + box = gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar)); + gtk_container_add (GTK_CONTAINER (box), label); + gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_WARNING); + gtk_widget_show (label); + gtk_widget_show (infobar); + gtk_container_add (GTK_CONTAINER (frame), infobar); + + row++; + } + else + { + dialog->example_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_widget_set_name (dialog->example_box, "example"); + gtk_widget_set_hexpand (dialog->example_box, TRUE); + gtk_widget_set_margin_top (dialog->example_box, 10); + gtk_widget_set_margin_bottom (dialog->example_box, 10); + gtk_grid_attach (GTK_GRID (grid), dialog->example_box, 0, row, 1, 1); + gtk_widget_show (dialog->example_box); + + image = gtk_image_new_from_icon_name ("text-x-generic", GTK_ICON_SIZE_DIALOG); + gtk_box_pack_start (GTK_BOX (dialog->example_box), image, FALSE, FALSE, 5); + gtk_widget_set_margin_top (image, 5); + gtk_widget_set_margin_bottom (image, 5); + gtk_widget_show (image); + + label = gtk_label_new_with_mnemonic (_("Example.txt")); + gtk_box_pack_start (GTK_BOX (dialog->example_box), label, TRUE, TRUE, 0); + gtk_label_set_xalign (GTK_LABEL (label), 0.0f); + gtk_widget_show (label); + + row++; + } + + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + dialog->highlight_buttons = box; + gtk_widget_set_hexpand (box, TRUE); + gtk_widget_set_vexpand (box, TRUE); + gtk_grid_attach (GTK_GRID (grid), box, 0, row, 1, 1); + if (G_UNLIKELY (!thunar_g_vfs_metadata_is_supported ())) + gtk_widget_set_sensitive (box, FALSE); + gtk_widget_show (box); + + button = gtk_button_new_with_mnemonic (_("_Apply")); + gtk_style_context_add_class (gtk_widget_get_style_context (button), "suggested-action"); + g_signal_connect_swapped (G_OBJECT (button), "clicked", + G_CALLBACK (thunar_properties_dialog_apply_highlight), dialog); + gtk_box_pack_end (GTK_BOX (box), button, FALSE, FALSE, 0); + gtk_widget_set_vexpand (button, FALSE); + gtk_widget_set_valign (button, GTK_ALIGN_END); + gtk_widget_show (button); + + dialog->highlight_apply_button = button; + gtk_widget_set_sensitive (dialog->highlight_apply_button, FALSE); + + button = gtk_button_new_with_mnemonic (_("_Reset")); + g_signal_connect_swapped (G_OBJECT (button), "clicked", + G_CALLBACK (thunar_properties_dialog_reset_highlight), dialog); + gtk_box_pack_end (GTK_BOX (box), button, FALSE, FALSE, 0); + gtk_widget_set_vexpand (button, FALSE); + gtk_widget_set_valign (button, GTK_ALIGN_END); + gtk_widget_show (button); + + button = gtk_button_new_with_mnemonic (_("Set _Foreground")); + g_signal_connect_swapped (G_OBJECT (button), "clicked", + G_CALLBACK (thunar_properties_dialog_set_foreground), dialog); + gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0); + gtk_widget_set_vexpand (button, FALSE); + gtk_widget_set_valign (button, GTK_ALIGN_END); + gtk_widget_show (button); + + button = gtk_button_new_with_mnemonic (_("Set _Background")); + g_signal_connect_swapped (G_OBJECT (button), "clicked", + G_CALLBACK (thunar_properties_dialog_set_background), dialog); + gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0); + gtk_widget_set_vexpand (button, FALSE); + gtk_widget_set_valign (button, GTK_ALIGN_END); + gtk_widget_show (button); + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + dialog->editor_button = box; + gtk_grid_attach (GTK_GRID (grid), box, 0, row, 1, 1); + + button = gtk_button_new_from_icon_name ("go-previous", GTK_ICON_SIZE_BUTTON); + g_signal_connect_swapped (G_OBJECT (button), "clicked", + G_CALLBACK (thunar_properties_dialog_color_editor_close), dialog); + gtk_box_pack_start (GTK_BOX (box), button, TRUE, TRUE, 0); + gtk_widget_show (button); } @@ -1017,6 +1182,8 @@ thunar_properties_dialog_update_single (ThunarPropertiesDialog *dialog) guint64 fs_free; guint64 fs_size; gdouble fs_fraction = 0.0; + const gchar *background; + const gchar *foreground; _thunar_return_if_fail (THUNAR_IS_PROPERTIES_DIALOG (dialog)); _thunar_return_if_fail (g_list_length (dialog->files) == 1); @@ -1257,6 +1424,13 @@ thunar_properties_dialog_update_single (ThunarPropertiesDialog *dialog) gtk_widget_hide (dialog->volume_label); } + if (G_LIKELY (thunar_g_vfs_metadata_is_supported ())) + { + background = thunar_file_get_metadata_setting (file, "highlight-color-background"); + foreground = thunar_file_get_metadata_setting (file, "highlight-color-foreground"); + thunar_properties_dialog_colorize_example_box (dialog, background, foreground); + } + /* cleanup */ g_object_unref (G_OBJECT (icon_factory)); g_free (date_custom_style); @@ -1621,3 +1795,202 @@ thunar_properties_dialog_set_file (ThunarPropertiesDialog *dialog, thunar_properties_dialog_set_files (dialog, &foo); } } + + + +static void +thunar_properties_dialog_reset_highlight (ThunarPropertiesDialog *dialog) +{ + GList *lp; + + _thunar_return_if_fail (THUNAR_IS_PROPERTIES_DIALOG (dialog)); + + for (lp = dialog->files; lp != NULL; lp = lp->next) + { + thunar_file_clear_metadata_setting (lp->data, "highlight-color-background"); + thunar_file_clear_metadata_setting (lp->data, "highlight-color-foreground"); + } + + /* clear previouly set colors */ + g_free (dialog->foreground_color); + dialog->foreground_color = NULL; + g_free (dialog->background_color); + dialog->background_color = NULL; + + thunar_properties_dialog_colorize_example_box (dialog, NULL, NULL); + + thunar_properties_dialog_reload (dialog); + + thunar_properties_dialog_update_apply_button (dialog); +} + + + +static void +thunar_properties_dialog_apply_highlight (ThunarPropertiesDialog *dialog) +{ + GList *lp; + gboolean highlighting_enabled; + + /* if this feature is disabled, then enable the feature */ + if (dialog->foreground_color != NULL || dialog->background_color != NULL) + { + g_object_get (G_OBJECT (dialog->preferences), "misc-highlighting-enabled", &highlighting_enabled, NULL); + if (!highlighting_enabled) + g_object_set (G_OBJECT (dialog->preferences), "misc-highlighting-enabled", TRUE, NULL); + } + + for (lp = dialog->files; lp != NULL; lp = lp->next) + { + if (dialog->foreground_color != NULL) + thunar_file_set_metadata_setting (lp->data, "highlight-color-foreground", dialog->foreground_color, FALSE); + if (dialog->background_color != NULL) + thunar_file_set_metadata_setting (lp->data, "highlight-color-background", dialog->background_color, FALSE); + } + + thunar_properties_dialog_reload (dialog); + + thunar_properties_dialog_update_apply_button (dialog); +} + + + +static void +thunar_properties_dialog_set_foreground (ThunarPropertiesDialog *dialog) +{ + GdkRGBA color; + gchar *color_str; + + _thunar_return_if_fail (THUNAR_IS_PROPERTIES_DIALOG (dialog)); + + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (dialog->color_chooser), &color); + color_str = gdk_rgba_to_string (&color); + thunar_properties_dialog_colorize_example_box (dialog, NULL, color_str); + g_free (dialog->foreground_color); + dialog->foreground_color = NULL; + dialog->foreground_color = color_str; + thunar_properties_dialog_update_apply_button (dialog); +} + + + +static void +thunar_properties_dialog_set_background (ThunarPropertiesDialog *dialog) +{ + GdkRGBA color; + gchar *color_str; + + _thunar_return_if_fail (THUNAR_IS_PROPERTIES_DIALOG (dialog)); + + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (dialog->color_chooser), &color); + color_str = gdk_rgba_to_string (&color); + thunar_properties_dialog_colorize_example_box (dialog, color_str, NULL); + g_free (dialog->background_color); + dialog->background_color = NULL; + dialog->background_color = color_str; + thunar_properties_dialog_update_apply_button (dialog); +} + + + +static void +thunar_properties_dialog_update_apply_button (ThunarPropertiesDialog *dialog) +{ + for (GList *lp = dialog->files; lp != NULL; lp = lp->next) + { + if (dialog->foreground_color != NULL && g_strcmp0 (dialog->foreground_color, thunar_file_get_metadata_setting (lp->data, "highlight-color-foreground")) != 0) + { + gtk_widget_set_sensitive (dialog->highlight_apply_button, TRUE); + return; + } + + if (dialog->background_color != NULL && g_strcmp0 (dialog->background_color, thunar_file_get_metadata_setting (lp->data, "highlight-color-background")) != 0) + { + gtk_widget_set_sensitive (dialog->highlight_apply_button, TRUE); + return; + } + } + + gtk_widget_set_sensitive (dialog->highlight_apply_button, FALSE); +} + + + +static void +thunar_properties_dialog_colorize_example_box (ThunarPropertiesDialog *dialog, + const gchar *background, + const gchar *foreground) +{ + GtkCssProvider *provider = gtk_css_provider_new (); + gchar *css_data = NULL; + + _thunar_return_if_fail (THUNAR_IS_PROPERTIES_DIALOG (dialog)); + + gtk_widget_reset_style (dialog->example_box); + + if (background != NULL && foreground != NULL) + css_data = g_strdup_printf ("#example { background-color: %s; color: %s; }", background, foreground); + else if (background != NULL) + css_data = g_strdup_printf ("#example { background-color: %s; }", background); + else if (foreground != NULL) + css_data = g_strdup_printf ("#example { color: %s; }", foreground); + else + css_data = g_strdup_printf ("#example { color: inherit; background-color: inherit; }"); + + if (css_data == NULL) + return; + + gtk_css_provider_load_from_data (provider, css_data, -1, NULL); + gtk_style_context_add_provider (gtk_widget_get_style_context (dialog->example_box), GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + + gtk_widget_show (dialog->example_box); + g_free (css_data); +} + + + +static void +thunar_properties_dialog_color_editor_changed (ThunarPropertiesDialog *dialog) +{ + gboolean show_editor; + + _thunar_return_if_fail (THUNAR_IS_PROPERTIES_DIALOG (dialog)); + + g_object_get (dialog->color_chooser, "show-editor", &show_editor, NULL); + + if (show_editor) + { + gtk_widget_show (dialog->editor_button); + gtk_widget_hide (dialog->example_box); + gtk_widget_hide (dialog->highlight_buttons); + } + else + { + gtk_widget_hide (dialog->editor_button); + gtk_widget_show (dialog->example_box); + gtk_widget_show (dialog->highlight_buttons); + } +} + + + +static void +thunar_properties_dialog_color_editor_close (ThunarPropertiesDialog *dialog) +{ + GdkRGBA color; + + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (dialog->color_chooser), &color); + gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (dialog->color_chooser), &color); + + g_object_set (G_OBJECT (dialog->color_chooser), "show-editor", FALSE, NULL); +} + + + +static void +thunar_properties_dialog_notebook_page_changed (ThunarPropertiesDialog *dialog) +{ + /* if the highlight tab is not active then the color editor should be switched off */ + if (gtk_notebook_get_current_page (GTK_NOTEBOOK (dialog->notebook)) != NOTEBOOK_PAGE_HIGHLIGHT) + g_object_set (G_OBJECT (dialog->color_chooser), "show-editor", FALSE, NULL); +} diff --git a/thunar/thunar-standard-view.c b/thunar/thunar-standard-view.c index b6cc4f628..5bb7b50fb 100644 --- a/thunar/thunar-standard-view.c +++ b/thunar/thunar-standard-view.c @@ -45,6 +45,7 @@ #include <thunar/thunar-gtk-extensions.h> #include <thunar/thunar-history.h> #include <thunar/thunar-icon-renderer.h> +#include <thunar/thunar-text-renderer.h> #include <thunar/thunar-marshal.h> #include <thunar/thunar-pango-extensions.h> #include <thunar/thunar-private.h> @@ -288,6 +289,12 @@ static void thunar_standard_view_set_sort_order GtkSortType order); static gboolean thunar_standard_view_toggle_sort_order (ThunarStandardView *standard_view); static void thunar_standard_view_store_sort_column (ThunarStandardView *standard_view); +static void thunar_standard_view_highlight_option_changed (ThunarStandardView *standard_view); +static void thunar_standard_view_cell_layout_data_func (GtkCellLayout *layout, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data); struct _ThunarStandardViewPrivate { @@ -547,6 +554,8 @@ thunar_standard_view_class_init (ThunarStandardViewClass *klass) gtkwidget_class->grab_focus = thunar_standard_view_grab_focus; gtkwidget_class->draw = thunar_standard_view_draw; + klass->cell_layout_data_func = thunar_standard_view_cell_layout_data_func; + xfce_gtk_translate_action_entries (thunar_standard_view_action_entries, G_N_ELEMENTS (thunar_standard_view_action_entries)); /** @@ -826,16 +835,22 @@ thunar_standard_view_init (ThunarStandardView *standard_view) g_object_ref_sink (G_OBJECT (standard_view->icon_renderer)); g_object_bind_property (G_OBJECT (standard_view), "zoom-level", G_OBJECT (standard_view->icon_renderer), "size", G_BINDING_SYNC_CREATE); g_object_bind_property (G_OBJECT (standard_view->icon_renderer), "size", G_OBJECT (standard_view->priv->thumbnailer), "thumbnail-size", G_BINDING_SYNC_CREATE); + g_object_bind_property (G_OBJECT (standard_view->preferences), "misc-highlighting-enabled", G_OBJECT (standard_view->icon_renderer), "highlighting-enabled", G_BINDING_SYNC_CREATE); /* setup the name renderer */ - standard_view->name_renderer = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, + standard_view->name_renderer = thunar_text_renderer_new (); + g_object_set (standard_view->name_renderer, #if PANGO_VERSION_CHECK (1, 44, 0) - "attributes", thunar_pango_attr_disable_hyphens (), + "attributes", thunar_pango_attr_disable_hyphens (), #endif - "alignment", PANGO_ALIGN_CENTER, - "xalign", 0.5, - NULL); + "alignment", PANGO_ALIGN_CENTER, + "xalign", 0.5, + NULL); g_object_ref_sink (G_OBJECT (standard_view->name_renderer)); + g_object_bind_property (G_OBJECT (standard_view->preferences), "misc-highlighting-enabled", G_OBJECT (standard_view->name_renderer), "highlighting-enabled", G_BINDING_SYNC_CREATE); + + /* this is required in order to disable foreground & background colors on the text renderers when the feature is disabled */ + g_object_bind_property (G_OBJECT (standard_view->preferences), "misc-highlighting-enabled", G_OBJECT (standard_view->name_renderer), "foreground-set", G_BINDING_SYNC_CREATE); /* TODO: prelit underline g_object_bind_property (G_OBJECT (standard_view->preferences), "misc-single-click", G_OBJECT (standard_view->name_renderer), "follow-prelit", G_BINDING_SYNC_CREATE);*/ @@ -1253,6 +1268,11 @@ thunar_standard_view_realize (GtkWidget *widget) /* apply the thumbnail frame preferences after icon_factory got initialized */ g_object_bind_property (G_OBJECT (standard_view->preferences), "misc-thumbnail-draw-frames", G_OBJECT (standard_view), "thumbnail-draw-frames", G_BINDING_SYNC_CREATE); + /* apply/unapply the highlights to name & icon renderers whenever the property changes */ + g_signal_connect_swapped (standard_view->preferences, "notify::misc-highlighting-enabled", + G_CALLBACK (thunar_standard_view_highlight_option_changed), standard_view); + thunar_standard_view_highlight_option_changed (standard_view); + /* store sort information to keep indicators in menu in sync */ thunar_standard_view_store_sort_column (standard_view); } @@ -1266,6 +1286,7 @@ thunar_standard_view_unrealize (GtkWidget *widget) /* drop the reference on the icon factory */ g_signal_handlers_disconnect_by_func (G_OBJECT (standard_view->icon_factory), gtk_widget_queue_draw, standard_view); + g_signal_handlers_disconnect_by_func (standard_view->preferences, thunar_standard_view_highlight_option_changed, standard_view); g_object_unref (G_OBJECT (standard_view->icon_factory)); standard_view->icon_factory = NULL; @@ -4383,3 +4404,77 @@ thunar_standard_view_get_action_entries (void) { return thunar_standard_view_action_entries; } + + + +static void +thunar_standard_view_highlight_option_changed (ThunarStandardView *standard_view) +{ + GtkWidget *view = gtk_bin_get_child (GTK_BIN (standard_view)); + GtkCellLayout *layout = NULL; + GtkCellLayoutDataFunc function = NULL; + gboolean show_highlight; + + if (GTK_IS_TREE_VIEW (view)) + layout = GTK_CELL_LAYOUT (gtk_tree_view_get_column (GTK_TREE_VIEW (view), THUNAR_COLUMN_NAME)); + else + layout = GTK_CELL_LAYOUT (view); + + g_object_get (G_OBJECT (THUNAR_STANDARD_VIEW (standard_view)->preferences), "misc-highlighting-enabled", &show_highlight, NULL); + + if (show_highlight) + function = (GtkCellLayoutDataFunc) THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->cell_layout_data_func; + + gtk_cell_layout_set_cell_data_func (layout, + standard_view->icon_renderer, + function, NULL, NULL); + gtk_cell_layout_set_cell_data_func (layout, + standard_view->name_renderer, + function, NULL, NULL); +} + + + +static void +thunar_standard_view_cell_layout_data_func (GtkCellLayout *layout, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + ThunarFile *file = THUNAR_FILE (thunar_list_model_get_file (THUNAR_LIST_MODEL (model), iter)); + const gchar *background = NULL; + const gchar *foreground = NULL; + + background = thunar_file_get_metadata_setting (file, "highlight-color-background"); + foreground = thunar_file_get_metadata_setting (file, "highlight-color-foreground"); + + /* since this function is being used for both icon & name renderers; + * we need to make sure the right properties are applied to the right renderers */ + if (THUNAR_IS_TEXT_RENDERER (cell)) + g_object_set (G_OBJECT (cell), + "foreground", foreground, + "highlight-color", background, + NULL); + + else if (THUNAR_IS_ICON_RENDERER (cell)) + g_object_set (G_OBJECT (cell), + "highlight-color", background, + NULL); + + else if (GTK_IS_CELL_RENDERER_TEXT (cell)) + g_object_set (G_OBJECT (cell), + "foreground", foreground, + "background", background, + NULL); + + else if (GTK_IS_CELL_RENDERER (cell)) + g_object_set (G_OBJECT (cell), + "cell-background", background, + NULL); + + else + g_warn_if_reached (); + + g_object_unref (file); +} diff --git a/thunar/thunar-standard-view.h b/thunar/thunar-standard-view.h index 58f3a6402..96ec470d8 100644 --- a/thunar/thunar-standard-view.h +++ b/thunar/thunar-standard-view.h @@ -136,6 +136,13 @@ struct _ThunarStandardViewClass /* Internal action signals */ gboolean (*delete_selected_files) (ThunarStandardView *standard_view); + /* Set the CellLayoutDataFunc to be used */ + void (*cell_layout_data_func) (GtkCellLayout *layout, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data); + /* The name of the property in ThunarPreferences, that determines * the last (and default) zoom-level for the view classes (i.e. in * case of ThunarIconView, this is "last-icon-view-zoom-level"). diff --git a/thunar/thunar-text-renderer.c b/thunar/thunar-text-renderer.c new file mode 100644 index 000000000..2ea1d3b82 --- /dev/null +++ b/thunar/thunar-text-renderer.c @@ -0,0 +1,276 @@ +/* vi:set et ai sw=2 sts=2 ts=2: */ +/*- + * Copyright (c) 2022 Amrit Borah <elessar1802@gmail.com> + * + * 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., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <thunar/thunar-text-renderer.h> +#include <thunar/thunar-util.h> + + + +enum +{ + PROP_0, + PROP_HIGHLIGHT_COLOR, + PROP_ROUNDED_CORNERS, + PROP_HIGHLIGHTING_ENABLED, +}; + + + +static void thunar_text_renderer_finalize (GObject *object); +static void thunar_text_renderer_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void thunar_text_renderer_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void thunar_text_renderer_render (GtkCellRenderer *renderer, + cairo_t *cr, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); + + + +struct _ThunarTextRendererClass +{ + GtkCellRendererTextClass __parent__; + + void (*default_render_function) (GtkCellRenderer *cell, + cairo_t *cr, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags); +}; + +struct _ThunarTextRenderer +{ + GtkCellRendererText __parent__; + + gchar *highlight_color; + gboolean rounded_corners; + gboolean highlighting_enabled; +}; + + + +G_DEFINE_TYPE (ThunarTextRenderer, thunar_text_renderer, GTK_TYPE_CELL_RENDERER_TEXT); + + + +static void +thunar_text_renderer_class_init (ThunarTextRendererClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); + + object_class->finalize = thunar_text_renderer_finalize; + + object_class->get_property = thunar_text_renderer_get_property; + object_class->set_property = thunar_text_renderer_set_property; + + klass->default_render_function = cell_class->render; + cell_class->render = thunar_text_renderer_render; + + /** + * ThunarTextRenderer:highlight-color: + * + * The color with which the cell should be highlighted. + **/ + g_object_class_install_property (object_class, + PROP_HIGHLIGHT_COLOR, + g_param_spec_string ("highlight-color", "highlight-color", "highlight-color", + NULL, + EXO_PARAM_READWRITE)); + + + + /** + * ThunarTextRenderer:rounded-corners: + * + * Determines if the cell should be clipped to rounded corners. + * Useful when highlighting is enabled & a highlight color is set. + **/ + g_object_class_install_property (object_class, + PROP_ROUNDED_CORNERS, + g_param_spec_boolean ("rounded-corners", "rounded-corners", "rounded-corners", + FALSE, + EXO_PARAM_READWRITE)); + + + + /** + * ThunarTextRenderer:highlighting-enabled: + * + * Determines if the cell background should be drawn with highlight color. + **/ + g_object_class_install_property (object_class, + PROP_HIGHLIGHTING_ENABLED, + g_param_spec_boolean ("highlighting-enabled", "highlighting-enabled", "highlighting-enabled", + FALSE, + EXO_PARAM_READWRITE)); +} + + + +static void +thunar_text_renderer_init (ThunarTextRenderer *text_renderer) +{ + text_renderer->highlight_color = NULL; +} + + + +static void +thunar_text_renderer_finalize (GObject *object) +{ + ThunarTextRenderer *text_renderer = THUNAR_TEXT_RENDERER (object); + + g_free (text_renderer->highlight_color); + + G_OBJECT_CLASS (thunar_text_renderer_parent_class)->finalize (object); +} + + + +static void +thunar_text_renderer_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ThunarTextRenderer *text_renderer = THUNAR_TEXT_RENDERER (object); + + switch (prop_id) + { + case PROP_HIGHLIGHT_COLOR: + g_value_set_string (value, text_renderer->highlight_color); + break; + + case PROP_ROUNDED_CORNERS: + g_value_set_boolean (value, text_renderer->rounded_corners); + break; + + case PROP_HIGHLIGHTING_ENABLED: + g_value_set_boolean (value, text_renderer->highlighting_enabled); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + + +static void +thunar_text_renderer_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ThunarTextRenderer *text_renderer = THUNAR_TEXT_RENDERER (object); + + switch (prop_id) + { + case PROP_HIGHLIGHT_COLOR: + g_free (text_renderer->highlight_color); + text_renderer->highlight_color = g_value_dup_string (value); + break; + + case PROP_ROUNDED_CORNERS: + text_renderer->rounded_corners = g_value_get_boolean (value); + break; + + case PROP_HIGHLIGHTING_ENABLED: + text_renderer->highlighting_enabled = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + + +/** + * thunar_text_renderer_new: + * + * Creates a new #ThunarTextRenderer. Adjust rendering + * parameters using object properties. Object properties can be + * set globally with #g_object_set. Also, with #GtkTreeViewColumn, + * you can bind a property to a value in a #GtkTreeModel. + * + * Return value: (transfer full) The newly allocated #ThunarTextRenderer. + **/ +GtkCellRenderer* +thunar_text_renderer_new (void) +{ + return g_object_new (THUNAR_TYPE_TEXT_RENDERER, NULL); +} + + + +static void +thunar_text_renderer_clear_background (cairo_t *cr, + GtkWidget *widget) +{ + GtkStyleContext *context; + GdkRGBA *color; + + context = gtk_widget_get_style_context (widget); + gtk_style_context_get (context, GTK_STATE_FLAG_NORMAL, GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &color, NULL); + gdk_cairo_set_source_rgba (cr, color); + + gdk_rgba_free (color); + cairo_paint (cr); +} + + + +static void +thunar_text_renderer_render (GtkCellRenderer *cell, + cairo_t *cr, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + if (THUNAR_TEXT_RENDERER (cell)->highlighting_enabled) + { + /* This should paint on top of the current surface. This should hide the highlight + that is drawn by the default render function of GtkCellRendererText */ + thunar_text_renderer_clear_background (cr, widget); + + thunar_util_clip_view_background (cell, cr, background_area, widget, flags); + } + + /* we only needed to manipulate the background_area, otherwise everything remains the same. + Hence, we are simply running the original render function now */ + THUNAR_TEXT_RENDERER_GET_CLASS (THUNAR_TEXT_RENDERER (cell)) + ->default_render_function (cell, cr, widget, background_area, cell_area, flags); +} diff --git a/thunar/thunar-text-renderer.h b/thunar/thunar-text-renderer.h new file mode 100644 index 000000000..4dbd625ea --- /dev/null +++ b/thunar/thunar-text-renderer.h @@ -0,0 +1,43 @@ +/* vi:set et ai sw=2 sts=2 ts=2: */ +/*- + * Copyright (c) 2022 Amrit Borah <elessar1802@gmail.com> + * + * 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., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __THUNAR_TEXT_RENDERER_H__ +#define __THUNAR_TEXT_RENDERER_H__ + +#include <thunar/thunar-enum-types.h> + +G_BEGIN_DECLS; + +typedef struct _ThunarTextRendererClass ThunarTextRendererClass; +typedef struct _ThunarTextRenderer ThunarTextRenderer; + +#define THUNAR_TYPE_TEXT_RENDERER (thunar_text_renderer_get_type ()) +#define THUNAR_TEXT_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_TEXT_RENDERER, ThunarTextRenderer)) +#define THUNAR_TEXT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_TEXT_RENDERER, ThunarTextRendererClass)) +#define THUNAR_IS_TEXT_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_TEXT_RENDERER)) +#define THUNAR_IS_TEXT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_TEXT_RENDERER)) +#define THUNAR_TEXT_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_TEXT_RENDERER, ThunarTextRendererClass)) + +GType thunar_text_renderer_get_type (void) G_GNUC_CONST; + +GtkCellRenderer *thunar_text_renderer_new (void) G_GNUC_MALLOC; + +G_END_DECLS; + +#endif /* !__THUNAR_TEXT_RENDERER_H__ */ diff --git a/thunar/thunar-util.c b/thunar/thunar-util.c index ad6d88dcf..142211e31 100644 --- a/thunar/thunar-util.c +++ b/thunar/thunar-util.c @@ -57,11 +57,30 @@ #include <thunar/thunar-private.h> #include <thunar/thunar-util.h> #include <thunar/thunar-folder.h> +#include <thunar/thunar-text-renderer.h> +#include <thunar/thunar-icon-renderer.h> #include <glib.h> #include <glib/gstdio.h> +#define BORDER_RADIUS 8 +enum +{ + THUNAR_CELL_TOP_RIGHT, + THUNAR_CELL_BOTTOM_RIGHT, + THUNAR_CELL_TOP_LEFT, + THUNAR_CELL_BOTTOM_LEFT, +}; + +enum +{ + DRAW_ON_LEFT, + DRAW_ON_RIGHT, + DRAW_ON_BOTTOM, + DRAW_ON_TOP, + DRAW_ON_ALL_SIDES, +} DrawOnSide; const char *SEARCH_PREFIX = "Search: "; @@ -813,3 +832,169 @@ thunar_util_strjoin_list (GList *string_list, else return joined_string; } + + + +static void +thunar_util_determine_corner_properties (GtkWidget *widget, + GtkCellRenderer *cell, + gint cell_height, + gint cell_width, + gdouble *radius, + gboolean *side) +{ + GtkTextDirection text_direction; + + /* only have rounded corners for icon view. */ + if (G_LIKELY (EXO_IS_ICON_VIEW (widget))) + { + if (exo_icon_view_get_orientation (EXO_ICON_VIEW (widget)) == GTK_ORIENTATION_HORIZONTAL) + { + /* Compact View */ + /* determine the radius proportional to either height or width (depens on the view) */ + *radius = cell_height * (BORDER_RADIUS / 100.0); + + text_direction = gtk_widget_get_direction (widget); + + /* decide which side to draw the rounded corners */ + if (THUNAR_IS_TEXT_RENDERER (cell)) + *side = text_direction == GTK_TEXT_DIR_LTR ? DRAW_ON_RIGHT : DRAW_ON_LEFT; + else + *side = text_direction == GTK_TEXT_DIR_LTR ? DRAW_ON_LEFT : DRAW_ON_RIGHT; + } + else + { + /* Icon View */ + *radius = cell_width * (BORDER_RADIUS / 100.0); + + /* text direction has no effect on this view */ + /* decide which side to draw the rounded corners */ + if (THUNAR_IS_TEXT_RENDERER (cell)) + *side = DRAW_ON_BOTTOM; + else + *side = DRAW_ON_TOP; + } + } +} + + + +static void +thunar_util_draw_rounded_corners (cairo_t *cr, + const GdkRectangle *background_area, + gdouble radius, + gint side) +{ + gdouble *corner_radius; + gdouble draw_round_corners_on_left[4] = { 0, 0, radius, radius }; + gdouble draw_round_corners_on_right[4] = { radius, radius, 0, 0 }; + gdouble draw_round_corners_on_top[4] = { radius, 0, 0, radius }; + gdouble draw_round_corners_on_bottom[4] = { 0, radius, radius, 0 }; + gdouble draw_round_corners_on_all_sides[4] = { radius, radius, radius, radius }; + gdouble degrees = G_PI / 180.0; + + switch (side) + { + case DRAW_ON_LEFT: + corner_radius = draw_round_corners_on_left; + break; + case DRAW_ON_RIGHT: + corner_radius = draw_round_corners_on_right; + break; + case DRAW_ON_BOTTOM: + corner_radius = draw_round_corners_on_bottom; + break; + case DRAW_ON_TOP: + corner_radius = draw_round_corners_on_top; + break; + case DRAW_ON_ALL_SIDES: + corner_radius = draw_round_corners_on_all_sides; + break; + default: + corner_radius = draw_round_corners_on_all_sides; + g_warn_if_reached (); + } + + cairo_new_sub_path (cr); + cairo_arc (cr, + background_area->x + background_area->width - corner_radius[THUNAR_CELL_TOP_RIGHT], /* cairo x coord */ + background_area->y + corner_radius[THUNAR_CELL_TOP_RIGHT], /* cairo y coord */ + corner_radius[THUNAR_CELL_TOP_RIGHT], -90 * degrees, 0 * degrees); /* radius, angle1, angle2 resp. */ + cairo_arc (cr, + background_area->x + background_area->width - corner_radius[THUNAR_CELL_BOTTOM_RIGHT], + background_area->y + background_area->height - corner_radius[THUNAR_CELL_BOTTOM_RIGHT], + corner_radius[THUNAR_CELL_BOTTOM_RIGHT], 0 * degrees, 90 * degrees); + cairo_arc (cr, + background_area->x + corner_radius[THUNAR_CELL_TOP_LEFT], + background_area->y + background_area->height - corner_radius[THUNAR_CELL_TOP_LEFT], + corner_radius[THUNAR_CELL_TOP_LEFT], 90 * degrees, 180 * degrees); + cairo_arc (cr, + background_area->x + corner_radius[THUNAR_CELL_BOTTOM_LEFT], + background_area->y + corner_radius[THUNAR_CELL_BOTTOM_LEFT], + corner_radius[THUNAR_CELL_BOTTOM_LEFT], 180 * degrees, 270 * degrees); + cairo_close_path (cr); + cairo_clip (cr); +} + + + +void +thunar_util_clip_view_background (GtkCellRenderer *cell, + cairo_t *cr, + const GdkRectangle *background_area, + GtkWidget *widget, + GtkCellRendererState flags) +{ + GtkStyleContext *context; + GdkRGBA *color = NULL; + GdkRGBA highlight_color_rgba; + gboolean color_selected = (flags & GTK_CELL_RENDERER_SELECTED) != 0; + gboolean rounded_corners; + gchar *highlight_color; + gdouble radius = 0.0; + gint side = DRAW_ON_ALL_SIDES; + + g_object_get (G_OBJECT (cell), + "highlight-color", &highlight_color, + "rounded-corners", &rounded_corners, + NULL); + + cairo_save (cr); + + if (G_LIKELY (rounded_corners)) + { + /* determine radius & the side to draw the rounded corners */ + thunar_util_determine_corner_properties (widget, cell, + background_area->height, background_area->width, + &radius, &side); + + thunar_util_draw_rounded_corners (cr, background_area, radius, side); + } + + if (G_UNLIKELY (highlight_color != NULL)) + { + gdk_rgba_parse (&highlight_color_rgba, highlight_color); + color = gdk_rgba_copy (&highlight_color_rgba); + } + + /** + * If the item is selected then paint the background area with the theme's selected item's color. + * To distinguish between highlighted & non highlighted files, the background area of icon renderer + * is left untouched if it already has a highlight color + **/ + if (G_UNLIKELY (color_selected && !(THUNAR_IS_ICON_RENDERER (cell) && highlight_color != NULL))) + { + context = gtk_widget_get_style_context (widget); + gtk_style_context_get (context, GTK_STATE_FLAG_SELECTED, GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &color, NULL); + } + + if (G_LIKELY (color != NULL)) + { + gdk_cairo_set_source_rgba (cr, color); + gdk_rgba_free (color); + cairo_paint (cr); + } + + g_free (highlight_color); + cairo_restore (cr); +} diff --git a/thunar/thunar-util.h b/thunar/thunar-util.h index 62325ce63..a92c6f97c 100644 --- a/thunar/thunar-util.h +++ b/thunar/thunar-util.h @@ -80,6 +80,11 @@ gchar* thunar_util_next_new_file_name (ThunarFile *dir, gboolean thunar_util_is_a_search_query (const gchar *string); gchar* thunar_util_strjoin_list (GList *string_list, const gchar *separator); +void thunar_util_clip_view_background (GtkCellRenderer *cell, + cairo_t *cr, + const GdkRectangle *background_area, + GtkWidget *widget, + GtkCellRendererState flags); extern const char *SEARCH_PREFIX; diff --git a/thunar/thunar-window.c b/thunar/thunar-window.c index 037e02246..7150de96b 100644 --- a/thunar/thunar-window.c +++ b/thunar/thunar-window.c @@ -237,6 +237,7 @@ static gboolean thunar_window_action_open_location (ThunarWindow static gboolean thunar_window_action_contents (ThunarWindow *window); static gboolean thunar_window_action_about (ThunarWindow *window); static gboolean thunar_window_action_show_hidden (ThunarWindow *window); +static gboolean thunar_window_action_show_highlight (ThunarWindow *window); static gboolean thunar_window_propagate_key_event (GtkWindow *window, GdkEvent *key_event, gpointer user_data); @@ -474,6 +475,7 @@ static XfceGtkActionEntry thunar_window_action_entries[] = { THUNAR_WINDOW_ACTION_CONFIGURE_TOOLBAR, "<Actions>/ThunarWindow/view-configure-toolbar", "", XFCE_GTK_MENU_ITEM , N_ ("Configure _Toolbar..."), N_ ("Configure the toolbar"), NULL, G_CALLBACK (thunar_window_action_show_toolbar_editor),}, { THUNAR_WINDOW_ACTION_CLEAR_DIRECTORY_SPECIFIC_SETTINGS,"<Actions>/ThunarWindow/clear-directory-specific-settings","", XFCE_GTK_IMAGE_MENU_ITEM, N_ ("Clear Saved _Folder View Settings"), N_ ("Delete saved view settings for this folder"), NULL, G_CALLBACK (thunar_window_action_clear_directory_specific_settings), }, { THUNAR_WINDOW_ACTION_SHOW_HIDDEN, "<Actions>/ThunarWindow/show-hidden", "<Primary>h", XFCE_GTK_CHECK_MENU_ITEM, N_ ("Show _Hidden Files"), N_ ("Toggles the display of hidden files in the current window"), NULL, G_CALLBACK (thunar_window_action_show_hidden), }, + { THUNAR_WINDOW_ACTION_SHOW_HIGHLIGHT, "<Actions>/ThunarWindow/show-highlight", "", XFCE_GTK_CHECK_MENU_ITEM, N_ ("Show File Hi_ghlight"), N_ ("Toggles the display of file highlight which can be configured in the file specific property dialog"), NULL,G_CALLBACK (thunar_window_action_show_highlight), }, { THUNAR_WINDOW_ACTION_ZOOM_IN, "<Actions>/ThunarWindow/zoom-in", "<Primary>KP_Add", XFCE_GTK_IMAGE_MENU_ITEM, N_ ("Zoom I_n"), N_ ("Show the contents in more detail"), "zoom-in-symbolic", G_CALLBACK (thunar_window_zoom_in), }, { THUNAR_WINDOW_ACTION_ZOOM_IN_ALT_1, "<Actions>/ThunarWindow/zoom-in-alt1", "<Primary>plus", XFCE_GTK_IMAGE_MENU_ITEM, NULL, NULL, NULL, G_CALLBACK (thunar_window_zoom_in), }, { THUNAR_WINDOW_ACTION_ZOOM_IN_ALT_2, "<Actions>/ThunarWindow/zoom-in-alt2", "<Primary>equal", XFCE_GTK_IMAGE_MENU_ITEM, NULL, NULL, NULL, G_CALLBACK (thunar_window_zoom_in), }, @@ -1240,6 +1242,7 @@ thunar_window_update_view_menu (ThunarWindow *window, GtkWidget *item; GtkWidget *sub_items; gchar *last_location_bar; + gboolean highlight_enabled; _thunar_return_if_fail (THUNAR_IS_WINDOW (window)); @@ -1281,6 +1284,13 @@ thunar_window_update_view_menu (ThunarWindow *window, xfce_gtk_toggle_menu_item_new_from_action_entry (get_action_entry (THUNAR_WINDOW_ACTION_SHOW_HIDDEN), G_OBJECT (window), window->show_hidden, GTK_MENU_SHELL (menu)); xfce_gtk_menu_append_separator (GTK_MENU_SHELL (menu)); + if (thunar_g_vfs_metadata_is_supported ()) + { + g_object_get (G_OBJECT (window->preferences), "misc-highlighting-enabled", &highlight_enabled, NULL); + xfce_gtk_toggle_menu_item_new_from_action_entry (get_action_entry (THUNAR_WINDOW_ACTION_SHOW_HIGHLIGHT), G_OBJECT (window), + highlight_enabled, GTK_MENU_SHELL (menu)); + xfce_gtk_menu_append_separator (GTK_MENU_SHELL (menu)); + } if (window->view != NULL) thunar_standard_view_append_menu_items (THUNAR_STANDARD_VIEW (window->view), GTK_MENU (menu), window->accel_group); xfce_gtk_menu_append_separator (GTK_MENU_SHELL (menu)); @@ -4411,6 +4421,24 @@ thunar_window_action_show_hidden (ThunarWindow *window) +static gboolean +thunar_window_action_show_highlight (ThunarWindow *window) +{ + gboolean highlight_enabled; + + _thunar_return_val_if_fail (THUNAR_IS_WINDOW (window), FALSE); + + g_object_get (G_OBJECT (window->preferences), "misc-highlighting-enabled", &highlight_enabled, NULL); + g_object_set (G_OBJECT (window->preferences), "misc-highlighting-enabled", !highlight_enabled, NULL); + + /* refresh the view to refresh the cell renderer drawings */ + thunar_window_action_reload (window, NULL); + + return TRUE; +} + + + gboolean thunar_window_action_search (ThunarWindow *window) { diff --git a/thunar/thunar-window.h b/thunar/thunar-window.h index 462f904f8..8cbeab689 100644 --- a/thunar/thunar-window.h +++ b/thunar/thunar-window.h @@ -99,6 +99,7 @@ typedef enum THUNAR_WINDOW_ACTION_SWITCH_NEXT_TAB, THUNAR_WINDOW_ACTION_SEARCH, THUNAR_WINDOW_ACTION_CANCEL_SEARCH, + THUNAR_WINDOW_ACTION_SHOW_HIGHLIGHT, THUNAR_WINDOW_N_ACTIONS } ThunarWindowAction; diff --git a/thunarx/thunarx-file-info.h b/thunarx/thunarx-file-info.h index 9668afcde..5e6f8b136 100644 --- a/thunarx/thunarx-file-info.h +++ b/thunarx/thunarx-file-info.h @@ -51,7 +51,8 @@ G_BEGIN_DECLS "metadata::emblems," \ "metadata::thunar-view-type," \ "metadata::thunar-sort-column,metadata::thunar-sort-order," \ - "metadata::thunar-zoom-level" + "metadata::thunar-zoom-level," \ + "metadata::thunar-highlight-color-background,metadata::thunar-highlight-color-foreground" \ /* -- GitLab