[evolution] I#1433 - Message List columns for custom headers



commit e555887a46cc96b131e6ba2665b5fd11130edeba
Author: Milan Crha <mcrha redhat com>
Date:   Thu Aug 5 10:52:18 2021 +0200

    I#1433 - Message List columns for custom headers
    
    Closes https://gitlab.gnome.org/GNOME/evolution/-/issues/1433

 src/e-util/e-table-specification.c |  37 ++++
 src/e-util/e-table-specification.h |   5 +-
 src/mail/e-mail-reader.c           |  10 +
 src/mail/mail-config.ui            | 141 +++++++++++++-
 src/mail/message-list.c            | 159 ++++++++++++++++
 src/mail/message-list.etspec       |   3 +
 src/mail/message-list.h            |   3 +
 src/modules/mail/em-mailer-prefs.c | 378 ++++++++++++++++++++++++++++++++++++-
 8 files changed, 732 insertions(+), 4 deletions(-)
---
diff --git a/src/e-util/e-table-specification.c b/src/e-util/e-table-specification.c
index d6a35071a2..67f6b161a1 100644
--- a/src/e-util/e-table-specification.c
+++ b/src/e-util/e-table-specification.c
@@ -683,3 +683,40 @@ e_table_specification_get_column_index (ETableSpecification *specification,
        return column_index;
 }
 
+/**
+ * e_table_specification_get_column_by_model_col:
+ * @specification: an #ETableSpecification
+ * @model_col: a model column index to get
+ *
+ * Get an #ETableColumnSpecification for the given @model_col.
+ *
+ * Returns: (transfer none) (nullable): an #ETableColumnSpecification for the given @model_col
+ *    or %NULL, when not found.
+ *
+ * Since: 3.42
+ **/
+ETableColumnSpecification *
+e_table_specification_get_column_by_model_col (ETableSpecification *specification,
+                                              gint model_col)
+{
+       GPtrArray *columns;
+       ETableColumnSpecification *col_spec = NULL;
+       guint ii;
+
+       g_return_val_if_fail (E_IS_TABLE_SPECIFICATION (specification), NULL);
+
+       columns = e_table_specification_ref_columns (specification);
+
+       for (ii = 0; ii < columns->len; ii++) {
+               ETableColumnSpecification *adept = columns->pdata[ii];
+
+               if (adept && adept->model_col == model_col) {
+                       col_spec = adept;
+                       break;
+               }
+       }
+
+       g_ptr_array_unref (columns);
+
+       return col_spec;
+}
diff --git a/src/e-util/e-table-specification.h b/src/e-util/e-table-specification.h
index 3a3a7ca380..33c3f9582c 100644
--- a/src/e-util/e-table-specification.h
+++ b/src/e-util/e-table-specification.h
@@ -90,7 +90,10 @@ GPtrArray *  e_table_specification_ref_columns
 gint           e_table_specification_get_column_index
                                                (ETableSpecification *specification,
                                                 ETableColumnSpecification *column_spec);
-
+ETableColumnSpecification *
+               e_table_specification_get_column_by_model_col
+                                               (ETableSpecification *specification,
+                                                gint model_col);
 G_END_DECLS
 
 #endif /* E_TABLE_SPECIFICATION_H */
diff --git a/src/mail/e-mail-reader.c b/src/mail/e-mail-reader.c
index 7011250c51..b7473648f0 100644
--- a/src/mail/e-mail-reader.c
+++ b/src/mail/e-mail-reader.c
@@ -3648,8 +3648,18 @@ mail_reader_message_loaded_cb (CamelFolder *folder,
        }
 
        if (message != NULL) {
+               CamelMessageInfo *mi;
+
                mail_reader_manage_followup_flag (reader, folder, message_uid);
 
+               mi = camel_folder_get_message_info (folder, message_uid);
+               if (mi) {
+                       if (camel_util_fill_message_info_user_headers (mi, camel_medium_get_headers 
(CAMEL_MEDIUM (message))))
+                               gtk_widget_queue_draw (message_list);
+
+                       g_object_unref (mi);
+               }
+
                g_signal_emit (
                        reader, signals[MESSAGE_LOADED], 0,
                        message_uid, message);
diff --git a/src/mail/mail-config.ui b/src/mail/mail-config.ui
index 7639044267..7a92ef2d63 100644
--- a/src/mail/mail-config.ui
+++ b/src/mail/mail-config.ui
@@ -3098,6 +3098,145 @@
             <property name="position">1</property>
           </packing>
         </child>
+        <child>
+          <object class="GtkVBox" id="user-headers-section">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkLabel" id="user-headers-header">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Message List User Headers</property>
+                <property name="use_underline">True</property>
+                <property name="xalign">0</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkAlignment" id="user-headers-alignment">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="left_padding">12</property>
+                <child>
+                  <object class="GtkHBox" id="user-headers-hbox">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkVBox" id="user-headers-vbox1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="spacing">12</property>
+                        <child>
+                          <object class="GtkScrolledWindow" id="user-headers-scrolledwindow">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="shadow_type">in</property>
+                            <child>
+                              <object class="GtkTreeView" id="treeUserHeaders">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="headers_visible">True</property>
+                                <child internal-child="accessible">
+                                  <object class="AtkObject" id="treeUserHeaders-atkobject">
+                                    <property name="AtkObject::accessible-name" translatable="yes">Message 
List User Headers Table</property>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">True</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkVBox" id="user-headers-vbox2">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkButton" id="cmdUserHeadersAdd">
+                            <property name="label" translatable="yes">_Add</property>
+                            <property name="visible">True</property>
+                            <property name="sensitive">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="use_underline">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="cmdUserHeadersEdit">
+                            <property name="label" translatable="yes">_Edit</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="use_underline">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="cmdUserHeadersRemove">
+                            <property name="label" translatable="yes">Re_move</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="use_underline">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
         <child>
           <object class="GtkVBox" id="datetime-format-section">
             <property name="visible">True</property>
@@ -3181,7 +3320,7 @@
           <packing>
             <property name="expand">False</property>
             <property name="fill">False</property>
-            <property name="position">2</property>
+            <property name="position">3</property>
           </packing>
         </child>
       </object>
diff --git a/src/mail/message-list.c b/src/mail/message-list.c
index 7d7588602c..7b22dee479 100644
--- a/src/mail/message-list.c
+++ b/src/mail/message-list.c
@@ -137,6 +137,10 @@ struct _MessageListPrivate {
        guint update_actions_idle_id;
 
        volatile gint setting_up_search_folder;
+
+       GSettings *eds_settings; /* references org.gnome.evolution-data-server schema */
+       gchar *user_headers[CAMEL_UTILS_MAX_USER_HEADERS + 1];
+       guint user_headers_count; /* how many are set */
 };
 
 /* XXX Plain GNode suffers from O(N) tail insertions, and that won't
@@ -2147,6 +2151,17 @@ ml_tree_value_at_ex (ETreeModel *etm,
        case COL_UID: {
                return (gpointer) camel_pstring_strdup (camel_message_info_get_uid (msg_info));
        }
+       case COL_USER_HEADER_1:
+       case COL_USER_HEADER_2:
+       case COL_USER_HEADER_3: {
+               const gchar *name = NULL;
+               guint index = col - COL_USER_HEADER_1;
+               if (message_list->priv->user_headers && index < message_list->priv->user_headers_count)
+                       name = message_list->priv->user_headers[index];
+               if (name && *name)
+                       return (gpointer) camel_message_info_dup_user_header (msg_info, name);
+               return NULL;
+       }
        default:
                g_warning ("%s: This shouldn't be reached (col:%d)", G_STRFUNC, col);
                return NULL;
@@ -3091,6 +3106,117 @@ message_list_localized_re_separators_changed_cb (GSettings *settings,
        g_mutex_unlock (&message_list->priv->re_prefixes_lock);
 }
 
+static void
+message_list_user_headers_changed_cb (GSettings *settings,
+                                     const gchar *key,
+                                     gpointer user_data)
+{
+       /* Do it this way, to reuse the localized strings from the message-list.etspec */
+       const gchar *default_titles[] = {
+               N_("User Header 1"),
+               N_("User Header 2"),
+               N_("User Header 3")
+       };
+       MessageList *message_list = user_data;
+       ETableSpecification *spec;
+       GnomeCanvasItem *header_item;
+       ETableHeader *header;
+       gchar **user_headers;
+       gboolean changed = FALSE;
+       guint ii, jj;
+
+       g_return_if_fail (IS_MESSAGE_LIST (message_list));
+       #ifdef ENABLE_MAINTAINER_MODE
+       g_warn_if_fail (G_N_ELEMENTS (default_titles) == CAMEL_UTILS_MAX_USER_HEADERS);
+       #endif
+
+       spec = e_tree_get_spec (E_TREE (message_list));
+       header_item = e_tree_get_header_item (E_TREE (message_list));
+       if (header_item)
+               g_object_get (header_item, "full-header", &header, NULL);
+       else
+               header = NULL;
+
+       user_headers = g_settings_get_strv (settings, "camel-message-info-user-headers");
+
+       for (ii = 0, jj = 0; user_headers && user_headers[ii] && jj < CAMEL_UTILS_MAX_USER_HEADERS; ii++) {
+               const gchar *header_name = NULL;
+               gchar *display_name = NULL;
+
+               camel_util_decode_user_header_setting (user_headers[ii], &display_name, &header_name);
+
+               if (header_name && *header_name) {
+                       ETableColumnSpecification *col_spec;
+
+                       if (g_strcmp0 (message_list->priv->user_headers[jj], header_name) != 0) {
+                               g_free (message_list->priv->user_headers[jj]);
+                               message_list->priv->user_headers[jj] = g_strdup (header_name);
+                               changed = TRUE;
+                       }
+
+                       col_spec = spec ? e_table_specification_get_column_by_model_col (spec, 
COL_USER_HEADER_1 + jj) : NULL;
+                       if (col_spec && g_strcmp0 (col_spec->title, display_name && *display_name ? 
display_name : header_name) != 0) {
+                               ETableCol *col;
+
+                               changed = TRUE;
+                               g_free (col_spec->title);
+                               if (display_name && *display_name) {
+                                       col_spec->title = display_name;
+                                       display_name = NULL;
+                               } else {
+                                       col_spec->title = g_strdup (header_name);
+                               }
+
+                               col = header ? e_table_header_get_column_by_col_idx (header, 
COL_USER_HEADER_1 + jj) : NULL;
+                               if (col && g_strcmp0 (col->text, col_spec->title) != 0) {
+                                       g_free (col->text);
+                                       col->text = g_strdup (col_spec->title);
+                               }
+                       }
+
+                       jj++;
+               }
+
+               g_free (display_name);
+       }
+
+       message_list->priv->user_headers_count = jj;
+
+       for (ii = jj; ii < CAMEL_UTILS_MAX_USER_HEADERS; ii++) {
+               if (message_list->priv->user_headers[ii]) {
+                       ETableColumnSpecification *col_spec;
+                       ETableCol *col;
+                       const gchar *title;
+
+                       title = _(default_titles[ii]);
+
+                       col_spec = spec ? e_table_specification_get_column_by_model_col (spec, 
COL_USER_HEADER_1 + jj) : NULL;
+                       if (col_spec && g_strcmp0 (col_spec->title, title) != 0) {
+                               g_free (col_spec->title);
+                               col_spec->title = g_strdup (title);
+                       }
+
+                       col = header ? e_table_header_get_column_by_col_idx (header, COL_USER_HEADER_1 + ii) 
: NULL;
+                       if (col && g_strcmp0 (col->text, title) != 0) {
+                               g_free (col->text);
+                               col->text = g_strdup (title);
+                       }
+
+                       changed = TRUE;
+               }
+
+               g_free (message_list->priv->user_headers[ii]);
+               message_list->priv->user_headers[ii] = NULL;
+       }
+
+       message_list->priv->user_headers[jj] = NULL;
+
+       g_strfreev (user_headers);
+
+       if (changed)
+               gtk_widget_queue_draw (GTK_WIDGET (message_list));
+}
+
 static void
 message_list_set_session (MessageList *message_list,
                           EMailSession *session)
@@ -3300,10 +3426,16 @@ message_list_dispose (GObject *object)
                        G_CALLBACK (message_list_localized_re_separators_changed_cb), message_list);
        }
 
+       if (priv->eds_settings) {
+               g_signal_handlers_disconnect_by_func (priv->eds_settings,
+                       G_CALLBACK (message_list_user_headers_changed_cb), message_list);
+       }
+
        g_clear_object (&priv->session);
        g_clear_object (&priv->folder);
        g_clear_object (&priv->invisible);
        g_clear_object (&priv->mail_settings);
+       g_clear_object (&priv->eds_settings);
 
        g_clear_object (&message_list->extras);
 
@@ -3330,6 +3462,7 @@ static void
 message_list_finalize (GObject *object)
 {
        MessageList *message_list = MESSAGE_LIST (object);
+       guint ii;
 
        g_hash_table_destroy (message_list->normalised_hash);
 
@@ -3355,6 +3488,11 @@ message_list_finalize (GObject *object)
        g_clear_pointer (&message_list->priv->new_mail_bg_color, gdk_rgba_free);
        g_clear_pointer (&message_list->priv->new_mail_fg_color, g_free);
 
+       for (ii = 0; ii < CAMEL_UTILS_MAX_USER_HEADERS; ii++) {
+               g_free (message_list->priv->user_headers[ii]);
+               message_list->priv->user_headers[ii] = NULL;
+       }
+
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (message_list_parent_class)->finalize (object);
 }
@@ -3623,6 +3761,9 @@ message_list_duplicate_value (ETreeModel *tree_model,
                case COL_MIXED_RECIPIENTS:
                case COL_LOCATION:
                case COL_LABELS:
+               case COL_USER_HEADER_1:
+               case COL_USER_HEADER_2:
+               case COL_USER_HEADER_3:
                        return g_strdup (value);
 
                case COL_SENT:
@@ -3688,6 +3829,9 @@ message_list_free_value (ETreeModel *tree_model,
                case COL_SENT:
                case COL_RECEIVED:
                case COL_FOLLOWUP_DUE_BY:
+               case COL_USER_HEADER_1:
+               case COL_USER_HEADER_2:
+               case COL_USER_HEADER_3:
                        g_free (value);
                        break;
 
@@ -3720,6 +3864,9 @@ message_list_initialize_value (ETreeModel *tree_model,
                case COL_FOLLOWUP_FLAG_STATUS:
                case COL_FOLLOWUP_DUE_BY:
                case COL_UID:
+               case COL_USER_HEADER_1:
+               case COL_USER_HEADER_2:
+               case COL_USER_HEADER_3:
                        return NULL;
 
                case COL_LOCATION:
@@ -3772,6 +3919,9 @@ message_list_value_is_empty (ETreeModel *tree_model,
                case COL_MIXED_RECIPIENTS:
                case COL_LABELS:
                case COL_UID:
+               case COL_USER_HEADER_1:
+               case COL_USER_HEADER_2:
+               case COL_USER_HEADER_3:
                        return !(value && *(gchar *) value);
 
                default:
@@ -3831,6 +3981,9 @@ message_list_value_to_string (ETreeModel *tree_model,
                case COL_MIXED_RECIPIENTS:
                case COL_LABELS:
                case COL_UID:
+               case COL_USER_HEADER_1:
+               case COL_USER_HEADER_2:
+               case COL_USER_HEADER_3:
                        return g_strdup (value);
 
                default:
@@ -4129,6 +4282,7 @@ message_list_init (MessageList *message_list)
        message_list->priv->paste_target_list = target_list;
 
        message_list->priv->mail_settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       message_list->priv->eds_settings = e_util_ref_settings ("org.gnome.evolution-data-server");
        message_list->priv->re_prefixes = NULL;
        message_list->priv->re_separators = NULL;
        message_list->priv->group_by_threads = TRUE;
@@ -4143,6 +4297,9 @@ message_list_init (MessageList *message_list)
 
        message_list_localized_re_changed_cb (message_list->priv->mail_settings, NULL, message_list);
        message_list_localized_re_separators_changed_cb (message_list->priv->mail_settings, NULL, 
message_list);
+
+       g_signal_connect (message_list->priv->eds_settings, "changed::camel-message-info-user-headers",
+               G_CALLBACK (message_list_user_headers_changed_cb), message_list);
 }
 
 static void
@@ -4243,6 +4400,8 @@ message_list_construct (MessageList *message_list)
 
        g_signal_connect (message_list, "style-updated",
                G_CALLBACK (ml_style_updated_cb), NULL);
+
+       message_list_user_headers_changed_cb (message_list->priv->eds_settings, NULL, message_list);
 }
 
 /**
diff --git a/src/mail/message-list.etspec b/src/mail/message-list.etspec
index 6a37104070..4ac83bb87a 100644
--- a/src/mail/message-list.etspec
+++ b/src/mail/message-list.etspec
@@ -37,6 +37,9 @@
   <ETableColumn model_col="23" _title="UID" expansion="0.0" minimum_width="32" resizable="true" 
cell="render_text" compare="string" search="string"/>
   <ETableColumn model_col="24" _title="Sender Mail" expansion="1.0" minimum_width="32" resizable="true" 
cell="render_text" compare="address_compare" search="string" priority="10"/>
   <ETableColumn model_col="25" _title="Recipients Mail" expansion="1.0" minimum_width="32" resizable="true" 
cell="render_text" compare="address_compare" search="string" priority="10"/>
+  <ETableColumn model_col="26" _title="User Header 1" expansion="0.0" minimum_width="32" resizable="true" 
cell="render_text" compare="string" search="string"/>
+  <ETableColumn model_col="27" _title="User Header 2" expansion="0.0" minimum_width="32" resizable="true" 
cell="render_text" compare="string" search="string"/>
+  <ETableColumn model_col="28" _title="User Header 3" expansion="0.0" minimum_width="32" resizable="true" 
cell="render_text" compare="string" search="string"/>
 
   <ETableState>
     <column source="0"/> <column source="3"/> <column source="1"/>
diff --git a/src/mail/message-list.h b/src/mail/message-list.h
index 0244250024..82739dafdb 100644
--- a/src/mail/message-list.h
+++ b/src/mail/message-list.h
@@ -81,6 +81,9 @@ enum {
        COL_UID,
        COL_SENDER_MAIL,
        COL_RECIPIENTS_MAIL,
+       COL_USER_HEADER_1,
+       COL_USER_HEADER_2,
+       COL_USER_HEADER_3,
 
        COL_LAST,
 
diff --git a/src/modules/mail/em-mailer-prefs.c b/src/modules/mail/em-mailer-prefs.c
index 3d7b9ac757..e83fdf228c 100644
--- a/src/modules/mail/em-mailer-prefs.c
+++ b/src/modules/mail/em-mailer-prefs.c
@@ -45,13 +45,24 @@ enum {
        HEADER_LIST_N_COLUMNS
 };
 
-static GType col_types[] = {
+static GType header_list_col_types[] = {
        G_TYPE_STRING,
        G_TYPE_BOOLEAN,
        G_TYPE_BOOLEAN,
        G_TYPE_STRING
 };
 
+enum {
+       USER_HEADERS_LIST_HEADER_COLUMN,
+       USER_HEADERS_LIST_TITLE_COLUMN,
+       USER_HEADERS_LIST_N_COLUMNS
+};
+
+static GType user_headers_list_col_types[] = {
+       G_TYPE_STRING,
+       G_TYPE_STRING
+};
+
 #define EM_FORMAT_HEADER_XMAILER "x-evolution-mailer"
 
 /* Keep this synchronized with the "show-headers" key
@@ -93,6 +104,7 @@ enum {
 struct _EMMailerPrefsPrivate {
        GtkBuilder *builder;
        GSettings *settings;
+       GSettings *eds_settings;
        EMailBackend *mail_backend;
 
        /* General tab */
@@ -124,6 +136,15 @@ struct _EMMailerPrefsPrivate {
        gulong header_list_store_row_changed_id;
        guint save_headers_id;
 
+       GtkButton *user_headers_add;
+       GtkButton *user_headers_edit;
+       GtkButton *user_headers_remove;
+       GtkTreeView *user_headers_list;
+       GtkListStore *user_headers_list_store;
+       gulong user_headers_list_store_row_changed_id;
+       gulong user_headers_settings_changed_id;
+       guint user_headers_save_id;
+
        GtkToggleButton *junk_header_check;
        GtkTreeView *junk_header_tree;
        GtkListStore *junk_header_list_store;
@@ -158,6 +179,13 @@ em_mailer_prefs_dispose (GObject *object)
                prefs->priv->save_headers_id = 0;
        }
 
+       if (prefs->priv->user_headers_save_id) {
+               g_source_remove (prefs->priv->user_headers_save_id);
+               prefs->priv->user_headers_save_id = 0;
+       }
+
+       g_signal_handlers_disconnect_by_data (prefs->priv->eds_settings, prefs);
+
        /* Chain up to parent's method. */
        G_OBJECT_CLASS (em_mailer_prefs_parent_class)->dispose (object);
 }
@@ -169,6 +197,7 @@ em_mailer_prefs_finalize (GObject *object)
 
        g_object_unref (prefs->priv->builder);
        g_object_unref (prefs->priv->settings);
+       g_object_unref (prefs->priv->eds_settings);
 
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (em_mailer_prefs_parent_class)->finalize (object);
@@ -191,6 +220,7 @@ em_mailer_prefs_init (EMMailerPrefs *preferences)
 {
        preferences->priv = G_TYPE_INSTANCE_GET_PRIVATE (preferences, EM_TYPE_MAILER_PREFS, 
EMMailerPrefsPrivate);
        preferences->priv->settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       preferences->priv->eds_settings = e_util_ref_settings ("org.gnome.evolution-data-server");
 
        gtk_orientable_set_orientation (GTK_ORIENTABLE (preferences), GTK_ORIENTATION_VERTICAL);
 }
@@ -1228,6 +1258,290 @@ em_mailer_prefs_window_notify_visible_cb (EPreferencesWindow *preferences,
        em_mailer_prefs_fill_remote_content_section (prefs, RC_SECTION_MAILS);
 }
 
+static void
+emmp_user_headers_update_buttons (EMMailerPrefs *prefs)
+{
+       GtkTreeSelection *selection;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       gboolean has_selection;
+
+       model = GTK_TREE_MODEL (prefs->priv->user_headers_list_store);
+       selection = gtk_tree_view_get_selection (prefs->priv->user_headers_list);
+       has_selection = gtk_tree_selection_get_selected (selection, NULL, &iter);
+
+       gtk_widget_set_sensitive (GTK_WIDGET (prefs->priv->user_headers_add), gtk_tree_model_iter_n_children 
(model, NULL) < CAMEL_UTILS_MAX_USER_HEADERS);
+       gtk_widget_set_sensitive (GTK_WIDGET (prefs->priv->user_headers_edit), has_selection);
+       gtk_widget_set_sensitive (GTK_WIDGET (prefs->priv->user_headers_remove), has_selection);
+}
+
+static void
+emmp_user_headers_save (EMMailerPrefs *prefs)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       GPtrArray *value;
+       gboolean valid;
+
+       value = g_ptr_array_new_with_free_func (g_free);
+
+       model = GTK_TREE_MODEL (prefs->priv->user_headers_list_store);
+       valid = gtk_tree_model_get_iter_first (model, &iter);
+
+       while (valid) {
+               gchar *header = NULL, *title = NULL;
+
+               gtk_tree_model_get (model, &iter,
+                       USER_HEADERS_LIST_HEADER_COLUMN, &header,
+                       USER_HEADERS_LIST_TITLE_COLUMN, &title,
+                       -1);
+
+               if (header && *header)
+                       g_ptr_array_add (value, camel_util_encode_user_header_setting (title, header));
+
+               g_free (header);
+               g_free (title);
+
+               valid = gtk_tree_model_iter_next (model, &iter);
+       }
+
+       /* NULL-terminated */
+       g_ptr_array_add (value, NULL);
+
+       g_signal_handler_block (prefs->priv->eds_settings, prefs->priv->user_headers_settings_changed_id);
+       g_settings_set_strv (prefs->priv->eds_settings, "camel-message-info-user-headers", (const gchar * 
const *) value->pdata);
+       g_signal_handler_unblock (prefs->priv->eds_settings, prefs->priv->user_headers_settings_changed_id);
+
+       g_ptr_array_unref (value);
+}
+
+static gboolean
+emmp_user_headers_save_idle_cb (gpointer user_data)
+{
+       EMMailerPrefs *prefs = user_data;
+
+       g_return_val_if_fail (prefs != NULL, FALSE);
+
+       if (!g_source_is_destroyed (g_main_current_source ())) {
+               prefs->priv->user_headers_save_id = 0;
+               emmp_user_headers_save (prefs);
+       }
+
+       return FALSE;
+}
+
+static void
+emmp_user_headers_schedule_save (EMMailerPrefs *prefs)
+{
+       if (!prefs->priv->user_headers_save_id)
+               prefs->priv->user_headers_save_id = g_idle_add (emmp_user_headers_save_idle_cb, prefs);
+}
+
+static void
+emmp_user_headers_add_clicked_cb (GtkWidget *button,
+                                 gpointer user_data)
+{
+       EMMailerPrefs *prefs = user_data;
+       GtkTreeModel *model;
+
+       model = GTK_TREE_MODEL (prefs->priv->user_headers_list_store);
+
+       if (gtk_tree_model_iter_n_children (model, NULL) < CAMEL_UTILS_MAX_USER_HEADERS) {
+               GtkTreeViewColumn *column;
+               GtkTreePath *path;
+               GtkTreeIter iter;
+
+               g_signal_handler_block (prefs->priv->user_headers_list_store, 
prefs->priv->user_headers_list_store_row_changed_id);
+
+               gtk_list_store_append (prefs->priv->user_headers_list_store, &iter);
+
+               path = gtk_tree_model_get_path (model, &iter);
+               column = gtk_tree_view_get_column (prefs->priv->user_headers_list, 
USER_HEADERS_LIST_HEADER_COLUMN);
+               gtk_tree_view_set_cursor (prefs->priv->user_headers_list, path, column, TRUE);
+               gtk_tree_view_row_activated (prefs->priv->user_headers_list, path, column);
+               gtk_tree_path_free (path);
+
+               g_signal_handler_unblock (prefs->priv->user_headers_list_store, 
prefs->priv->user_headers_list_store_row_changed_id);
+       }
+
+       emmp_user_headers_update_buttons (prefs);
+}
+
+static void
+emmp_user_headers_edit_clicked_cb (GtkWidget *button,
+                                  gpointer user_data)
+{
+       EMMailerPrefs *prefs = user_data;
+       GtkTreeSelection *selection;
+       GtkTreeModel *model;
+       GtkTreePath *path;
+       GtkTreeIter iter;
+       GtkTreeViewColumn *focus_col;
+
+       selection = gtk_tree_view_get_selection (prefs->priv->user_headers_list);
+       if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+               return;
+
+       focus_col = gtk_tree_view_get_column (prefs->priv->user_headers_list, 
USER_HEADERS_LIST_HEADER_COLUMN);
+       path = gtk_tree_model_get_path (model, &iter);
+
+       if (path) {
+               gtk_tree_view_set_cursor (prefs->priv->user_headers_list, path, focus_col, TRUE);
+               gtk_tree_path_free (path);
+       }
+
+       emmp_user_headers_update_buttons (prefs);
+}
+static void
+emmp_user_headers_remove_clicked_cb (GtkWidget *button,
+                                    gpointer user_data)
+{
+       EMMailerPrefs *prefs = user_data;
+       GtkTreeSelection *selection;
+       GtkTreeModel *model;
+       GtkTreeIter iter, next_iter;
+       GtkTreePath *path;
+
+       selection = gtk_tree_view_get_selection (prefs->priv->user_headers_list);
+       if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
+               emmp_user_headers_update_buttons (prefs);
+               return;
+       }
+
+       next_iter = iter;
+       path = gtk_tree_model_get_path (model, &iter);
+       if (!gtk_tree_model_iter_next (model, &next_iter) &&
+           !gtk_tree_path_prev (path)) {
+               gtk_tree_path_free (path);
+               path = NULL;
+       }
+
+       gtk_list_store_remove (prefs->priv->user_headers_list_store, &iter);
+
+       if (path) {
+               gtk_tree_selection_select_path (selection, path);
+               gtk_tree_path_free (path);
+       }
+
+       emmp_user_headers_update_buttons (prefs);
+       emmp_user_headers_schedule_save (prefs);
+}
+
+static void
+emmp_use_headers_cell_edited (EMMailerPrefs *prefs,
+                             gint cell,
+                             const gchar *path_string,
+                             gchar *new_text)
+{
+       GtkTreeIter iter;
+
+       if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (prefs->priv->user_headers_list_store), 
&iter, path_string)) {
+               g_warn_if_reached ();
+               return;
+       }
+
+       if (new_text)
+               g_strstrip (new_text);
+
+       if (cell == USER_HEADERS_LIST_HEADER_COLUMN && (!new_text || !*new_text)) {
+               gtk_button_clicked (GTK_BUTTON (prefs->priv->user_headers_remove));
+       } else {
+               gtk_list_store_set (prefs->priv->user_headers_list_store, &iter,
+                       cell, new_text,
+                       -1);
+       }
+
+       emmp_user_headers_update_buttons (prefs);
+}
+
+static void
+emmp_use_headers_cell_header_edited_cb (GtkCellRendererText *cell,
+                                       const gchar *path_string,
+                                       gchar *new_text,
+                                       gpointer user_data)
+{
+       EMMailerPrefs *prefs = user_data;
+
+       emmp_use_headers_cell_edited (prefs, USER_HEADERS_LIST_HEADER_COLUMN, path_string, new_text);
+}
+
+static void
+emmp_use_headers_cell_title_edited_cb (GtkCellRendererText *cell,
+                                      const gchar *path_string,
+                                      gchar *new_text,
+                                      gpointer user_data)
+{
+       EMMailerPrefs *prefs = user_data;
+
+       emmp_use_headers_cell_edited (prefs, USER_HEADERS_LIST_TITLE_COLUMN, path_string, new_text);
+}
+
+static void
+emmp_use_headers_cell_editing_canceled_cb (GtkCellRenderer *cell,
+                                          gpointer user_data)
+{
+       EMMailerPrefs *prefs = user_data;
+       GtkTreeSelection *selection;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       gchar *header = NULL;
+
+       selection = gtk_tree_view_get_selection (prefs->priv->user_headers_list);
+       if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+               return;
+
+       gtk_tree_model_get (model, &iter,
+               USER_HEADERS_LIST_HEADER_COLUMN, &header,
+               -1);
+
+       if (!header || !*header)
+               emmp_user_headers_remove_clicked_cb (NULL, prefs);
+
+       g_free (header);
+}
+
+static void
+emmp_user_headers_changed_cb (GSettings *settings,
+                             const gchar *key,
+                             gpointer user_data)
+{
+       EMMailerPrefs *prefs = user_data;
+       gchar **strv;
+       guint ii, added;
+
+       g_signal_handler_block (prefs->priv->user_headers_list_store, 
prefs->priv->user_headers_list_store_row_changed_id);
+
+       gtk_list_store_clear (prefs->priv->user_headers_list_store);
+       strv = g_settings_get_strv (prefs->priv->eds_settings, "camel-message-info-user-headers");
+
+       for (ii = 0, added = 0; strv && strv[ii] && added < CAMEL_UTILS_MAX_USER_HEADERS; ii++) {
+               gchar *title = NULL;
+               const gchar *header = NULL;
+
+               camel_util_decode_user_header_setting (strv[ii], &title, &header);
+
+               if (header && *header) {
+                       GtkTreeIter iter;
+
+                       added++;
+
+                       gtk_list_store_append (prefs->priv->user_headers_list_store, &iter);
+                       gtk_list_store_set (prefs->priv->user_headers_list_store, &iter,
+                               USER_HEADERS_LIST_HEADER_COLUMN, header,
+                               USER_HEADERS_LIST_TITLE_COLUMN, title,
+                               -1);
+               }
+
+               g_free (title);
+       }
+
+       g_strfreev (strv);
+
+       g_signal_handler_unblock (prefs->priv->user_headers_list_store, 
prefs->priv->user_headers_list_store_row_changed_id);
+
+       emmp_user_headers_update_buttons (prefs);
+}
+
 static void
 em_mailer_prefs_construct (EMMailerPrefs *prefs,
                            EMailSession *session,
@@ -1554,7 +1868,7 @@ em_mailer_prefs_construct (EMMailerPrefs *prefs,
                prefs->priv->entry_header,
                "activate", G_CALLBACK (emmp_header_add_header), prefs);
        /* initialise the tree with appropriate headings */
-       prefs->priv->header_list_store = gtk_list_store_newv (HEADER_LIST_N_COLUMNS, col_types);
+       prefs->priv->header_list_store = gtk_list_store_newv (HEADER_LIST_N_COLUMNS, header_list_col_types);
        prefs->priv->header_list_store_row_changed_id = g_signal_connect_swapped (
                prefs->priv->header_list_store, "row-changed",
                G_CALLBACK (emmp_shedule_save_headers), prefs);
@@ -1691,6 +2005,66 @@ em_mailer_prefs_construct (EMMailerPrefs *prefs,
        g_hash_table_destroy (default_header_hash);
        g_signal_handler_unblock (tree_model, prefs->priv->header_list_store_row_changed_id);
 
+       /* user headers */
+       prefs->priv->user_headers_add = GTK_BUTTON (e_builder_get_widget (prefs->priv->builder, 
"cmdUserHeadersAdd"));
+       prefs->priv->user_headers_edit = GTK_BUTTON (e_builder_get_widget (prefs->priv->builder, 
"cmdUserHeadersEdit"));
+       prefs->priv->user_headers_remove = GTK_BUTTON (e_builder_get_widget (prefs->priv->builder, 
"cmdUserHeadersRemove"));
+       prefs->priv->user_headers_list = GTK_TREE_VIEW (e_builder_get_widget (prefs->priv->builder, 
"treeUserHeaders"));
+
+       if (!g_settings_is_writable (prefs->priv->eds_settings, "camel-message-info-user-headers")) {
+               widget = e_builder_get_widget (prefs->priv->builder, "user-headers-alignment");
+               gtk_widget_set_sensitive (widget, FALSE);
+       }
+
+       gtk_tree_view_set_reorderable (prefs->priv->user_headers_list, TRUE);
+
+       selection = gtk_tree_view_get_selection (prefs->priv->user_headers_list);
+       g_signal_connect_swapped (
+               selection, "changed",
+               G_CALLBACK (emmp_user_headers_update_buttons), prefs);
+       prefs->priv->user_headers_list_store = gtk_list_store_newv (USER_HEADERS_LIST_N_COLUMNS, 
user_headers_list_col_types);
+       prefs->priv->user_headers_list_store_row_changed_id = g_signal_connect_swapped (
+               prefs->priv->user_headers_list_store, "row-changed",
+               G_CALLBACK (emmp_user_headers_schedule_save), prefs);
+       g_signal_connect (
+               prefs->priv->user_headers_add, "clicked",
+               G_CALLBACK (emmp_user_headers_add_clicked_cb), prefs);
+       g_signal_connect (
+               prefs->priv->user_headers_edit, "clicked",
+               G_CALLBACK (emmp_user_headers_edit_clicked_cb), prefs);
+       g_signal_connect (
+               prefs->priv->user_headers_remove, "clicked",
+               G_CALLBACK (emmp_user_headers_remove_clicked_cb), prefs);
+       gtk_tree_view_set_model (prefs->priv->user_headers_list, GTK_TREE_MODEL 
(prefs->priv->user_headers_list_store));
+
+       renderer = gtk_cell_renderer_text_new ();
+       g_object_set (renderer, "editable", TRUE, NULL);
+       gtk_tree_view_insert_column_with_attributes (
+               GTK_TREE_VIEW (prefs->priv->user_headers_list), -1,
+               _("Header"), renderer,
+               "text", USER_HEADERS_LIST_HEADER_COLUMN,
+               NULL);
+       g_signal_connect (renderer, "edited",
+               G_CALLBACK (emmp_use_headers_cell_header_edited_cb), prefs);
+       g_signal_connect (renderer, "editing-canceled",
+               G_CALLBACK (emmp_use_headers_cell_editing_canceled_cb), prefs);
+       renderer = gtk_cell_renderer_text_new ();
+       g_object_set (renderer, "editable", TRUE, NULL);
+       gtk_tree_view_insert_column_with_attributes (
+               GTK_TREE_VIEW (prefs->priv->user_headers_list), -1,
+               _("Title"), renderer,
+               "text", USER_HEADERS_LIST_TITLE_COLUMN,
+               NULL);
+       g_signal_connect (renderer, "edited",
+               G_CALLBACK (emmp_use_headers_cell_title_edited_cb), prefs);
+       g_signal_connect (renderer, "editing-canceled",
+               G_CALLBACK (emmp_use_headers_cell_editing_canceled_cb), prefs);
+
+       prefs->priv->user_headers_settings_changed_id = g_signal_connect (prefs->priv->eds_settings, 
"changed::camel-message-info-user-headers",
+               G_CALLBACK (emmp_user_headers_changed_cb), prefs);
+
+       emmp_user_headers_changed_cb (prefs->priv->eds_settings, NULL, prefs);
+
        /* date/time format */
        table = e_builder_get_widget (prefs->priv->builder, "datetime-format-table");
        /* To Translators: 'Table column' is a label for configurable date/time format for table columns 
showing a date in message list */


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