[evolution] I#1255 - Composer: Add plugin to validate expected sender before send



commit 4752d087b372c81be5a7d85203abceff265e9298
Author: Milan Crha <mcrha redhat com>
Date:   Wed Dec 2 23:07:23 2020 +0100

    I#1255 - Composer: Add plugin to validate expected sender before send
    
    Closes https://gitlab.gnome.org/GNOME/evolution/-/issues/1255

 CMakeLists.txt                                     |   1 +
 data/CMakeLists.txt                                |   1 +
 ...volution.plugin.sender-validator.gschema.xml.in |   9 +
 po/POTFILES.in                                     |   4 +
 .../plugin-manager/evolution-plugin-manager.c      |   3 +-
 src/plugins/sender-validation/CMakeLists.txt       |  50 ++
 ...org-gnome-evolution-sender-validation.eplug.xml |   8 +
 .../org-gnome-sender-validation.error.xml          |  10 +
 src/plugins/sender-validation/sender-validation.c  | 581 +++++++++++++++++++++
 9 files changed, 666 insertions(+), 1 deletion(-)
---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b3314b344e..542431865b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -636,6 +636,7 @@ set(plugins_standard
        mailing-list-actions
        mail-notification
        save-calendar
+       sender-validation
        templates
 )
 
diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt
index 06e1e17006..dd76d93556 100644
--- a/data/CMakeLists.txt
+++ b/data/CMakeLists.txt
@@ -48,6 +48,7 @@ set(SCHEMAS
        org.gnome.evolution.plugin.mail-notification.gschema.xml
        org.gnome.evolution.plugin.prefer-plain.gschema.xml
        org.gnome.evolution.plugin.publish-calendar.gschema.xml
+       org.gnome.evolution.plugin.sender-validator.gschema.xml
        org.gnome.evolution.plugin.templates.gschema.xml
 )
 
diff --git a/data/org.gnome.evolution.plugin.sender-validator.gschema.xml.in 
b/data/org.gnome.evolution.plugin.sender-validator.gschema.xml.in
new file mode 100644
index 0000000000..f96e37f3d3
--- /dev/null
+++ b/data/org.gnome.evolution.plugin.sender-validator.gschema.xml.in
@@ -0,0 +1,9 @@
+<schemalist>
+  <schema gettext-domain="evolution" id="org.gnome.evolution.plugin.sender-validation" 
path="/org/gnome/evolution/plugin/sender-validation/">
+     <key name="assignments" type="as">
+      <_default>[]</_default>
+      <_summary>List of assignments for recipient and the address to be used with it</_summary>
+      <_description>The list is of format 'recipient&lt;tab&gt;sender', where the recipient can be part of 
the address only and the sender is a full email address, which should match the From address 
precisely.</_description>
+    </key>
+  </schema>
+</schemalist>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index ba48b258e3..9ffb348f29 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -15,6 +15,7 @@ data/org.gnome.evolution.plugin.itip.gschema.xml.in
 data/org.gnome.evolution.plugin.mail-notification.gschema.xml.in
 data/org.gnome.evolution.plugin.prefer-plain.gschema.xml.in
 data/org.gnome.evolution.plugin.publish-calendar.gschema.xml.in
+data/org.gnome.evolution.plugin.sender-validator.gschema.xml.in
 data/org.gnome.evolution.plugin.templates.gschema.xml.in
 data/org.gnome.evolution.shell.gschema.xml.in
 data/org.gnome.evolution.spamassassin.gschema.xml.in
@@ -524,6 +525,9 @@ src/plugins/save-calendar/ical-format.c
 src/plugins/save-calendar/org-gnome-save-calendar.eplug.xml
 src/plugins/save-calendar/rdf-format.c
 src/plugins/save-calendar/save-calendar.c
+src/plugins/sender-validation/org-gnome-evolution-sender-validation.eplug.xml
+src/plugins/sender-validation/org-gnome-sender-validation.error.xml
+src/plugins/sender-validation/sender-validation.c
 src/plugins/templates/org-gnome-templates.eplug.xml
 src/plugins/templates/templates.c
 src/shell/e-shell-backend.c
diff --git a/src/modules/plugin-manager/evolution-plugin-manager.c 
b/src/modules/plugin-manager/evolution-plugin-manager.c
index 024b5f9f82..bc72074be9 100644
--- a/src/modules/plugin-manager/evolution-plugin-manager.c
+++ b/src/modules/plugin-manager/evolution-plugin-manager.c
@@ -165,8 +165,9 @@ eppm_show_plugin (Manager *m,
                        }
                        gtk_label_set_label (m->items[LABEL_AUTHOR], out->str);
                        g_string_free (out, TRUE);
+                       gtk_widget_show (gtk_widget_get_parent (GTK_WIDGET (m->labels[LABEL_AUTHOR])));
                } else {
-                       eppm_set_label (m->items[LABEL_AUTHOR], NULL);
+                       gtk_widget_hide (gtk_widget_get_parent (GTK_WIDGET (m->labels[LABEL_AUTHOR])));
                }
 
                eppm_set_label (m->items[LABEL_DESCRIPTION], ep->description);
diff --git a/src/plugins/sender-validation/CMakeLists.txt b/src/plugins/sender-validation/CMakeLists.txt
new file mode 100644
index 0000000000..92e48a2bcc
--- /dev/null
+++ b/src/plugins/sender-validation/CMakeLists.txt
@@ -0,0 +1,50 @@
+add_error_files(sender-validation org-gnome-sender-validation.error)
+add_eplug_file(sender-validation org-gnome-evolution-sender-validation.eplug)
+
+set(DEPENDENCIES
+       evolution-calendar
+       evolution-mail
+       evolution-mail-composer
+       evolution-shell
+       evolution-util
+)
+
+set(SOURCES
+       sender-validation.c
+)
+
+add_library(org-gnome-evolution-sender-validation MODULE
+       ${SOURCES}
+)
+
+add_dependencies(org-gnome-evolution-sender-validation
+       ${DEPENDENCIES}
+)
+
+target_compile_definitions(org-gnome-evolution-sender-validation PRIVATE
+       -DG_LOG_DOMAIN=\"sender-validation\"
+)
+
+target_compile_options(org-gnome-evolution-sender-validation PUBLIC
+       ${EVOLUTION_DATA_SERVER_CFLAGS}
+       ${GNOME_PLATFORM_CFLAGS}
+)
+
+target_include_directories(org-gnome-evolution-sender-validation PUBLIC
+       ${CMAKE_BINARY_DIR}
+       ${CMAKE_BINARY_DIR}/src
+       ${CMAKE_SOURCE_DIR}/src
+       ${CMAKE_CURRENT_BINARY_DIR}
+       ${EVOLUTION_DATA_SERVER_INCLUDE_DIRS}
+       ${GNOME_PLATFORM_INCLUDE_DIRS}
+)
+
+target_link_libraries(org-gnome-evolution-sender-validation
+       ${DEPENDENCIES}
+       ${EVOLUTION_DATA_SERVER_LDFLAGS}
+       ${GNOME_PLATFORM_LDFLAGS}
+)
+
+install(TARGETS org-gnome-evolution-sender-validation
+       DESTINATION ${plugindir}
+)
diff --git a/src/plugins/sender-validation/org-gnome-evolution-sender-validation.eplug.xml 
b/src/plugins/sender-validation/org-gnome-evolution-sender-validation.eplug.xml
new file mode 100644
index 0000000000..07009b345e
--- /dev/null
+++ b/src/plugins/sender-validation/org-gnome-evolution-sender-validation.eplug.xml
@@ -0,0 +1,8 @@
+<e-plugin-list>
+       <e-plugin type="shlib" id="org.gnome.evolution.sender-validation" _name="Sender Validation" 
location="@PLUGINDIR@/liborg-gnome-evolution-sender-validation@SOEXT@">
+               <_description>Validates sender before sending an e-mail against a list of recipients and for 
them assigned expected send account.</_description>
+               <hook class="org.gnome.evolution.mail.events:1.0">
+                       <event id="composer.presendchecks" 
handle="org_gnome_evolution_sender_validation_presendchecks" target="message" />
+               </hook>
+       </e-plugin>
+</e-plugin-list>
diff --git a/src/plugins/sender-validation/org-gnome-sender-validation.error.xml 
b/src/plugins/sender-validation/org-gnome-sender-validation.error.xml
new file mode 100644
index 0000000000..f98b338215
--- /dev/null
+++ b/src/plugins/sender-validation/org-gnome-sender-validation.error.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<error-list domain="org.gnome.evolution.plugins.sender-validation" default="GTK_RESPONSE_YES">
+       <error id="sender-validation" type="info">
+               <_primary>Confirm sender address</_primary>
+               <_secondary>The message contains recipient “{0}” for which it is set up to use sender account 
“{1}”, but the account “{2}” is used instead.</_secondary>
+               <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL" _label="_Edit Message"/>
+               <button response="GTK_RESPONSE_YES" _label="_Send Anyway"></button>
+       </error>
+</error-list>
diff --git a/src/plugins/sender-validation/sender-validation.c 
b/src/plugins/sender-validation/sender-validation.c
new file mode 100644
index 0000000000..f02a04ae4f
--- /dev/null
+++ b/src/plugins/sender-validation/sender-validation.c
@@ -0,0 +1,581 @@
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evolution-config.h"
+
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+
+#include <camel/camel.h>
+
+#include "e-util/e-util.h"
+#include "composer/e-msg-composer.h"
+#include "gui/itip-utils.h"
+#include "mail/em-event.h"
+#include "shell/e-shell.h"
+
+#define CONF_KEY_NAME "assignments"
+
+gint           e_plugin_lib_enable                     (EPlugin *ep,
+                                                        gint enable);
+GtkWidget *    e_plugin_lib_get_configure_widget       (EPlugin *plugin);
+void           org_gnome_evolution_sender_validation_presendchecks
+                                                       (EPlugin *ep,
+                                                        EMEventTargetComposer *tt);
+
+static gboolean plugin_enabled = TRUE;
+
+gint
+e_plugin_lib_enable (EPlugin *ep,
+                     gint enable)
+{
+       plugin_enabled = enable != 0;
+
+       return 0;
+}
+
+typedef struct _Assignment {
+       const gchar *recipient;
+       const gchar *account;
+} Assignment;
+
+static void
+e_sender_validation_free_assignment (gpointer ptr)
+{
+       if (!ptr)
+               return;
+
+       g_slice_free (Assignment, ptr);
+}
+
+/* (transfer full): Internal data uses strings from in_assignments */
+static GSList *
+e_sender_validation_parse_assignments (gchar **in_assignments)
+{
+       GSList *items = NULL;
+       guint ii;
+
+       if (!in_assignments || !*in_assignments)
+               return NULL;
+
+       for (ii = 0; in_assignments[ii]; ii++) {
+               Assignment *assignment;
+               gchar *value = in_assignments[ii];
+               gchar *tab;
+
+               tab = strchr (value, '\t');
+               if (!tab || tab == value || !tab[1])
+                       continue;
+
+               *tab = '\0';
+
+               assignment = g_slice_new (Assignment);
+               assignment->recipient = value;
+               assignment->account = tab + 1;
+
+               items = g_slist_prepend (items, assignment);
+       }
+
+       return g_slist_reverse (items);
+}
+
+static gboolean
+e_sender_validation_ask (GtkWindow *window,
+                        const gchar *recipient,
+                        const gchar *expected_account,
+                        const gchar *used_account)
+{
+       gint response;
+
+       response = e_alert_run_dialog_for_args (window,
+               "org.gnome.evolution.plugins.sender-validation:sender-validation",
+               recipient, expected_account, used_account,
+               NULL);
+
+       return response == GTK_RESPONSE_YES;
+}
+
+static gboolean
+e_sender_validation_check (EMsgComposer *composer)
+{
+       GSettings *settings;
+       GSList *assignments; /* Assignment * */
+       gchar **strv;
+       gboolean can_send = TRUE;
+
+       g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
+
+       settings = e_util_ref_settings ("org.gnome.evolution.plugin.sender-validation");
+       strv = g_settings_get_strv (settings, CONF_KEY_NAME);
+       g_clear_object (&settings);
+
+       assignments = e_sender_validation_parse_assignments (strv);
+
+       if (assignments) {
+               EComposerHeaderTable *header_table;
+               const gchar *from_address;
+
+               header_table = e_msg_composer_get_header_table (composer);
+               from_address = e_composer_header_table_get_from_address (header_table);
+
+               if (from_address && *from_address) {
+                       EDestination **destinations;
+                       guint ii;
+
+                       destinations = e_composer_header_table_get_destinations (header_table);
+
+                       for (ii = 0; destinations && destinations[ii]; ii++) {
+                               EDestination *dest = destinations[ii];
+                               const gchar *recipient;
+
+                               recipient = e_destination_get_address (dest);
+
+                               if (recipient && *recipient) {
+                                       const Assignment *has_mismatch = NULL;
+                                       gboolean has_match = FALSE;
+                                       GSList *link;
+
+                                       for (link = assignments; link && !has_match; link = g_slist_next 
(link)) {
+                                               const Assignment *assignment = link->data;
+
+                                               if (camel_strstrcase (recipient, assignment->recipient)) {
+                                                       if (camel_strstrcase (from_address, 
assignment->account)) {
+                                                               has_match = TRUE;
+                                                       } else if (!has_mismatch) {
+                                                               has_mismatch = assignment;
+                                                       }
+                                               }
+                                       }
+
+                                       if (!has_match && has_mismatch) {
+                                               can_send = e_sender_validation_ask (GTK_WINDOW (composer), 
recipient, has_mismatch->account, from_address);
+                                               break;
+                                       }
+                               }
+                       }
+
+                       e_destination_freev (destinations);
+               }
+       }
+
+       g_slist_free_full (assignments, e_sender_validation_free_assignment);
+       /* Can free 'strv' only after 'assignments', because 'assignments' is using the memory from 'strv' */
+       g_strfreev (strv);
+
+       return can_send;
+}
+
+void
+org_gnome_evolution_sender_validation_presendchecks (EPlugin *ep,
+                                                    EMEventTargetComposer *tt)
+{
+       if (plugin_enabled &&
+           !e_sender_validation_check (tt->composer))
+               g_object_set_data (G_OBJECT (tt->composer), "presend_check_status", GINT_TO_POINTER (1));
+}
+
+enum {
+       RECIPIENT_COLUMN,
+       ACCOUNT_COLUMN,
+       N_COLUMNS
+};
+
+typedef struct {
+       GSettings *settings;
+       GtkWidget *treeview;
+       GtkWidget *button_add;
+       GtkWidget *button_edit;
+       GtkWidget *button_remove;
+       GtkListStore *store;
+} UIData;
+
+static void
+commit_changes (UIData *ui)
+{
+       GtkTreeModel *model = NULL;
+       GVariantBuilder builder;
+       GVariant *var;
+       GtkTreeIter iter;
+       gboolean valid;
+
+       model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
+       valid = gtk_tree_model_get_iter_first (model, &iter);
+
+       g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+
+       while (valid) {
+               gchar *recipient = NULL, *account = NULL;
+
+               gtk_tree_model_get (model, &iter,
+                       RECIPIENT_COLUMN, &recipient,
+                       ACCOUNT_COLUMN, &account,
+                       -1);
+
+               if (recipient && g_utf8_strlen (g_strstrip (recipient), -1) > 0 &&
+                   account && g_utf8_strlen (g_strstrip (account), -1) > 0) {
+                       gchar *value;
+
+                       value = g_strconcat (recipient, "\t", account, NULL);
+
+                       g_variant_builder_add (&builder, "s", value);
+
+                       g_free (value);
+               }
+
+               g_free (recipient);
+               g_free (account);
+
+               valid = gtk_tree_model_iter_next (model, &iter);
+       }
+
+       /* A floating GVariant is returned, which is consumed by the g_settings_set_value() */
+       var = g_variant_builder_end (&builder);
+       g_settings_set_value (ui->settings, CONF_KEY_NAME, var);
+}
+
+static void
+column_edited (UIData *ui,
+              gint column,
+              const gchar *path_string,
+              const gchar *new_text)
+{
+       GtkTreeIter iter;
+
+       gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (ui->store), &iter, path_string);
+
+       gtk_list_store_set (ui->store, &iter, column, new_text, -1);
+       commit_changes (ui);
+}
+
+static void
+recipient_edited_cb (GtkCellRendererText *cell,
+                    const gchar *path_string,
+                    const gchar *new_text,
+                    UIData *ui)
+{
+       column_edited (ui, RECIPIENT_COLUMN, path_string, new_text);
+}
+
+static void
+account_edited_cb (GtkCellRendererText *cell,
+                  const gchar *path_string,
+                  const gchar *new_text,
+                  UIData *ui)
+{
+       column_edited (ui, ACCOUNT_COLUMN, path_string, new_text);
+}
+
+static void
+button_add_clicked (GtkButton *button,
+                   UIData *ui)
+{
+       GtkTreeModel *model;
+       GtkTreeView *tree_view;
+       GtkTreeViewColumn *column;
+       GtkTreePath *path;
+       GtkTreeIter iter;
+
+       tree_view = GTK_TREE_VIEW (ui->treeview);
+       model = gtk_tree_view_get_model (tree_view);
+
+       gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+
+       path = gtk_tree_model_get_path (model, &iter);
+       column = gtk_tree_view_get_column (tree_view, RECIPIENT_COLUMN);
+       gtk_tree_view_set_cursor (tree_view, path, column, TRUE);
+       gtk_tree_view_row_activated (tree_view, path, column);
+       gtk_tree_path_free (path);
+}
+
+static void
+button_remove_clicked (GtkButton *button,
+                      UIData *ui)
+{
+       GtkTreeSelection *selection;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       GtkTreePath *path;
+       gboolean valid;
+       gint len;
+
+       valid = FALSE;
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
+       if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+               return;
+
+       /* Get the path and move to the previous node */
+       path = gtk_tree_model_get_path (model, &iter);
+       if (path)
+               valid = gtk_tree_path_prev (path);
+
+       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+
+       len = gtk_tree_model_iter_n_children (model, NULL);
+       if (len > 0) {
+               if (gtk_list_store_iter_is_valid (GTK_LIST_STORE (model), &iter)) {
+                       gtk_tree_selection_select_iter (selection, &iter);
+               } else {
+                       if (path && valid) {
+                               gtk_tree_model_get_iter (model, &iter, path);
+                               gtk_tree_selection_select_iter (selection, &iter);
+                       }
+               }
+       } else {
+               gtk_widget_set_sensitive (ui->button_edit, FALSE);
+               gtk_widget_set_sensitive (ui->button_remove, FALSE);
+       }
+
+       gtk_widget_grab_focus (ui->treeview);
+       gtk_tree_path_free (path);
+
+       commit_changes (ui);
+}
+
+static void
+button_edit_clicked (GtkButton *button,
+                    UIData *ui)
+{
+       GtkTreeSelection *selection;
+       GtkTreeModel *model;
+       GtkTreePath *path;
+       GtkTreeIter iter;
+       GtkTreeViewColumn *focus_col;
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
+       if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+               return;
+
+       focus_col = gtk_tree_view_get_column (GTK_TREE_VIEW (ui->treeview), RECIPIENT_COLUMN);
+       path = gtk_tree_model_get_path (model, &iter);
+
+       if (path) {
+               gtk_tree_view_set_cursor (GTK_TREE_VIEW (ui->treeview), path, focus_col, TRUE);
+               gtk_tree_path_free (path);
+       }
+}
+
+static void
+selection_changed_cb (GtkTreeSelection *selection,
+                     UIData *ui)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+
+       if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+               gtk_widget_set_sensitive (ui->button_edit, TRUE);
+               gtk_widget_set_sensitive (ui->button_remove, TRUE);
+       } else {
+               gtk_widget_set_sensitive (ui->button_edit, FALSE);
+               gtk_widget_set_sensitive (ui->button_remove, FALSE);
+       }
+}
+
+static void
+destroy_ui_data (gpointer data)
+{
+       UIData *ui = (UIData *) data;
+
+       if (!ui)
+               return;
+
+       g_object_unref (ui->settings);
+       g_slice_free (UIData, ui);
+}
+
+static void
+e_sender_validation_fill_accounts (GtkCellRendererCombo *renderer)
+{
+       EShell *shell;
+       GtkListStore *store = NULL;
+       CamelInternetAddress *address;
+       gchar **identities;
+       guint ii;
+
+       shell = e_shell_get_default ();
+       if (!shell)
+               return;
+
+       address = camel_internet_address_new ();
+       identities = itip_get_user_identities (e_shell_get_registry (shell));
+
+       for (ii = 0; identities && identities[ii]; ii++) {
+               const gchar *identity = identities[ii];
+               guint len;
+
+               while (len = camel_address_length (CAMEL_ADDRESS (address)), len > 0)
+                       camel_address_remove (CAMEL_ADDRESS (address), len - 1);
+
+               if (camel_address_unformat (CAMEL_ADDRESS (address), identity) > 0) {
+                       const gchar *email;
+                       guint jj;
+
+                       for (jj = 0; camel_internet_address_get (address, jj, NULL, &email); jj++) {
+                               if (email && *email) {
+                                       GtkTreeIter iter;
+
+                                       if (!store)
+                                               store = gtk_list_store_new (1, G_TYPE_STRING);
+
+                                       gtk_list_store_append (store, &iter);
+                                       gtk_list_store_set (store, &iter, 0, email, -1);
+                               }
+                       }
+               }
+       }
+
+       g_clear_object (&address);
+       g_strfreev (identities);
+
+       if (store) {
+               g_object_set (G_OBJECT (renderer),
+                       "has-entry", TRUE,
+                       "model", store,
+                       "text-column", 0,
+                       NULL);
+
+               g_object_unref (store);
+       }
+}
+
+GtkWidget *
+e_plugin_lib_get_configure_widget (EPlugin *plugin)
+{
+       GtkCellRenderer *renderer;
+       GtkTreeSelection *selection;
+       GtkTreeIter iter;
+       GtkWidget *hbox;
+       GtkWidget *vbox;
+       GtkWidget *container;
+       GtkWidget *scrolledwindow;
+       GtkWidget *vbuttonbox;
+       GSList *assignments, *link;
+       UIData *ui;
+       gchar **strv;
+
+       ui = g_slice_new0 (UIData);
+
+       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+       gtk_widget_show (vbox);
+       gtk_widget_set_size_request (vbox, 385, 189);
+
+       container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+       gtk_widget_show (container);
+       gtk_box_pack_start (GTK_BOX (vbox), container, TRUE, TRUE, 0);
+
+       scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
+       gtk_widget_show (scrolledwindow);
+       gtk_box_pack_start (GTK_BOX (container), scrolledwindow, TRUE, TRUE, 0);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_AUTOMATIC, 
GTK_POLICY_AUTOMATIC);
+
+       ui->treeview = gtk_tree_view_new ();
+       gtk_widget_show (ui->treeview);
+       gtk_container_add (GTK_CONTAINER (scrolledwindow), ui->treeview);
+       gtk_container_set_border_width (GTK_CONTAINER (ui->treeview), 1);
+       gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ui->treeview), TRUE);
+       gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW (ui->treeview), FALSE);
+
+       vbuttonbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
+       gtk_widget_show (vbuttonbox);
+       gtk_box_pack_start (GTK_BOX (container), vbuttonbox, FALSE, TRUE, 0);
+       gtk_button_box_set_layout (GTK_BUTTON_BOX (vbuttonbox), GTK_BUTTONBOX_START);
+       gtk_box_set_spacing (GTK_BOX (vbuttonbox), 6);
+
+       ui->button_add = e_dialog_button_new_with_icon ("list-add", _("_Add"));
+       gtk_widget_show (ui->button_add);
+       gtk_container_add (GTK_CONTAINER (vbuttonbox), ui->button_add);
+       gtk_widget_set_can_default (ui->button_add, TRUE);
+
+       ui->button_edit = gtk_button_new_with_mnemonic (_("_Edit"));
+       gtk_widget_show (ui->button_edit);
+       gtk_container_add (GTK_CONTAINER (vbuttonbox), ui->button_edit);
+       gtk_widget_set_can_default (ui->button_edit, TRUE);
+
+       ui->button_remove = e_dialog_button_new_with_icon ("list-remove", _("_Remove"));
+       gtk_widget_show (ui->button_remove);
+       gtk_container_add (GTK_CONTAINER (vbuttonbox), ui->button_remove);
+       gtk_widget_set_can_default (ui->button_remove, TRUE);
+
+       ui->settings = e_util_ref_settings ("org.gnome.evolution.plugin.sender-validation");
+       ui->store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
+
+       gtk_tree_view_set_model (GTK_TREE_VIEW (ui->treeview), GTK_TREE_MODEL (ui->store));
+
+       renderer = gtk_cell_renderer_text_new ();
+       gtk_tree_view_insert_column_with_attributes (
+               GTK_TREE_VIEW (ui->treeview), -1, _("Recipient Contains"),
+               renderer, "text", RECIPIENT_COLUMN, NULL);
+       g_object_set (renderer, "editable", TRUE, NULL);
+       g_signal_connect (
+               renderer, "edited",
+               G_CALLBACK (recipient_edited_cb), ui);
+
+       renderer = gtk_cell_renderer_combo_new ();
+       e_sender_validation_fill_accounts (GTK_CELL_RENDERER_COMBO (renderer));
+       gtk_tree_view_insert_column_with_attributes (
+               GTK_TREE_VIEW (ui->treeview), -1, _("Account to Use"),
+               renderer, "text", ACCOUNT_COLUMN, NULL);
+       g_object_set (renderer, "editable", TRUE, NULL);
+       g_signal_connect (
+               renderer, "edited",
+               G_CALLBACK (account_edited_cb), ui);
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
+       gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+       g_signal_connect (
+               selection, "changed",
+               G_CALLBACK (selection_changed_cb), ui);
+       gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ui->treeview), TRUE);
+
+       g_signal_connect (
+               ui->button_add, "clicked",
+               G_CALLBACK (button_add_clicked), ui);
+
+       g_signal_connect (
+               ui->button_remove, "clicked",
+               G_CALLBACK (button_remove_clicked), ui);
+       gtk_widget_set_sensitive (ui->button_remove, FALSE);
+
+       g_signal_connect (
+               ui->button_edit, "clicked",
+               G_CALLBACK (button_edit_clicked), ui);
+       gtk_widget_set_sensitive (ui->button_edit, FALSE);
+
+       strv = g_settings_get_strv (ui->settings, CONF_KEY_NAME);
+       assignments = e_sender_validation_parse_assignments (strv);
+
+       for (link = assignments; link; link = g_slist_next (link)) {
+               const Assignment *assignment = link->data;
+
+               gtk_list_store_append (ui->store, &iter);
+               gtk_list_store_set (ui->store, &iter,
+                       RECIPIENT_COLUMN, assignment->recipient,
+                       ACCOUNT_COLUMN, assignment->account,
+                       -1);
+       }
+
+       g_slist_free_full (assignments, e_sender_validation_free_assignment);
+       g_strfreev (strv);
+
+       hbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+       gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+
+       /* to let free data properly on destroy of configuration widget */
+       g_object_set_data_full (G_OBJECT (hbox), "myui-data", ui, destroy_ui_data);
+
+       return hbox;
+}


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