[evolution] I#1433 - Message List columns for custom headers
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] I#1433 - Message List columns for custom headers
- Date: Thu, 5 Aug 2021 08:53:24 +0000 (UTC)
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]