[evolution] Calendar: Show probable HTML-formatted component DESCRIPTION as formatted text



commit 3bb9beb38b1641af09b041d089f56a7adb4c7228
Author: Milan Crha <mcrha redhat com>
Date:   Thu May 7 17:48:04 2020 +0200

    Calendar: Show probable HTML-formatted component DESCRIPTION as formatted text
    
    This covers the component editor and the itip-formatter, aka meeting
    invitations. The component editor can switch between viewing the DESCRIPTION
    as HTML and editing it as text. There is no editing as HTML.
    
    For example Google produces DESCRIPTION with mixed HTML
    content and plain text data, without any clue that the DESCRIPTION
    is with HTML tags (the DESCRIPTION itself is plain text, according
    to RFC 5545 (see section 3.8.1.5 and section 3.3.11)). Showing the HTML
    tags can make the DESCRIPTION human-unreadable.
    
    Partly related to https://gitlab.gnome.org/GNOME/evolution/-/issues/724

 src/calendar/gui/e-comp-editor-event.c          |   4 +-
 src/calendar/gui/e-comp-editor-memo.c           |   4 +-
 src/calendar/gui/e-comp-editor-property-part.c  |  56 +++++--
 src/calendar/gui/e-comp-editor-property-part.h  |   4 +
 src/calendar/gui/e-comp-editor-property-parts.c | 201 +++++++++++++++++++++++-
 src/calendar/gui/e-comp-editor-task.c           |   4 +-
 src/modules/itip-formatter/itip-view.c          |  44 ++++--
 7 files changed, 278 insertions(+), 39 deletions(-)
---
diff --git a/src/calendar/gui/e-comp-editor-event.c b/src/calendar/gui/e-comp-editor-event.c
index 0c566d056b..de257a30f2 100644
--- a/src/calendar/gui/e-comp-editor-event.c
+++ b/src/calendar/gui/e-comp-editor-event.c
@@ -188,8 +188,8 @@ ece_event_sensitize_widgets (ECompEditor *comp_editor,
 
        /* Make the Description read-only, not completely insensitive,
           thus it can be read and scrolled through and so on */
-       widget = e_comp_editor_property_part_get_edit_widget (event_editor->priv->description);
-       gtk_text_view_set_editable (GTK_TEXT_VIEW (gtk_bin_get_child (GTK_BIN (widget))), 
gtk_widget_get_sensitive (widget));
+       widget = e_comp_editor_property_part_string_get_real_edit_widget (E_COMP_EDITOR_PROPERTY_PART_STRING 
(event_editor->priv->description));
+       gtk_text_view_set_editable (GTK_TEXT_VIEW (widget), gtk_widget_get_sensitive (widget));
        gtk_widget_set_sensitive (widget, TRUE);
 
        action = e_comp_editor_get_action (comp_editor, "all-day-event");
diff --git a/src/calendar/gui/e-comp-editor-memo.c b/src/calendar/gui/e-comp-editor-memo.c
index ee1bb04060..8c2aef6638 100644
--- a/src/calendar/gui/e-comp-editor-memo.c
+++ b/src/calendar/gui/e-comp-editor-memo.c
@@ -202,8 +202,8 @@ ece_memo_sensitize_widgets (ECompEditor *comp_editor,
 
        /* Make the Description read-only, not completely insensitive,
           thus it can be read and scrolled through and so on */
-       widget = e_comp_editor_property_part_get_edit_widget (memo_editor->priv->description);
-       gtk_text_view_set_editable (GTK_TEXT_VIEW (gtk_bin_get_child (GTK_BIN (widget))), 
gtk_widget_get_sensitive (widget));
+       widget = e_comp_editor_property_part_string_get_real_edit_widget (E_COMP_EDITOR_PROPERTY_PART_STRING 
(memo_editor->priv->description));
+       gtk_text_view_set_editable (GTK_TEXT_VIEW (widget), gtk_widget_get_sensitive (widget));
        gtk_widget_set_sensitive (widget, TRUE);
 
        if (memo_editor->priv->insensitive_info_alert)
diff --git a/src/calendar/gui/e-comp-editor-property-part.c b/src/calendar/gui/e-comp-editor-property-part.c
index 1cbef9352c..35577132c3 100644
--- a/src/calendar/gui/e-comp-editor-property-part.c
+++ b/src/calendar/gui/e-comp-editor-property-part.c
@@ -401,8 +401,8 @@ ecepp_string_fill_widget (ECompEditorPropertyPart *property_part,
        g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
        g_return_if_fail (I_CAL_IS_COMPONENT (component));
 
-       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
-       g_return_if_fail (GTK_IS_ENTRY (edit_widget) || GTK_IS_SCROLLED_WINDOW (edit_widget));
+       edit_widget = e_comp_editor_property_part_string_get_real_edit_widget 
(E_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
+       g_return_if_fail (GTK_IS_ENTRY (edit_widget) || GTK_IS_TEXT_VIEW (edit_widget));
 
        klass = E_COMP_EDITOR_PROPERTY_PART_STRING_GET_CLASS (property_part);
        g_return_if_fail (klass != NULL);
@@ -442,10 +442,10 @@ ecepp_string_fill_widget (ECompEditorPropertyPart *property_part,
 
        if (GTK_IS_ENTRY (edit_widget)) {
                gtk_entry_set_text (GTK_ENTRY (edit_widget), text ? text : "");
-       } else /* if (GTK_IS_SCROLLED_WINDOW (edit_widget)) */ {
+       } else /* if (GTK_IS_TEXT_VIEW (edit_widget)) */ {
                GtkTextBuffer *buffer;
 
-               buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (gtk_bin_get_child (GTK_BIN (edit_widget))));
+               buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (edit_widget));
                gtk_text_buffer_set_text (buffer, text ? text : "", -1);
        }
 
@@ -466,8 +466,8 @@ ecepp_string_fill_component (ECompEditorPropertyPart *property_part,
        g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
        g_return_if_fail (I_CAL_IS_COMPONENT (component));
 
-       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
-       g_return_if_fail (GTK_IS_ENTRY (edit_widget) || GTK_IS_SCROLLED_WINDOW (edit_widget));
+       edit_widget = e_comp_editor_property_part_string_get_real_edit_widget 
(E_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
+       g_return_if_fail (GTK_IS_ENTRY (edit_widget) || GTK_IS_TEXT_VIEW (edit_widget));
 
        klass = E_COMP_EDITOR_PROPERTY_PART_STRING_GET_CLASS (property_part);
        g_return_if_fail (klass != NULL);
@@ -477,11 +477,11 @@ ecepp_string_fill_component (ECompEditorPropertyPart *property_part,
 
        if (GTK_IS_ENTRY (edit_widget)) {
                value = g_strdup (gtk_entry_get_text (GTK_ENTRY (edit_widget)));
-       } else /* if (GTK_IS_SCROLLED_WINDOW (edit_widget)) */ {
+       } else /* if (GTK_IS_TEXT_VIEW (edit_widget)) */ {
                GtkTextBuffer *buffer;
                GtkTextIter text_iter_start, text_iter_end;
 
-               buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (gtk_bin_get_child (GTK_BIN (edit_widget))));
+               buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (edit_widget));
                gtk_text_buffer_get_start_iter (buffer, &text_iter_start);
                gtk_text_buffer_get_end_iter (buffer, &text_iter_end);
                value = gtk_text_buffer_get_text (buffer, &text_iter_start, &text_iter_end, FALSE);
@@ -533,6 +533,14 @@ ecepp_string_fill_component (ECompEditorPropertyPart *property_part,
        g_free (value);
 }
 
+static GtkWidget *
+ecepp_string_get_real_edit_widget (ECompEditorPropertyPartString *part_string)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (part_string), NULL);
+
+       return e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_string));
+}
+
 static void
 e_comp_editor_property_part_string_init (ECompEditorPropertyPartString *part_string)
 {
@@ -555,6 +563,7 @@ e_comp_editor_property_part_string_class_init (ECompEditorPropertyPartStringClas
        klass->i_cal_new_func = NULL;
        klass->i_cal_set_func = NULL;
        klass->i_cal_get_func = NULL;
+       klass->get_real_edit_widget = ecepp_string_get_real_edit_widget;
 
        part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
        part_class->create_widgets = ecepp_string_create_widgets;
@@ -575,13 +584,10 @@ e_comp_editor_property_part_string_attach_focus_tracker (ECompEditorPropertyPart
 
        g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
 
-       edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_string));
-       if (edit_widget) {
-               if (GTK_IS_SCROLLED_WINDOW (edit_widget))
-                       e_widget_undo_attach (gtk_bin_get_child (GTK_BIN (edit_widget)), focus_tracker);
-               else
-                       e_widget_undo_attach (edit_widget, focus_tracker);
-       }
+       edit_widget = e_comp_editor_property_part_string_get_real_edit_widget (part_string);
+
+       if (edit_widget)
+               e_widget_undo_attach (edit_widget, focus_tracker);
 }
 
 void
@@ -601,6 +607,26 @@ e_comp_editor_property_part_string_is_multivalue (ECompEditorPropertyPartString
        return part_string->priv->is_multivalue;
 }
 
+GtkWidget *
+e_comp_editor_property_part_string_get_real_edit_widget (ECompEditorPropertyPartString *part_string)
+{
+       ECompEditorPropertyPartStringClass *klass;
+       GtkWidget *edit_widget;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (part_string), NULL);
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_STRING_GET_CLASS (part_string);
+       g_return_val_if_fail (klass != NULL, NULL);
+       g_return_val_if_fail (klass->get_real_edit_widget != NULL, NULL);
+
+       edit_widget = klass->get_real_edit_widget (part_string);
+
+       if (GTK_IS_SCROLLED_WINDOW (edit_widget))
+               edit_widget = gtk_bin_get_child (GTK_BIN (edit_widget));
+
+       return edit_widget;
+}
+
 /* ************************************************************************* */
 
 struct _ECompEditorPropertyPartDatetimePrivate {
diff --git a/src/calendar/gui/e-comp-editor-property-part.h b/src/calendar/gui/e-comp-editor-property-part.h
index cf29a09beb..96f0062f3e 100644
--- a/src/calendar/gui/e-comp-editor-property-part.h
+++ b/src/calendar/gui/e-comp-editor-property-part.h
@@ -218,6 +218,8 @@ struct _ECompEditorPropertyPartStringClass {
        void            (* i_cal_set_func)      (ICalProperty *prop,
                                                 const gchar *value);
        const gchar *   (* i_cal_get_func)      (ICalProperty *prop);
+
+       GtkWidget *     (* get_real_edit_widget)(ECompEditorPropertyPartString *part_string);
 };
 
 GType          e_comp_editor_property_part_string_get_type     (void) G_GNUC_CONST;
@@ -229,6 +231,8 @@ void                e_comp_editor_property_part_string_set_is_multivalue
                                                                 gboolean is_multivalue);
 gboolean       e_comp_editor_property_part_string_is_multivalue
                                                                (ECompEditorPropertyPartString *part_string);
+GtkWidget *    e_comp_editor_property_part_string_get_real_edit_widget
+                                                               (ECompEditorPropertyPartString *part_string);
 
 /* ************************************************************************* */
 
diff --git a/src/calendar/gui/e-comp-editor-property-parts.c b/src/calendar/gui/e-comp-editor-property-parts.c
index fbc97e0ab0..b27c6b3278 100644
--- a/src/calendar/gui/e-comp-editor-property-parts.c
+++ b/src/calendar/gui/e-comp-editor-property-parts.c
@@ -538,6 +538,13 @@ typedef struct _ECompEditorPropertyPartDescriptionClass ECompEditorPropertyPartD
 
 struct _ECompEditorPropertyPartDescription {
        ECompEditorPropertyPartString parent;
+
+       gboolean has_html;
+       gboolean mode_html;
+       GtkWidget *real_edit_widget;
+       GtkWidget *view_as_label;
+       GtkWidget *web_view_scrolled_window;
+       GtkWidget *web_view;
 };
 
 struct _ECompEditorPropertyPartDescriptionClass {
@@ -548,13 +555,85 @@ GType e_comp_editor_property_part_description_get_type (void) G_GNUC_CONST;
 
 G_DEFINE_TYPE (ECompEditorPropertyPartDescription, e_comp_editor_property_part_description, 
E_TYPE_COMP_EDITOR_PROPERTY_PART_STRING)
 
+static GtkWidget *
+ecepp_description_get_real_edit_widget (ECompEditorPropertyPartString *part_string)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DESCRIPTION (part_string), NULL);
+
+       return E_COMP_EDITOR_PROPERTY_PART_DESCRIPTION (part_string)->real_edit_widget;
+}
+
+static void
+ecepp_description_update_view_mode (ECompEditorPropertyPartDescription *description_part)
+{
+       if (description_part->has_html) {
+               gchar *markup;
+
+               markup = g_markup_printf_escaped ("<a href=\"evo-switch-view-mode\">%s</a>",
+                       description_part->mode_html ? _("Edit as text") : _("View as HTML"));
+
+               gtk_label_set_markup (GTK_LABEL (description_part->view_as_label), markup);
+
+               g_free (markup);
+
+               gtk_widget_show (description_part->view_as_label);
+
+               if (description_part->mode_html) {
+                       GtkTextBuffer *buffer;
+                       GtkTextIter text_iter_start, text_iter_end;
+                       GtkWidget *edit_widget;
+                       gchar *value;
+
+                       edit_widget = e_comp_editor_property_part_string_get_real_edit_widget 
(E_COMP_EDITOR_PROPERTY_PART_STRING (description_part));
+                       g_return_if_fail (GTK_IS_TEXT_VIEW (edit_widget));
+
+                       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (edit_widget));
+                       gtk_text_buffer_get_start_iter (buffer, &text_iter_start);
+                       gtk_text_buffer_get_end_iter (buffer, &text_iter_end);
+                       value = gtk_text_buffer_get_text (buffer, &text_iter_start, &text_iter_end, FALSE);
+
+                       e_web_view_load_string (E_WEB_VIEW (description_part->web_view), value ? value : "");
+
+                       g_free (value);
+
+                       gtk_widget_hide (description_part->real_edit_widget);
+                       gtk_widget_show (description_part->web_view_scrolled_window);
+               } else {
+                       gtk_widget_hide (description_part->web_view_scrolled_window);
+                       gtk_widget_show (description_part->real_edit_widget);
+               }
+       } else {
+               gtk_widget_hide (description_part->view_as_label);
+               gtk_widget_hide (description_part->web_view_scrolled_window);
+               gtk_widget_show (description_part->real_edit_widget);
+       }
+}
+
+static gboolean
+ecepp_description_flip_view_as_cb (GtkLabel *label,
+                                  const gchar *uri,
+                                  gpointer user_data)
+{
+       ECompEditorPropertyPartDescription *description_part = user_data;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DESCRIPTION (description_part), FALSE);
+
+       description_part->mode_html = !description_part->mode_html;
+
+       ecepp_description_update_view_mode (description_part);
+
+       return TRUE;
+}
+
 static void
 ecepp_description_create_widgets (ECompEditorPropertyPart *property_part,
                                  GtkWidget **out_label_widget,
                                  GtkWidget **out_edit_widget)
 {
        ECompEditorPropertyPartClass *part_class;
+       ECompEditorPropertyPartDescription *description_part;
        GtkTextView *text_view;
+       GtkWidget *box, *label;
 
        g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DESCRIPTION (property_part));
        g_return_if_fail (out_label_widget != NULL);
@@ -564,14 +643,18 @@ ecepp_description_create_widgets (ECompEditorPropertyPart *property_part,
        g_return_if_fail (part_class != NULL);
        g_return_if_fail (part_class->create_widgets != NULL);
 
+       description_part = E_COMP_EDITOR_PROPERTY_PART_DESCRIPTION (property_part);
+
        *out_label_widget = NULL;
 
        part_class->create_widgets (property_part, out_label_widget, out_edit_widget);
        g_return_if_fail (*out_label_widget == NULL);
        g_return_if_fail (*out_edit_widget != NULL);
 
-       *out_label_widget = gtk_label_new_with_mnemonic (C_("ECompEditor", "_Description:"));
-       gtk_label_set_mnemonic_widget (GTK_LABEL (*out_label_widget), *out_edit_widget);
+       description_part->real_edit_widget = *out_edit_widget;
+
+       label = gtk_label_new_with_mnemonic (C_("ECompEditor", "_Description:"));
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), *out_edit_widget);
 
        text_view = GTK_TEXT_VIEW (gtk_bin_get_child (GTK_BIN (*out_edit_widget)));
        gtk_text_view_set_wrap_mode (text_view, GTK_WRAP_WORD);
@@ -579,7 +662,7 @@ ecepp_description_create_widgets (ECompEditorPropertyPart *property_part,
        e_buffer_tagger_connect (text_view);
        e_spell_text_view_attach (text_view);
 
-       g_object_set (G_OBJECT (*out_label_widget),
+       g_object_set (G_OBJECT (label),
                "hexpand", FALSE,
                "halign", GTK_ALIGN_END,
                "vexpand", FALSE,
@@ -594,7 +677,92 @@ ecepp_description_create_widgets (ECompEditorPropertyPart *property_part,
                "height-request", 100,
                NULL);
 
-       gtk_widget_show (*out_label_widget);
+       box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+
+       g_object_set (G_OBJECT (box),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+
+       gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+
+       description_part->view_as_label = gtk_label_new ("");
+
+       g_object_set (G_OBJECT (description_part->view_as_label),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "track-visited-links", FALSE,
+               NULL);
+
+       g_signal_connect (description_part->view_as_label, "activate-link",
+               G_CALLBACK (ecepp_description_flip_view_as_cb), description_part);
+
+       gtk_box_pack_start (GTK_BOX (box), description_part->view_as_label, FALSE, FALSE, 0);
+
+       gtk_widget_show_all (box);
+
+       *out_label_widget = box;
+
+       box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+
+       g_object_set (G_OBJECT (box),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "visible", TRUE,
+               NULL);
+
+       gtk_box_pack_start (GTK_BOX (box), description_part->real_edit_widget, TRUE, TRUE, 0);
+
+       description_part->web_view = e_web_view_new ();
+       description_part->web_view_scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+
+       gtk_container_add (GTK_CONTAINER (description_part->web_view_scrolled_window), 
description_part->web_view);
+
+       g_object_set (G_OBJECT (description_part->web_view),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "visible", TRUE,
+               NULL);
+
+       g_object_set (G_OBJECT (description_part->web_view_scrolled_window),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "shadow-type", GTK_SHADOW_IN,
+               "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "visible", FALSE,
+               NULL);
+
+       gtk_box_pack_start (GTK_BOX (box), description_part->web_view_scrolled_window, TRUE, TRUE, 0);
+
+       *out_edit_widget = box;
+}
+
+static gboolean
+ecepp_description_contains_html (const gchar *value)
+{
+       if (!value || !*value)
+               return FALSE;
+
+       return camel_strstrcase (value, "<br>") ||
+               camel_strstrcase (value, "<span>") ||
+               camel_strstrcase (value, "<b>") ||
+               camel_strstrcase (value, "<i>") ||
+               camel_strstrcase (value, "<u>") ||
+               camel_strstrcase (value, "&nbsp;") ||
+               camel_strstrcase (value, "<ul>") ||
+               camel_strstrcase (value, "<li>") ||
+               camel_strstrcase (value, "</a>");
 }
 
 static void
@@ -602,7 +770,11 @@ ecepp_description_fill_widget (ECompEditorPropertyPart *property_part,
                               ICalComponent *component)
 {
        ECompEditorPropertyPartClass *part_class;
+       ECompEditorPropertyPartDescription *description_part;
+       GtkTextBuffer *buffer;
+       GtkTextIter text_iter_start, text_iter_end;
        GtkWidget *edit_widget;
+       gchar *value;
 
        g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DESCRIPTION (property_part));
        g_return_if_fail (I_CAL_IS_COMPONENT (component));
@@ -611,17 +783,31 @@ ecepp_description_fill_widget (ECompEditorPropertyPart *property_part,
        g_return_if_fail (part_class != NULL);
        g_return_if_fail (part_class->fill_widget != NULL);
 
+       description_part = E_COMP_EDITOR_PROPERTY_PART_DESCRIPTION (property_part);
+
        part_class->fill_widget (property_part, component);
 
-       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
-       g_return_if_fail (GTK_IS_SCROLLED_WINDOW (edit_widget));
+       edit_widget = e_comp_editor_property_part_string_get_real_edit_widget 
(E_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
+       g_return_if_fail (GTK_IS_TEXT_VIEW (edit_widget));
+
+       e_buffer_tagger_update_tags (GTK_TEXT_VIEW (edit_widget));
+
+       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (edit_widget));
+       gtk_text_buffer_get_start_iter (buffer, &text_iter_start);
+       gtk_text_buffer_get_end_iter (buffer, &text_iter_end);
+       value = gtk_text_buffer_get_text (buffer, &text_iter_start, &text_iter_end, FALSE);
+
+       description_part->has_html = ecepp_description_contains_html (value);
+
+       ecepp_description_update_view_mode (description_part);
 
-       e_buffer_tagger_update_tags (GTK_TEXT_VIEW (gtk_bin_get_child (GTK_BIN (edit_widget))));
+       g_free (value);
 }
 
 static void
 e_comp_editor_property_part_description_init (ECompEditorPropertyPartDescription *part_description)
 {
+       part_description->mode_html = TRUE;
 }
 
 static void
@@ -636,6 +822,7 @@ e_comp_editor_property_part_description_class_init (ECompEditorPropertyPartDescr
        part_string_class->i_cal_new_func = i_cal_property_new_description;
        part_string_class->i_cal_set_func = i_cal_property_set_description;
        part_string_class->i_cal_get_func = i_cal_property_get_description;
+       part_string_class->get_real_edit_widget = ecepp_description_get_real_edit_widget;
 
        part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
        part_class->create_widgets = ecepp_description_create_widgets;
diff --git a/src/calendar/gui/e-comp-editor-task.c b/src/calendar/gui/e-comp-editor-task.c
index 4280ae6c8b..6c94f79b12 100644
--- a/src/calendar/gui/e-comp-editor-task.c
+++ b/src/calendar/gui/e-comp-editor-task.c
@@ -511,8 +511,8 @@ ece_task_sensitize_widgets (ECompEditor *comp_editor,
 
        /* Make the Description read-only, not completely insensitive,
           thus it can be read and scrolled through and so on */
-       widget = e_comp_editor_property_part_get_edit_widget (task_editor->priv->description);
-       gtk_text_view_set_editable (GTK_TEXT_VIEW (gtk_bin_get_child (GTK_BIN (widget))), 
gtk_widget_get_sensitive (widget));
+       widget = e_comp_editor_property_part_string_get_real_edit_widget (E_COMP_EDITOR_PROPERTY_PART_STRING 
(task_editor->priv->description));
+       gtk_text_view_set_editable (GTK_TEXT_VIEW (widget), gtk_widget_get_sensitive (widget));
        gtk_widget_set_sensitive (widget, TRUE);
 
        action = e_comp_editor_get_action (comp_editor, "all-day-task");
diff --git a/src/modules/itip-formatter/itip-view.c b/src/modules/itip-formatter/itip-view.c
index 3475125622..a5d5ceeab4 100644
--- a/src/modules/itip-formatter/itip-view.c
+++ b/src/modules/itip-formatter/itip-view.c
@@ -6554,18 +6554,40 @@ itip_view_init_view (ItipView *view)
        g_slist_free_full (list, e_cal_component_text_free);
 
        if (gstring) {
-               gchar *html;
-
-               html = camel_text_to_html (
-                       gstring->str,
-                       CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
-                       CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
-                       CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
-                       CAMEL_MIME_FILTER_TOHTML_MARK_CITATION |
-                       CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES,
-                       0);
+               gchar *html = NULL;
+
+               /* Google encodes HTML into the description, without giving a clue about it,
+                  but try to guess whether it can be an HTML blob or not. */
+               if (camel_strstrcase (gstring->str, "<br>") ||
+                   camel_strstrcase (gstring->str, "<span>") ||
+                   camel_strstrcase (gstring->str, "<b>") ||
+                   camel_strstrcase (gstring->str, "<i>") ||
+                   camel_strstrcase (gstring->str, "<u>") ||
+                   camel_strstrcase (gstring->str, "&nbsp;") ||
+                   camel_strstrcase (gstring->str, "<ul>") ||
+                   camel_strstrcase (gstring->str, "<li>") ||
+                   camel_strstrcase (gstring->str, "</a>")) {
+                       gchar *ptr = gstring->str;
+                       /* To make things easier, Google mixes HTML '<br>' with plain text '\n'... */
+                       while (ptr = strchr (ptr, '\n'), ptr) {
+                               gssize pos = ptr - gstring->str;
+
+                               g_string_insert (gstring, pos, "<br>");
+
+                               ptr = gstring->str + pos + 4 + 1;
+                       }
+               } else {
+                       html = camel_text_to_html (
+                               gstring->str,
+                               CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
+                               CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
+                               CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
+                               CAMEL_MIME_FILTER_TOHTML_MARK_CITATION |
+                               CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES,
+                               0);
+               }
 
-               itip_view_set_description (view, html);
+               itip_view_set_description (view, html ? html : gstring->str);
                g_string_free (gstring, TRUE);
                g_free (html);
        }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]