[evolution] I#1255 - Composer: Add plugin to validate expected sender before send
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] I#1255 - Composer: Add plugin to validate expected sender before send
- Date: Wed, 2 Dec 2020 22:08:52 +0000 (UTC)
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<tab>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]