From 3f906d61ac05801e411d7bc2ba3c840a81fd5994 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Tue, 26 Jun 2018 14:32:33 +0200 Subject: [PATCH] I#37 - Color support for iCalendar entries Closes https://gitlab.gnome.org/GNOME/evolution/issues/37 --- CMakeLists.txt | 10 ++ config.h.in | 3 + src/calendar/gui/e-cal-model.c | 18 +++ src/calendar/gui/e-comp-editor-page-general.c | 101 ++++++++++++-- src/calendar/gui/e-comp-editor-property-parts.c | 170 ++++++++++++++++++++++++ src/calendar/gui/e-comp-editor-property-parts.h | 2 + src/calendar/gui/e-to-do-pane.c | 21 ++- src/e-util/e-color-combo.c | 7 +- 8 files changed, 316 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a148c77717..86f8692a43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -614,6 +614,16 @@ CHECK_C_SOURCE_COMPILES("#include return 0; }" HAVE_ICAL_FILENAME_PARAMETER) +CHECK_C_SOURCE_COMPILES("#include + int main(void) { + icalproperty *prop; + prop = icalcomponent_get_first_property (NULL, ICAL_COLOR_PROPERTY); + icalproperty_get_color (prop); + icalproperty_set_color (prop, \"white\"); + prop = icalproperty_new_color (NULL); + return 0; + }" HAVE_ICAL_COLOR_PROPERTY) + unset(CMAKE_REQUIRED_DEFINITIONS) unset(CMAKE_REQUIRED_INCLUDES) unset(CMAKE_REQUIRED_LIBRARIES) diff --git a/config.h.in b/config.h.in index ce437c468e..0b94db4825 100644 --- a/config.h.in +++ b/config.h.in @@ -108,6 +108,9 @@ /* libical provides ICAL_FILENAME_PARAMETER */ #cmakedefine HAVE_ICAL_FILENAME_PARAMETER 1 +/* libical provides ICAL_COLOR_PROPERTY */ +#cmakedefine HAVE_ICAL_COLOR_PROPERTY 1 + /* When defined spell checking is enabled */ #cmakedefine HAVE_GTKSPELL 1 diff --git a/src/calendar/gui/e-cal-model.c b/src/calendar/gui/e-cal-model.c index ffb55c05d6..7706d2255d 100644 --- a/src/calendar/gui/e-cal-model.c +++ b/src/calendar/gui/e-cal-model.c @@ -1234,6 +1234,9 @@ cal_model_get_color_for_component (ECalModel *model, const gchar *extension_name; const gchar *uid; gint i, first_empty = 0; + #ifdef HAVE_ICAL_COLOR_PROPERTY + icalproperty *prop; + #endif static AssignedColorData assigned_colors[] = { { "#BECEDD", NULL }, /* 190 206 221 Blue */ @@ -1250,6 +1253,21 @@ cal_model_get_color_for_component (ECalModel *model, g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL); + #ifdef HAVE_ICAL_COLOR_PROPERTY + prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_COLOR_PROPERTY); + if (prop) { + GdkRGBA rgba; + + color_spec = icalproperty_get_color (prop); + if (color_spec && gdk_rgba_parse (&rgba, color_spec)) { + g_free (comp_data->color); + comp_data->color = g_strdup (color_spec); + + return comp_data->color; + } + } + #endif + switch (e_cal_client_get_source_type (comp_data->client)) { case E_CAL_CLIENT_SOURCE_TYPE_EVENTS: extension_name = E_SOURCE_EXTENSION_CALENDAR; diff --git a/src/calendar/gui/e-comp-editor-page-general.c b/src/calendar/gui/e-comp-editor-page-general.c index bfba1d9418..9a1e05d8b1 100644 --- a/src/calendar/gui/e-comp-editor-page-general.c +++ b/src/calendar/gui/e-comp-editor-page-general.c @@ -28,6 +28,7 @@ #include "comp-util.h" #include "e-comp-editor.h" #include "e-comp-editor-page.h" +#include "e-comp-editor-property-parts.h" #include "e-meeting-list-view.h" #include "itip-utils.h" @@ -46,6 +47,9 @@ struct _ECompEditorPageGeneralPrivate { GtkWidget *attendees_button_add; GtkWidget *attendees_button_edit; GtkWidget *attendees_button_remove; + ECompEditorPropertyPart *comp_color; + gulong comp_color_changed_handler_id; + GtkWidget *source_and_color_hbox; /* has together source_combo_box and comp_color::edit_widget */ gint data_column_width; gchar *source_label_text; @@ -497,6 +501,17 @@ ecep_general_target_client_notify_cb (ECompEditor *comp_editor, cal_email_address = e_comp_editor_get_cal_email_address (comp_editor); ecep_general_pick_organizer_for_email_address (page_general, cal_email_address); + + if (page_general->priv->comp_color) { + ECalClient *target_client; + gboolean supports_color = FALSE; + + target_client = e_comp_editor_get_target_client (comp_editor); + if (target_client) + supports_color = e_client_check_capability (E_CLIENT (target_client), CAL_STATIC_CAPABILITY_COMPONENT_COLOR); + + e_comp_editor_property_part_set_visible (page_general->priv->comp_color, supports_color); + } } static gboolean @@ -749,6 +764,19 @@ ecep_general_sensitize_widgets (ECompEditorPage *page, action = e_comp_editor_get_action (comp_editor, "option-attendees"); gtk_action_set_sensitive (action, !force_insensitive && !read_only && organizer_is_user); + if (page_general->priv->comp_color && + !e_comp_editor_property_part_get_sensitize_handled (page_general->priv->comp_color)) { + GtkWidget *widget; + + widget = e_comp_editor_property_part_get_label_widget (page_general->priv->comp_color); + if (widget) + gtk_widget_set_sensitive (widget, !force_insensitive && !read_only); + + widget = e_comp_editor_property_part_get_edit_widget (page_general->priv->comp_color); + if (widget) + gtk_widget_set_sensitive (widget, !force_insensitive && !read_only); + } + g_clear_object (&comp_editor); } @@ -766,6 +794,9 @@ ecep_general_fill_widgets (ECompEditorPage *page, page_general = E_COMP_EDITOR_PAGE_GENERAL (page); + if (page_general->priv->comp_color) + e_comp_editor_property_part_fill_widget (page_general->priv->comp_color, component); + g_slist_free_full (page_general->priv->orig_attendees, g_free); page_general->priv->orig_attendees = NULL; @@ -926,6 +957,9 @@ ecep_general_fill_component (ECompEditorPage *page, page_general = E_COMP_EDITOR_PAGE_GENERAL (page); + if (page_general->priv->comp_color) + e_comp_editor_property_part_fill_component (page_general->priv->comp_color, component); + cal_comp_util_remove_all_properties (component, ICAL_ATTENDEE_PROPERTY); if (e_comp_editor_page_general_get_show_attendees (page_general)) { @@ -1192,6 +1226,7 @@ ecep_general_constructed (GObject *object) { ECompEditor *comp_editor; ECompEditorPageGeneral *page_general; + ECompEditorPropertyPart *part; GtkWidget *widget, *scrolled_window; GtkTreeSelection *selection; GtkGrid *grid; @@ -1266,6 +1301,18 @@ ecep_general_constructed (GObject *object) page_general->priv->source_label = widget; + widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4); + g_object_set (G_OBJECT (widget), + "hexpand", TRUE, + "halign", GTK_ALIGN_FILL, + "vexpand", FALSE, + "valign", GTK_ALIGN_CENTER, + NULL); + gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox), widget, TRUE, TRUE, 0); + gtk_widget_show (widget); + + page_general->priv->source_and_color_hbox = widget; + shell = e_comp_editor_get_shell (comp_editor); widget = e_source_combo_box_new ( e_shell_get_registry (shell), @@ -1277,7 +1324,7 @@ ecep_general_constructed (GObject *object) "vexpand", FALSE, "valign", GTK_ALIGN_START, NULL); - gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox), widget, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (page_general->priv->source_and_color_hbox), widget, TRUE, TRUE, 0); gtk_widget_show (widget); page_general->priv->source_combo_box = widget; @@ -1287,6 +1334,33 @@ ecep_general_constructed (GObject *object) g_signal_connect (page_general->priv->source_combo_box, "changed", G_CALLBACK (ecep_general_source_combo_box_changed_cb), page_general); + /* Returns NULL when not supported by libical */ + part = e_comp_editor_property_part_color_new (); + if (part) { + widget = e_comp_editor_property_part_get_edit_widget (part); + + if (widget) { + const gchar *tooltip; + + gtk_box_pack_start (GTK_BOX (page_general->priv->source_and_color_hbox), widget, FALSE, FALSE, 0); + + if (g_strcmp0 (page_general->priv->source_extension_name, E_SOURCE_EXTENSION_CALENDAR) == 0) { + tooltip = _("Override color of the event. If not set, then color of the calendar is used."); + } else if (g_strcmp0 (page_general->priv->source_extension_name, E_SOURCE_EXTENSION_MEMO_LIST) == 0) { + tooltip = _("Override color of the memo. If not set, then color of the memo list is used."); + } else { /* E_SOURCE_EXTENSION_TASK_LIST */ + tooltip = _("Override color of the task. If not set, then color of the task list is used."); + } + + gtk_widget_set_tooltip_text (widget, tooltip); + } + + page_general->priv->comp_color_changed_handler_id = g_signal_connect_swapped (part, "changed", + G_CALLBACK (e_comp_editor_page_emit_changed), page_general); + + page_general->priv->comp_color = part; + } + widget = gtk_button_new_with_mnemonic (C_("ECompEditor", "Atte_ndees...")); g_object_set (G_OBJECT (widget), "hexpand", FALSE, @@ -1403,6 +1477,12 @@ ecep_general_finalize (GObject *object) g_free (page_general->priv->user_delegator); page_general->priv->user_delegator = NULL; + if (page_general->priv->comp_color && page_general->priv->comp_color_changed_handler_id) { + g_signal_handler_disconnect (page_general->priv->comp_color, page_general->priv->comp_color_changed_handler_id); + page_general->priv->comp_color_changed_handler_id = 0; + } + + g_clear_object (&page_general->priv->comp_color); g_clear_object (&page_general->priv->select_source); g_clear_object (&page_general->priv->meeting_store); @@ -1694,19 +1774,20 @@ e_comp_editor_page_general_update_view (ECompEditorPageGeneral *page_general) if (page_general->priv->show_attendees) { if (gtk_widget_get_parent (page_general->priv->source_label) == GTK_WIDGET (grid)) { + g_object_ref (page_general->priv->source_label); - g_object_ref (page_general->priv->source_combo_box); + g_object_ref (page_general->priv->source_and_color_hbox); gtk_container_remove (grid, page_general->priv->source_label); - gtk_container_remove (grid, page_general->priv->source_combo_box); + gtk_container_remove (grid, page_general->priv->source_and_color_hbox); gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox), page_general->priv->source_label, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox), - page_general->priv->source_combo_box, TRUE, TRUE, 0); + page_general->priv->source_and_color_hbox, TRUE, TRUE, 0); g_object_unref (page_general->priv->source_label); - g_object_unref (page_general->priv->source_combo_box); + g_object_unref (page_general->priv->source_and_color_hbox); } gtk_container_child_set (grid, page_general->priv->organizer_label, @@ -1727,21 +1808,21 @@ e_comp_editor_page_general_update_view (ECompEditorPageGeneral *page_general) ggrid = GTK_GRID (grid); g_object_ref (page_general->priv->source_label); - g_object_ref (page_general->priv->source_combo_box); + g_object_ref (page_general->priv->source_and_color_hbox); gtk_container_remove (container, page_general->priv->source_label); - gtk_container_remove (container, page_general->priv->source_combo_box); + gtk_container_remove (container, page_general->priv->source_and_color_hbox); gtk_grid_attach (ggrid, page_general->priv->source_label, 0, 0, 1, 1); - gtk_grid_attach (ggrid, page_general->priv->source_combo_box, 1, 0, 1, 1); + gtk_grid_attach (ggrid, page_general->priv->source_and_color_hbox, 1, 0, 1, 1); g_object_unref (page_general->priv->source_label); - g_object_unref (page_general->priv->source_combo_box); + g_object_unref (page_general->priv->source_and_color_hbox); } gtk_container_child_set (grid, page_general->priv->source_label, "left-attach", 0, NULL); - gtk_container_child_set (grid, page_general->priv->source_combo_box, + gtk_container_child_set (grid, page_general->priv->source_and_color_hbox, "left-attach", 1, "width", page_general->priv->data_column_width, NULL); gtk_widget_hide (page_general->priv->organizer_label); diff --git a/src/calendar/gui/e-comp-editor-property-parts.c b/src/calendar/gui/e-comp-editor-property-parts.c index 3b2cc7100f..b279a0ba2d 100644 --- a/src/calendar/gui/e-comp-editor-property-parts.c +++ b/src/calendar/gui/e-comp-editor-property-parts.c @@ -1595,3 +1595,173 @@ e_comp_editor_property_part_transparency_new (void) } /* ************************************************************************* */ + +#define E_TYPE_COMP_EDITOR_PROPERTY_PART_COLOR \ + (e_comp_editor_property_part_color_get_type ()) +#define E_COMP_EDITOR_PROPERTY_PART_COLOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_COLOR, ECompEditorPropertyPartColor)) +#define E_IS_COMP_EDITOR_PROPERTY_PART_COLOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_COLOR)) + +typedef struct _ECompEditorPropertyPartColor ECompEditorPropertyPartColor; +typedef struct _ECompEditorPropertyPartColorClass ECompEditorPropertyPartColorClass; + +struct _ECompEditorPropertyPartColor { + ECompEditorPropertyPart parent; +}; + +struct _ECompEditorPropertyPartColorClass { + ECompEditorPropertyPartClass parent_class; +}; + +GType e_comp_editor_property_part_color_get_type (void) G_GNUC_CONST; + +G_DEFINE_TYPE (ECompEditorPropertyPartColor, e_comp_editor_property_part_color, E_TYPE_COMP_EDITOR_PROPERTY_PART) + +static void +ecepp_color_create_widgets (ECompEditorPropertyPart *property_part, + GtkWidget **out_label_widget, + GtkWidget **out_edit_widget) +{ + GdkRGBA rgba; + + g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_COLOR (property_part)); + g_return_if_fail (out_label_widget != NULL); + g_return_if_fail (out_edit_widget != NULL); + + rgba.red = 0.0; + rgba.green = 0.0; + rgba.blue = 0.0; + rgba.alpha = 0.001; + + *out_label_widget = NULL; + + /* Translators: This 'None' is meant for 'Color' in calendar component editor, like 'None color' */ + *out_edit_widget = e_color_combo_new_defaults (&rgba, C_("ECompEditor", "None")); + + g_object_set (G_OBJECT (*out_edit_widget), + "hexpand", FALSE, + "halign", GTK_ALIGN_START, + "vexpand", FALSE, + "valign", GTK_ALIGN_CENTER, + NULL); + + gtk_widget_show (*out_edit_widget); + + g_signal_connect_swapped (*out_edit_widget, "activated", + G_CALLBACK (e_comp_editor_property_part_emit_changed), property_part); +} + +static void +ecepp_color_fill_widget (ECompEditorPropertyPart *property_part, + icalcomponent *component) +{ + #ifdef HAVE_ICAL_COLOR_PROPERTY + GtkWidget *edit_widget; + icalproperty *prop; + gboolean color_set = FALSE; + + g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_COLOR (property_part)); + + edit_widget = e_comp_editor_property_part_get_edit_widget (property_part); + g_return_if_fail (E_IS_COLOR_COMBO (edit_widget)); + + prop = icalcomponent_get_first_property (component, ICAL_COLOR_PROPERTY); + if (prop) { + const gchar *color = icalproperty_get_color (prop); + GdkRGBA rgba; + + if (color && gdk_rgba_parse (&rgba, color)) { + e_color_combo_set_current_color (E_COLOR_COMBO (edit_widget), &rgba); + color_set = TRUE; + } + } + + if (!color_set) { + GdkRGBA rgba; + + rgba.red = 0.0; + rgba.green = 0.0; + rgba.blue = 0.0; + rgba.alpha = 0.001; + + e_color_combo_set_current_color (E_COLOR_COMBO (edit_widget), &rgba); + } + #endif +} + +static void +ecepp_color_fill_component (ECompEditorPropertyPart *property_part, + icalcomponent *component) +{ + #ifdef HAVE_ICAL_COLOR_PROPERTY + GtkWidget *edit_widget; + icalproperty *prop; + GdkRGBA rgba; + + g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_COLOR (property_part)); + + edit_widget = e_comp_editor_property_part_get_edit_widget (property_part); + g_return_if_fail (E_IS_COLOR_COMBO (edit_widget)); + + rgba.red = 0.0; + rgba.green = 0.0; + rgba.blue = 0.0; + rgba.alpha = 0.001; + + e_color_combo_get_current_color (E_COLOR_COMBO (edit_widget), &rgba); + + prop = icalcomponent_get_first_property (component, ICAL_COLOR_PROPERTY); + + if (rgba.alpha <= 1.0 - 1e-9) { + if (prop) { + icalcomponent_remove_property (component, prop); + icalproperty_free (prop); + } + } else { + gchar *str; + + str = gdk_rgba_to_string (&rgba); + if (str) { + if (prop) { + icalproperty_set_color (prop, str); + } else { + prop = icalproperty_new_color (str); + icalcomponent_add_property (component, prop); + } + + g_free (str); + } else { + g_warning ("%s: Failed to convert RGBA (%f,%f,%f,%f) to string", G_STRFUNC, rgba.red, rgba.green, rgba.blue, rgba.alpha); + } + } + #endif +} + +static void +e_comp_editor_property_part_color_init (ECompEditorPropertyPartColor *part_color) +{ +} + +static void +e_comp_editor_property_part_color_class_init (ECompEditorPropertyPartColorClass *klass) +{ + ECompEditorPropertyPartClass *part_class; + + part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass); + part_class->create_widgets = ecepp_color_create_widgets; + part_class->fill_widget = ecepp_color_fill_widget; + part_class->fill_component = ecepp_color_fill_component; +} + +ECompEditorPropertyPart * +e_comp_editor_property_part_color_new (void) +{ + #ifdef HAVE_ICAL_COLOR_PROPERTY + return g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_COLOR, NULL); + #else + return NULL; + #endif +} diff --git a/src/calendar/gui/e-comp-editor-property-parts.h b/src/calendar/gui/e-comp-editor-property-parts.h index 68a1bbed45..dbc03a637e 100644 --- a/src/calendar/gui/e-comp-editor-property-parts.h +++ b/src/calendar/gui/e-comp-editor-property-parts.h @@ -60,6 +60,8 @@ ECompEditorPropertyPart * e_comp_editor_property_part_timezone_new (void); ECompEditorPropertyPart * e_comp_editor_property_part_transparency_new (void); +ECompEditorPropertyPart * + e_comp_editor_property_part_color_new (void); G_END_DECLS diff --git a/src/calendar/gui/e-to-do-pane.c b/src/calendar/gui/e-to-do-pane.c index ac6c3ebc9e..9047fd2cd7 100644 --- a/src/calendar/gui/e-to-do-pane.c +++ b/src/calendar/gui/e-to-do-pane.c @@ -774,7 +774,11 @@ etdp_get_comp_colors (EToDoPane *to_do_pane, gboolean *out_fgcolor_set, time_t *out_nearest_due) { - GdkRGBA *bgcolor, fgcolor; + GdkRGBA *bgcolor = NULL, fgcolor; + #ifdef HAVE_ICAL_COLOR_PROPERTY + GdkRGBA stack_bgcolor; + icalproperty *prop; + #endif g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane)); g_return_if_fail (out_bgcolor); @@ -788,7 +792,20 @@ etdp_get_comp_colors (EToDoPane *to_do_pane, g_return_if_fail (E_IS_CAL_CLIENT (client)); g_return_if_fail (E_IS_CAL_COMPONENT (comp)); - bgcolor = g_hash_table_lookup (to_do_pane->priv->client_colors, e_client_get_source (E_CLIENT (client))); + #ifdef HAVE_ICAL_COLOR_PROPERTY + prop = icalcomponent_get_first_property (e_cal_component_get_icalcomponent (comp), ICAL_COLOR_PROPERTY); + if (prop) { + const gchar *color_spec; + + color_spec = icalproperty_get_color (prop); + if (color_spec && gdk_rgba_parse (&stack_bgcolor, color_spec)) { + bgcolor = &stack_bgcolor; + } + } + #endif + + if (!bgcolor) + bgcolor = g_hash_table_lookup (to_do_pane->priv->client_colors, e_client_get_source (E_CLIENT (client))); if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_TODO && to_do_pane->priv->highlight_overdue && diff --git a/src/e-util/e-color-combo.c b/src/e-util/e-color-combo.c index 7e8ef88e0d..ff17f792dc 100644 --- a/src/e-util/e-color-combo.c +++ b/src/e-util/e-color-combo.c @@ -442,7 +442,7 @@ color_combo_draw_frame_cb (GtkWidget *widget, if (rgba.alpha == 0) { draw_transparent_graphic (cr, width, height); } else { - cairo_set_source_rgb (cr, rgba.red, rgba.green, rgba.blue); + cairo_set_source_rgba (cr, rgba.red, rgba.green, rgba.blue, rgba.alpha); cairo_rectangle (cr, 0, 0, width, height); cairo_fill (cr); } @@ -456,7 +456,6 @@ color_combo_set_default_color_cb (EColorCombo *combo, e_color_combo_get_default_color (combo, &color); e_color_combo_set_current_color (combo, &color); - e_color_combo_set_default_transparent (combo, (color.alpha == 0)); g_signal_emit (combo, signals[ACTIVATED], 0, &color); } @@ -749,6 +748,8 @@ e_color_combo_init (EColorCombo *combo) gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0); combo->priv->arrow = widget; /* do not reference */ + gtk_widget_show_all (container); + /* Build the drop-down menu */ widget = gtk_window_new (GTK_WINDOW_POPUP); gtk_container_set_border_width (GTK_CONTAINER (widget), 5); @@ -934,8 +935,6 @@ e_color_combo_set_default_color (EColorCombo *combo, gtk_color_chooser_set_rgba ( GTK_COLOR_CHOOSER (combo->priv->chooser_widget), color); - e_color_combo_set_default_transparent (combo, (color->alpha == 0)); - g_object_notify (G_OBJECT (combo), "default-color"); } -- 2.11.4.GIT