evolution r35780 - in trunk: . composer mail plugins/templates po



Author: abharath
Date: Sun Jul 20 18:26:42 2008
New Revision: 35780
URL: http://svn.gnome.org/viewvc/evolution?rev=35780&view=rev

Log:
2008-07-18  Bharath Acharya  <abharath novell com>

        ** Fixes Bug #200147

        Basic functionality implemented by Diego Escalante Urrelo 
        <diegoe gnome org> Everyone owes him a big mug of Beer for that.

        ** Added Templates plugin
        * Makefile.am:
        * apps-evolution-template-placeholders.schemas.in:
        * org-gnome-templates.eplug.xml:
        * templates.c: 
        * templates.glade:



Added:
   trunk/plugins/templates/
   trunk/plugins/templates/ChangeLog
   trunk/plugins/templates/Makefile.am
   trunk/plugins/templates/apps-evolution-template-placeholders.schemas.in
   trunk/plugins/templates/org-gnome-templates.eplug.xml
   trunk/plugins/templates/templates.c
   trunk/plugins/templates/templates.glade
Modified:
   trunk/ChangeLog
   trunk/composer/ChangeLog
   trunk/composer/evolution-composer.ui
   trunk/configure.in
   trunk/mail/ChangeLog
   trunk/mail/em-composer-utils.c
   trunk/mail/em-composer-utils.h
   trunk/mail/em-folder-tree.c
   trunk/mail/em-folder-utils.c
   trunk/mail/em-folder-view.c
   trunk/mail/em-utils.c
   trunk/mail/em-utils.h
   trunk/mail/mail-component.c
   trunk/mail/mail-component.h
   trunk/po/ChangeLog
   trunk/po/POTFILES.in

Modified: trunk/composer/evolution-composer.ui
==============================================================================
--- trunk/composer/evolution-composer.ui	(original)
+++ trunk/composer/evolution-composer.ui	Sun Jul 20 18:26:42 2008
@@ -9,6 +9,7 @@
         <menuitem action='save'/>
         <menuitem action='save-as'/>
         <menuitem action='save-draft'/>
+	<placeholder name='template-holder'/>
         <separator/>
         <menuitem action='print-preview'/>
         <menuitem action='print'/>

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Sun Jul 20 18:26:42 2008
@@ -1736,7 +1736,7 @@
 plugins_base="$plugins_base_always $SA_JUNK_PLUGIN $BF_JUNK_PLUGIN $EXCHANGE_PLUGIN $MONO_PLUGIN " 
 all_plugins_base="$plugins_base_always sa-junk-plugin bogo-junk-plugin exchange-operations mono"
 
-plugins_standard_always="bbdb subject-thread save-calendar select-one-source copy-tool mail-to-task mark-calendar-offline audio-inline mailing-list-actions default-mailer import-ics-attachments prefer-plain mail-notification attachment-reminder face backup-restore email-custom-header" 
+plugins_standard_always="bbdb subject-thread save-calendar select-one-source copy-tool mail-to-task mark-calendar-offline audio-inline mailing-list-actions default-mailer import-ics-attachments prefer-plain mail-notification attachment-reminder face backup-restore email-custom-header templates" 
 
 plugins_standard="$plugins_standard_always"
 all_plugins_standard="$plugins_standard"
@@ -2046,6 +2046,7 @@
 plugins/import-ics-attachments/Makefile
 plugins/imap-features/Makefile
 plugins/tnef-attachments/Makefile
+plugins/templates/Makefile
 plugins/face/Makefile
 plugins/external-editor/Makefile
 smime/Makefile

Modified: trunk/mail/em-composer-utils.c
==============================================================================
--- trunk/mail/em-composer-utils.c	(original)
+++ trunk/mail/em-composer-utils.c	Sun Jul 20 18:26:42 2008
@@ -68,6 +68,8 @@
 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
 #endif
 
+#define GCONF_KEY_TEMPLATE_PLACEHOLDERS "/apps/evolution/mail/template_placeholders"
+
 static EAccount * guess_account (CamelMimeMessage *message, CamelFolder *folder);
 
 struct emcs_t {
@@ -808,8 +810,144 @@
 {
 	EMsgComposer *composer;
 
+	/* Template specific code follows. */
+	if (em_utils_folder_is_templates(drafts, NULL) == TRUE) {
+		/* retrieve the message from the CamelFolder */
+		CamelDataWrapper *content;
+		CamelStream *mem;
+		CamelContentType *type;
+		CamelMimePart *mime_part = CAMEL_MIME_PART (message);
+		CamelDataWrapper *mail_text;
+		CamelMultipart *body = camel_multipart_new ();
+		CamelStream *stream;		
+		CamelMimePart *part;
+		int count1 = 0, string_changed = 0;
+
+		char *str, *convert_str = NULL;
+		gsize bytes_read, bytes_written;
+		gint count = 2;
+
+		content = camel_medium_get_content_object ((CamelMedium *) message);
+		if (!content)
+			return;
+
+		/*
+		 * Get non-multipart content from multipart message.
+		 */
+		while (CAMEL_IS_MULTIPART (content) && count > 0)
+		{
+			mime_part = camel_multipart_get_part (CAMEL_MULTIPART (content), 0);
+			content = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+			count--;
+		}
+
+		if (!mime_part)
+			return;
+
+		type = camel_mime_part_get_content_type (mime_part);
+		if (!camel_content_type_is (type, "text", "plain"))
+			return;
+
+		mem = camel_stream_mem_new ();
+		camel_data_wrapper_decode_to_stream (content, mem);
+
+		str = g_strndup ((const gchar*)((CamelStreamMem *) mem)->buffer->data, ((CamelStreamMem *) mem)->buffer->len);
+		camel_object_unref (mem);
+
+		const char *cur = str;
+		int i;
+		for (i = 0; i < strlen(str); i++) {
+			if (!g_ascii_strncasecmp (cur, "$", 1)) {
+				const char *end = cur, *check_env;
+				char *out;
+				GConfClient *gconf;
+				GSList *clue_list = NULL, *list;
+
+				gconf = gconf_client_get_default ();
+
+				while (*end && !isspace (*end) && (*end != '"'))
+					end++;
+
+				out = g_strndup ((const gchar *) cur, end - cur);
+				check_env = out;
+
+				char **temp_str = g_strsplit (str, out, 2);
+
+				/* Get the list from gconf */
+				clue_list = gconf_client_get_list ( gconf, GCONF_KEY_TEMPLATE_PLACEHOLDERS, GCONF_VALUE_STRING, NULL );
+
+				g_object_unref (gconf);
+
+				for (list = clue_list; list; list = g_slist_next (list)) {
+					char **temp = g_strsplit (list->data, "=", 2);
+					if (!g_ascii_strcasecmp(temp[0], out+1)) {
+						str = g_strdup_printf("%s%s%s", temp_str[0], temp[1], temp_str[1]);
+						cur = str + i;
+						count1 = 1;
+						string_changed = 1;
+					}
+					else
+						count1 = 0;
+					g_strfreev(temp);
+				}
+
+				if (clue_list) {
+					g_slist_foreach (clue_list, (GFunc) g_free, NULL);
+					g_slist_free (clue_list);
+				}
+
+				if (!count1) {
+					if (getenv(out+1)) {
+						str = g_strdup_printf("%s%s%s", temp_str[0], getenv(out + 1), temp_str[1]);
+						cur = str + i;
+						count1 = 1;
+						string_changed = 1;
+					}
+					else
+						count1 = 0;
+				}
+
+				g_strfreev(temp_str);
+			}	
+			else 
+				cur++;
+		}
+
+		if (string_changed) {
+
+			/* Create toplevel container */
+			camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (body),
+					"multipart/alternative;");
+			camel_multipart_set_boundary (body, NULL);
+
+			stream = camel_stream_mem_new ();
+
+			mail_text = camel_data_wrapper_new ();
+			camel_data_wrapper_set_mime_type_field (mail_text, type);
+			
+			camel_stream_printf (stream, "%s", g_strdup(str));
+
+			camel_data_wrapper_construct_from_stream (mail_text, stream);
+			camel_object_unref (stream);
+
+			part = camel_mime_part_new ();
+			camel_medium_set_content_object (CAMEL_MEDIUM (part), mail_text);
+			camel_object_unref (mail_text);
+			camel_multipart_add_part (body, part);
+			camel_object_unref (part);
+
+			/* Finish creating the message */
+			camel_medium_set_content_object (CAMEL_MEDIUM (message), CAMEL_DATA_WRAPPER(body));
+			camel_object_unref (body);
+		}
+	}
+		
 	composer = e_msg_composer_new_with_message (message);
-	em_composer_utils_setup_callbacks (composer, NULL, NULL, 0, 0, drafts, uid);
+	
+	if (em_utils_folder_is_templates(drafts, NULL) == TRUE) 
+		em_composer_utils_setup_callbacks (composer, NULL, NULL, 0, 0, NULL, NULL);
+	else
+		em_composer_utils_setup_callbacks (composer, NULL, NULL, 0, 0, drafts, uid);
 
 	composer_set_no_change (composer, TRUE);
 
@@ -819,16 +957,20 @@
 /**
  * em_utils_edit_message:
  * @message: message to edit
+ * @folder: used to recognize the templates folder
  *
  * Opens a composer filled in with the headers/mime-parts/etc of
  * @message.
  **/
 void
-em_utils_edit_message (CamelMimeMessage *message)
+em_utils_edit_message (CamelMimeMessage *message, CamelFolder *folder)
 {
 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
 
-	edit_message (message, NULL, NULL);
+	if (folder)
+		edit_message (message, folder, NULL);
+	else
+		edit_message (message, NULL, NULL);	
 }
 
 static void

Modified: trunk/mail/em-composer-utils.h
==============================================================================
--- trunk/mail/em-composer-utils.h	(original)
+++ trunk/mail/em-composer-utils.h	Sun Jul 20 18:26:42 2008
@@ -51,7 +51,7 @@
 void em_utils_post_to_folder (struct _CamelFolder *folder);
 void em_utils_post_to_url (const char *url);
 
-void em_utils_edit_message (struct _CamelMimeMessage *message);
+void em_utils_edit_message (struct _CamelMimeMessage *message, struct _CamelFolder *folder);
 void em_utils_edit_messages (struct _CamelFolder *folder, GPtrArray *uids, gboolean replace);
 
 void em_utils_forward_attached (struct _CamelFolder *folder, GPtrArray *uids, const char *fromuri);

Modified: trunk/mail/em-folder-tree.c
==============================================================================
--- trunk/mail/em-folder-tree.c	(original)
+++ trunk/mail/em-folder-tree.c	Sun Jul 20 18:26:42 2008
@@ -1087,7 +1087,7 @@
 static gboolean
 is_special_local_folder (const char *name)
 {
-	return (!strcmp (name, "Drafts") || !strcmp (name, "Inbox") || !strcmp (name, "Outbox") || !strcmp (name, "Sent"));
+	return (!strcmp (name, "Drafts") || !strcmp (name, "Inbox") || !strcmp (name, "Outbox") || !strcmp (name, "Sent") || !strcmp (name, "Templates"));
 }
 
 static GdkAtom

Modified: trunk/mail/em-folder-utils.c
==============================================================================
--- trunk/mail/em-folder-utils.c	(original)
+++ trunk/mail/em-folder-utils.c	Sun Jul 20 18:26:42 2008
@@ -81,7 +81,7 @@
 static gboolean
 emfu_is_special_local_folder (const char *name)
 {
-	return (!strcmp (name, "Drafts") || !strcmp (name, "Inbox") || !strcmp (name, "Outbox") || !strcmp (name, "Sent"));
+	return (!strcmp (name, "Drafts") || !strcmp (name, "Inbox") || !strcmp (name, "Outbox") || !strcmp (name, "Sent") || !strcmp (name, "Templates"));
 }
 
 struct _EMCopyFolders {

Modified: trunk/mail/em-folder-view.c
==============================================================================
--- trunk/mail/em-folder-view.c	(original)
+++ trunk/mail/em-folder-view.c	Sun Jul 20 18:26:42 2008
@@ -520,6 +520,7 @@
 	}
 
         if (em_utils_folder_is_drafts(emfv->folder, emfv->folder_uri)
+	    || em_utils_folder_is_templates(emfv->folder, emfv->folder_uri) 	
 	    || em_utils_folder_is_outbox(emfv->folder, emfv->folder_uri)) {
 		em_utils_edit_messages(emfv->folder, uids, TRUE);
 		return uids->len;
@@ -2647,7 +2648,7 @@
 	/* Ignore double-clicks on columns that handle thier own state */
 	if (MESSAGE_LIST_COLUMN_IS_ACTIVE (col))
 		return;
-
+	
 	em_folder_view_open_selected(emfv);
 }
 

Modified: trunk/mail/em-utils.c
==============================================================================
--- trunk/mail/em-utils.c	(original)
+++ trunk/mail/em-utils.c	Sun Jul 20 18:26:42 2008
@@ -1364,7 +1364,53 @@
 	return path;
 }
 
+/** em_utils_folder_is_templates:
+ * @folder: folder
+ * @uri: uri for this folder, if known
+ *
+ * Decides if @folder is a Templates folder.
+ *
+ * Returns %TRUE if this is a Drafts folder or %FALSE otherwise.
+ **/
+
+gboolean
+em_utils_folder_is_templates (CamelFolder *folder, const char *uri)
+{
+	EAccountList *accounts;
+	EAccount *account;
+	EIterator *iter;
+	int is = FALSE;
+	char *templates_uri;
 
+	if (folder == mail_component_get_folder (NULL, MAIL_COMPONENT_FOLDER_TEMPLATES))
+		return TRUE;
+	
+	if (uri == NULL)
+		return FALSE;
+		
+	accounts = mail_config_get_accounts();
+	iter = e_list_get_iterator ((EList *)accounts);
+	while (e_iterator_is_valid (iter)) {
+		account = (EAccount *)e_iterator_get (iter);
+		
+		if (account->templates_folder_uri) {
+			templates_uri = em_uri_to_camel (account->templates_folder_uri);
+			if (camel_store_folder_uri_equal (folder->parent_store, templates_uri, uri)) {
+				g_free (templates_uri);
+				is = TRUE;
+				break;
+			}
+			g_free (templates_uri);
+		}
+		
+		e_iterator_next (iter);
+	}
+	
+	g_object_unref (iter);
+	
+	return is;
+}
+		
 /**
  * em_utils_folder_is_drafts:
  * @folder: folder

Modified: trunk/mail/em-utils.h
==============================================================================
--- trunk/mail/em-utils.h	(original)
+++ trunk/mail/em-utils.h	Sun Jul 20 18:26:42 2008
@@ -82,6 +82,7 @@
 void em_utils_save_parts (struct _GtkWidget *parent, const char *prompt, GSList * parts);
 
 gboolean em_utils_folder_is_drafts(struct _CamelFolder *folder, const char *uri);
+gboolean em_utils_folder_is_templates(struct _CamelFolder *folder, const char *uri);
 gboolean em_utils_folder_is_sent(struct _CamelFolder *folder, const char *uri);
 gboolean em_utils_folder_is_outbox(struct _CamelFolder *folder, const char *uri);
 

Modified: trunk/mail/mail-component.c
==============================================================================
--- trunk/mail/mail-component.c	(original)
+++ trunk/mail/mail-component.c	Sun Jul 20 18:26:42 2008
@@ -161,6 +161,7 @@
 	{ N_("Drafts"), },
 	{ N_("Outbox"), },
 	{ N_("Sent"), },
+	{ N_("Templates"), },
 	{ "Inbox", },		/* 'always local' inbox */
 };
 

Modified: trunk/mail/mail-component.h
==============================================================================
--- trunk/mail/mail-component.h	(original)
+++ trunk/mail/mail-component.h	Sun Jul 20 18:26:42 2008
@@ -47,6 +47,7 @@
 	MAIL_COMPONENT_FOLDER_DRAFTS,
 	MAIL_COMPONENT_FOLDER_OUTBOX,
 	MAIL_COMPONENT_FOLDER_SENT,
+	MAIL_COMPONENT_FOLDER_TEMPLATES,
 	MAIL_COMPONENT_FOLDER_LOCAL_INBOX,
 };
 

Added: trunk/plugins/templates/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/plugins/templates/Makefile.am	Sun Jul 20 18:26:42 2008
@@ -0,0 +1,40 @@
+INCLUDES =						\
+	-I$(top_srcdir)					\
+	-I$(top_builddir)/composer			\
+	$(EVOLUTION_MAIL_CFLAGS)			\
+	-DEVOLUTION_GLADEDIR=\""$(gladedir)"\"		\
+	-DEVOLUTION_PLUGINDIR="\"$(plugindir)\""
+
+ EVO_PLUGIN_RULE@
+
+plugin_DATA = 					\
+		org-gnome-templates.eplug	\
+		templates.glade
+
+plugin_LTLIBRARIES = liborg-gnome-templates.la
+
+liborg_gnome_templates_la_SOURCES = templates.c
+liborg_gnome_templates_la_LDFLAGS = -module -avoid-version
+
+schemadir       = $(GCONF_SCHEMA_FILE_DIR)
+schema_in_files = apps-evolution-template-placeholders.schemas.in
+schema_DATA     = $(schema_in_files:.schemas.in=.schemas)
+
+ INTLTOOL_SCHEMAS_RULE@
+
+install-data-local:
+	if test -z "$(DESTDIR)" ; then                                                                                          \
+		for p in $(schema_DATA) ; do                                                                                    \
+			GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $$p;             \
+		done                                                                                                            \
+	fi
+
+EXTRA_DIST = 	org-gnome-templates.eplug.xml	\
+		$(schema_in_files)	   	\
+		templates.glade
+
+BUILT_SOURCES = org-gnome-templates.eplug
+
+CLEANFILES = $(BUILT_SOURCES)
+
+DISTCLEANFILES = $(schema_DATA)

Added: trunk/plugins/templates/apps-evolution-template-placeholders.schemas.in
==============================================================================
--- (empty file)
+++ trunk/plugins/templates/apps-evolution-template-placeholders.schemas.in	Sun Jul 20 18:26:42 2008
@@ -0,0 +1,26 @@
+<gconfschemafile>
+  <schemalist>
+    <schema>
+      <key>/schemas/apps/evolution/mail/template_placeholders</key>
+      <applyto>/apps/evolution/mail/template_placeholders</applyto>
+      <owner>evolution-mail</owner>
+      <type>list</type>
+      <list_type>string</list_type>
+
+      <!-- The following are the keyword/value pairs used by the plugin to 
+      substitute the messages stored under the Templates folder. The list can 
+      have any number of such pairs.-->
+
+      <default>[myphone=012345,myplace=Abcd,myname=Alice]</default>
+      <locale name="C">
+         <short>List of keyword/value pairs for the Templates plugin to 
+		substitute in a message body.</short>
+         <long>
+	       List of keyword/value pairs for the Templates plugin to 
+	       substitute in a message body.
+         </long>
+      </locale>
+    </schema>
+  </schemalist>
+</gconfschemafile>
+

Added: trunk/plugins/templates/org-gnome-templates.eplug.xml
==============================================================================
--- (empty file)
+++ trunk/plugins/templates/org-gnome-templates.eplug.xml	Sun Jul 20 18:26:42 2008
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<e-plugin-list>
+    <e-plugin
+        type="shlib"
+        id="org.gnome.evolution.plugin.templates"
+        location="@PLUGINDIR@/liborg-gnome-templates SOEXT@"
+        _name="Templates">
+        <_description>Drafts based template plugin</_description>
+        <author name="Bharath Acharya" email="abharath novell com"/>
+	<author name="Diego Escalante Urrelo" email="diegoe gnome org"/>
+
+        <!-- hook into the mail popup menu -->
+        <hook class="org.gnome.evolution.mail.popup:1.0">
+            <menu 
+                id="org.gnome.evolution.mail.folderview.popup" 
+                target="select"
+                factory="org_gnome_templates_popup">
+            </menu>
+        </hook>
+
+	<hook class="org.gnome.evolution.ui:1.0">
+		<ui-manager id="org.gnome.evolution.composer">
+			<menubar name='main-menu'>
+				<placeholder name='pre-edit-menu'>
+      					<menu action='file-menu'>
+						<placeholder name="template-holder">
+							<menuitem action="Template"/>
+						</placeholder>
+					</menu>
+				</placeholder>
+			</menubar>
+		</ui-manager>
+	</hook>
+    </e-plugin>
+</e-plugin-list>
+

Added: trunk/plugins/templates/templates.c
==============================================================================
--- (empty file)
+++ trunk/plugins/templates/templates.c	Sun Jul 20 18:26:42 2008
@@ -0,0 +1,764 @@
+/*
+ * templates.c
+ * This file is part of Draft Templates plugin for Evolution
+ *
+ * Authors:
+ *	Diego Escalante Urrelo <diegoe gnome org>
+ * 	Bharath Acharya <abharath novell com>
+ * Copyright (C) 2008 - Diego Escalante Urrelo
+ * 			Bharath Acharya
+ *
+ * Draft Templates is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Draft Templates 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 General Public License
+ * along with Draft Templates; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+#include <gconf/gconf-client.h>
+
+#include <e-util/e-config.h>
+#include <camel/camel-url.h>
+#include <camel/camel-folder.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-multipart.h>
+#include <camel/camel-stream-mem.h>
+#include <camel/camel-store.h>
+
+#include <mail/em-composer-utils.h>
+#include <mail/em-popup.h>
+#include <mail/mail-component.h>
+#include <mail/mail-session.h>
+#include <mail/mail-ops.h>
+#include <e-util/e-error.h>
+#include <e-util/e-plugin.h>
+#include <glade/glade.h>
+
+#include <composer/e-msg-composer.h>
+
+#define GCONF_KEY_TEMPLATE_PLACEHOLDERS "/apps/evolution/mail/template_placeholders"
+
+typedef struct {
+	GladeXML *xml;
+	GConfClient *gconf;
+	GtkWidget   *treeview;
+	GtkWidget   *clue_add;
+	GtkWidget   *clue_edit;
+	GtkWidget   *clue_remove;
+	GtkListStore *store;
+} UIData;
+
+enum {
+	CLUE_KEYWORD_COLUMN,
+	CLUE_VALUE_COLUMN,
+	CLUE_N_COLUMNS,
+};
+
+typedef struct {
+    CamelMimeMessage *msg;
+    EMPopupTargetSelect *t;
+} UserData;
+
+static char* get_content	(CamelMimeMessage *message);
+
+static void reply_with_template	(EPopup *ep, EPopupItem *item, void *data);
+
+static void popup_free		(EPopup *ep, GSList *l, void *data);
+
+static GSList *fill_submenu	(CamelStore *store, 
+                             	CamelFolderInfo *info, 
+                              	GSList *list, 
+                              	EMPopupTargetSelect *t);
+
+static GSList *append_to_menu 	(CamelFolder *folder, 
+                             	GPtrArray *uids, 
+                             	GSList *list, 
+                             	EMPopupTargetSelect *t);
+
+void org_gnome_templates_popup	(EPlugin *ep, EMPopupTargetSelect *t);
+
+GtkWidget *e_plugin_lib_get_configure_widget	(EPlugin *epl);
+
+gboolean e_plugin_ui_init	(GtkUIManager *manager, EMsgComposer *composer);
+
+
+/* Thanks to attachment reminder plugin for this*/
+static void commit_changes (UIData *ui);
+
+static void  key_cell_edited_callback (GtkCellRendererText *cell, gchar *path_string,
+				   gchar *new_text,UIData *ui);
+
+static void  value_cell_edited_callback (GtkCellRendererText *cell, gchar *path_string,
+				   gchar *new_text,UIData *ui);				   
+
+static gboolean clue_foreach_check_isempty (GtkTreeModel *model, GtkTreePath
+					*path, GtkTreeIter *iter, UIData *ui);
+
+static void
+selection_changed (GtkTreeSelection *selection, UIData *ui)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+
+	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		gtk_widget_set_sensitive (ui->clue_edit, TRUE);
+		gtk_widget_set_sensitive (ui->clue_remove, TRUE);
+	} else {
+		gtk_widget_set_sensitive (ui->clue_edit, FALSE);
+		gtk_widget_set_sensitive (ui->clue_remove, FALSE);
+	}
+}
+
+static void
+destroy_ui_data (gpointer data)
+{
+	UIData *ui = (UIData *) data;
+
+	if (!ui)
+		return;
+
+	g_object_unref (ui->xml);
+	g_object_unref (ui->gconf);
+	g_free (ui);
+}
+
+static void
+commit_changes (UIData *ui)
+{
+	GtkTreeModel *model = NULL;
+	GSList *clue_list = NULL;
+	GtkTreeIter iter;
+	gboolean valid;
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
+	valid = gtk_tree_model_get_iter_first (model, &iter);
+
+	while (valid) {
+		char *keyword, *value;
+		char *key;
+		
+		gtk_tree_model_get (model, &iter, CLUE_KEYWORD_COLUMN, &keyword, -1);
+		gtk_tree_model_get (model, &iter, CLUE_VALUE_COLUMN, &value, -1);
+
+		/* Check if the keyword and value are not empty */
+		if ((keyword) && (value) && (g_utf8_strlen(g_strstrip(keyword), -1) > 0) 
+			&& (g_utf8_strlen(g_strstrip(value), -1) > 0)) {
+			key = g_strdup_printf("%s=%s", keyword, value);
+			clue_list = g_slist_append (clue_list, key);
+		}	
+		valid = gtk_tree_model_iter_next (model, &iter);
+	}
+
+	gconf_client_set_list (ui->gconf, GCONF_KEY_TEMPLATE_PLACEHOLDERS, GCONF_VALUE_STRING, clue_list, NULL);
+
+	g_slist_foreach (clue_list, (GFunc) g_free, NULL);
+	g_slist_free (clue_list);
+}
+
+static void
+clue_check_isempty (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, UIData *ui)
+{
+	GtkTreeSelection *selection;
+	char *keyword = NULL;
+	gboolean valid;
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
+	/* move to the previous node */
+	valid = gtk_tree_path_prev (path);
+
+	gtk_tree_model_get (model, iter, CLUE_KEYWORD_COLUMN, &keyword, -1);
+	if ((keyword) && !(g_utf8_strlen (g_strstrip (keyword), -1) > 0))
+		gtk_list_store_remove (ui->store, iter);
+
+	/* Check if we have a valid row to select. If not, then select
+	 * the previous row */
+	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);
+		}
+	}
+
+	gtk_widget_grab_focus (ui->treeview);
+	g_free (keyword);
+}
+
+static gboolean
+clue_foreach_check_isempty (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, UIData *ui)
+{
+	gboolean valid;
+
+	valid = gtk_tree_model_get_iter_first (model, iter);
+	while (valid && gtk_list_store_iter_is_valid (ui->store, iter)) {
+		char *keyword = NULL;
+		gtk_tree_model_get (model, iter, CLUE_KEYWORD_COLUMN, &keyword, -1);
+		/* Check if the keyword is not empty and then emit the row-changed
+		signal (if we delete the row, then the iter gets corrupted) */
+		if ((keyword) && !(g_utf8_strlen (g_strstrip (keyword), -1) > 0))
+			gtk_tree_model_row_changed (model, path, iter);
+
+		g_free (keyword);
+		valid = gtk_tree_model_iter_next (model, iter);
+	}
+
+	return FALSE;
+}
+
+static void
+key_cell_edited_callback (GtkCellRendererText *cell,
+		      gchar               *path_string,
+		      gchar               *new_text,
+		      UIData             *ui)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	char *value;
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
+
+	gtk_tree_model_get_iter_from_string (model, &iter, path_string);
+
+	gtk_tree_model_get (model, &iter, CLUE_VALUE_COLUMN, &value, -1);	
+	gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+				    CLUE_KEYWORD_COLUMN, new_text, CLUE_VALUE_COLUMN, value, -1);
+
+	commit_changes (ui);
+}
+
+static void
+value_cell_edited_callback (GtkCellRendererText *cell,
+		      gchar               *path_string,
+		      gchar               *new_text,
+		      UIData             *ui)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	char *keyword;
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
+
+	gtk_tree_model_get_iter_from_string (model, &iter, path_string);
+	
+	gtk_tree_model_get (model, &iter, CLUE_KEYWORD_COLUMN, &keyword, -1);
+
+	gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+				    CLUE_KEYWORD_COLUMN, keyword, CLUE_VALUE_COLUMN, new_text, -1);
+
+	commit_changes (ui);
+}
+
+static void
+clue_add_clicked (GtkButton *button, UIData *ui)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	gchar *new_clue = NULL;
+	GtkTreeViewColumn *focus_col;
+	GtkTreePath *path;
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
+	gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc) clue_foreach_check_isempty, ui);
+
+	/* Disconnect from signal so that we can create an empty row */
+	g_signal_handlers_disconnect_matched(G_OBJECT(model), G_SIGNAL_MATCH_FUNC, 0, 0, NULL, clue_check_isempty, ui);
+
+	/* TODO : Trim and check for blank strings */
+	new_clue = g_strdup ("");
+	gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+	gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+				    CLUE_KEYWORD_COLUMN, new_clue, CLUE_VALUE_COLUMN, new_clue, -1);
+
+	focus_col = gtk_tree_view_get_column (GTK_TREE_VIEW (ui->treeview), CLUE_KEYWORD_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_view_row_activated(GTK_TREE_VIEW(ui->treeview), path, focus_col);
+		gtk_tree_path_free (path);
+	}
+
+	/* We have done our job, connect back to the signal */
+	g_signal_connect(G_OBJECT(model), "row-changed", G_CALLBACK(clue_check_isempty), ui);
+}
+
+static void
+clue_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->clue_edit, FALSE);
+		gtk_widget_set_sensitive (ui->clue_remove, FALSE);
+	}
+
+	gtk_widget_grab_focus(ui->treeview);
+	gtk_tree_path_free (path);
+
+	commit_changes (ui);
+}
+
+static void
+clue_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), CLUE_KEYWORD_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);
+	}
+}
+
+GtkWidget *
+e_plugin_lib_get_configure_widget (EPlugin *epl)
+{
+	GtkCellRenderer *renderer_key, *renderer_value;
+	GtkTreeSelection *selection;
+	GtkTreeIter iter;
+	GConfClient *gconf = gconf_client_get_default();
+	GtkWidget *hbox;
+	GSList *clue_list = NULL, *list;
+	GtkTreeModel *model;
+
+	UIData *ui = g_new0 (UIData, 1);
+
+	char *gladefile;
+
+	gladefile = g_build_filename (EVOLUTION_PLUGINDIR,
+			"templates.glade",
+			NULL);
+	ui->xml = glade_xml_new (gladefile, "templates_configuration_box", NULL);
+	g_free (gladefile);
+
+	ui->gconf = gconf_client_get_default ();
+
+	ui->treeview = glade_xml_get_widget (ui->xml, "clue_treeview");
+
+	ui->store = gtk_list_store_new (CLUE_N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
+
+	gtk_tree_view_set_model (GTK_TREE_VIEW (ui->treeview), GTK_TREE_MODEL (ui->store));
+
+	renderer_key = gtk_cell_renderer_text_new ();
+	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (ui->treeview), -1, _("Keywords"),
+			renderer_key, "text", CLUE_KEYWORD_COLUMN, NULL);
+	g_object_set (G_OBJECT (renderer_key), "editable", TRUE, NULL);
+	g_signal_connect(renderer_key, "edited", (GCallback) key_cell_edited_callback, ui);
+
+	renderer_value = gtk_cell_renderer_text_new ();
+	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (ui->treeview), -1, _("Values"),
+			renderer_value, "text", CLUE_VALUE_COLUMN, NULL);
+	g_object_set (G_OBJECT (renderer_value), "editable", TRUE, NULL);
+	g_signal_connect(renderer_value, "edited", (GCallback) value_cell_edited_callback, ui);
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
+	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+	g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (selection_changed), ui);
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ui->treeview), TRUE);
+
+	ui->clue_add = glade_xml_get_widget (ui->xml, "clue_add");
+	g_signal_connect (G_OBJECT (ui->clue_add), "clicked", G_CALLBACK (clue_add_clicked), ui);
+
+	ui->clue_remove = glade_xml_get_widget (ui->xml, "clue_remove");
+	g_signal_connect (G_OBJECT (ui->clue_remove), "clicked", G_CALLBACK (clue_remove_clicked), ui);
+	gtk_widget_set_sensitive (ui->clue_remove, FALSE);
+
+	ui->clue_edit = glade_xml_get_widget (ui->xml, "clue_edit");
+	g_signal_connect (G_OBJECT (ui->clue_edit), "clicked", G_CALLBACK (clue_edit_clicked), ui);
+	gtk_widget_set_sensitive (ui->clue_edit, FALSE);
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
+	g_signal_connect(G_OBJECT(model), "row-changed", G_CALLBACK(clue_check_isempty), ui);
+
+	/* Populate tree view with values from gconf */
+	clue_list = gconf_client_get_list ( gconf, GCONF_KEY_TEMPLATE_PLACEHOLDERS, GCONF_VALUE_STRING, NULL );
+
+	for (list = clue_list; list; list = g_slist_next (list)) {
+		char **temp = g_strsplit (list->data, "=", 2);
+		gtk_list_store_append (ui->store, &iter);
+		gtk_list_store_set (ui->store, &iter, CLUE_KEYWORD_COLUMN, temp[0], CLUE_VALUE_COLUMN, temp[1], -1);
+		g_strfreev(temp);
+	}
+
+	if (clue_list) {
+		g_slist_foreach (clue_list, (GFunc) g_free, NULL);
+		g_slist_free (clue_list);
+	}
+
+	/* Add the list here */
+
+	hbox = gtk_vbox_new (FALSE, 0);
+
+	gtk_box_pack_start (GTK_BOX (hbox), glade_xml_get_widget (ui->xml, "templates_configuration_box"), 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;
+}
+
+/* borrowed from plugins/mail-to-task/ */
+static char *
+get_content (CamelMimeMessage *message)
+{
+	CamelDataWrapper *content;
+	CamelStream *mem;
+	CamelContentType *type;
+	CamelMimePart *mime_part = CAMEL_MIME_PART (message);
+	char *str, *convert_str = NULL;
+	gsize bytes_read, bytes_written;
+	gint count = 2;
+
+	content = camel_medium_get_content_object ((CamelMedium *) message);
+	if (!content)
+		return NULL;
+
+	/* Get non-multipart content from multipart message. */
+	while (CAMEL_IS_MULTIPART (content) && count > 0) {
+		mime_part = camel_multipart_get_part (CAMEL_MULTIPART (content), 0);
+		content = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+		count--;
+	}
+
+	if (!mime_part)
+		return NULL;
+
+	type = camel_mime_part_get_content_type (mime_part);
+	if (!camel_content_type_is (type, "text", "plain"))
+		return NULL;
+
+	mem = camel_stream_mem_new ();
+	camel_data_wrapper_decode_to_stream (content, mem);
+
+	str = g_strndup ((const gchar*)((CamelStreamMem *) mem)->buffer->data, ((CamelStreamMem *) mem)->buffer->len);
+	camel_object_unref (mem);
+
+	/* convert to UTF-8 string */
+	if (str && content->mime_type->params && content->mime_type->params->value) {
+		convert_str = g_convert (str, strlen (str),
+				"UTF-8", content->mime_type->params->value,
+				&bytes_read, &bytes_written, NULL);
+	}
+
+	if (convert_str) {
+		g_free (str);
+		return convert_str;
+	}
+	else 
+		return str;
+			
+}
+
+static void
+reply_with_template (EPopup *ep, EPopupItem *item, void *data)
+{
+	CamelMimeMessage *new, *template, *reply_to;
+	CamelStore *store;
+	CamelFolder *templates_folder;
+	struct _camel_header_raw *header;
+	UserData *userdata = item->user_data;
+	char *cont, *basedir, *url;
+
+	/* We get the templates folder and all the uids of the messages in there */
+	basedir = g_build_filename (g_get_home_dir (), ".evolution", "mail", "local", NULL);
+	url = g_strdup_printf ("mbox://%s", basedir);
+	g_free (basedir);
+
+	store = (CamelStore *) camel_session_get_service (session, url, CAMEL_PROVIDER_STORE, NULL);
+	g_free (url);
+
+	templates_folder = camel_store_get_folder (store, _("Templates"), CAMEL_STORE_FOLDER_CREATE, NULL);
+
+	/* Get from the currently selected folder, the currently selected message */
+	reply_to = camel_folder_get_message (userdata->t->folder, 
+			g_ptr_array_index (userdata->t->uids, 0), 
+			NULL);
+
+	/* The message we'll be using has been stored when building the menu */
+	template = userdata->msg;
+
+	/* The new message we are creating */
+	new = camel_mime_message_new();
+
+	/* Add the headers from the message we are replying to, so CC and that
+	 * stuff is preserved.
+	 */
+	header = ((CamelMimePart *)reply_to)->headers;
+	while (header) {
+		if (g_ascii_strncasecmp (header->name, "content-", 8) != 0) {
+			camel_medium_add_header((CamelMedium *) new, 
+					header->name, 
+					header->value);
+		}
+		header = header->next;
+	}
+
+	camel_mime_part_set_encoding((CamelMimePart *) new, CAMEL_TRANSFER_ENCODING_8BIT);
+
+	/* Get the template content. */
+	cont = get_content (template);
+
+	/* Set the To: field to the same To: field of the message we are replying to. */
+	camel_mime_message_set_recipients (new, CAMEL_RECIPIENT_TYPE_TO, 
+			camel_mime_message_get_from (reply_to));
+
+
+	/* Copy the CC and BCC from the template.*/
+	camel_mime_message_set_recipients (new, CAMEL_RECIPIENT_TYPE_CC, 
+			camel_mime_message_get_recipients (template, CAMEL_RECIPIENT_TYPE_CC));
+
+	camel_mime_message_set_recipients (new, CAMEL_RECIPIENT_TYPE_BCC, 
+			camel_mime_message_get_recipients (template, CAMEL_RECIPIENT_TYPE_BCC));
+
+	camel_mime_part_set_content((CamelMimePart *)new,
+			cont, (int) g_utf8_strlen(cont, -1), "text");
+
+	/* Create the composer */
+	em_utils_edit_message (new, templates_folder);
+
+	camel_object_unref(new);
+}
+
+static void 
+popup_free (EPopup *ep, GSList *l, void *data)
+{
+	g_slist_free (l);
+}
+
+static GSList
+*append_to_menu (CamelFolder *folder, GPtrArray *uids, GSList *list, EMPopupTargetSelect *t)
+{
+	int i;
+
+	for (i = 0; i < uids->len; i++) {
+		const char *subject;
+		char *path;
+		EPopupItem *item;
+		CamelMimeMessage *message;
+		const char *uid;
+
+		uid = g_strdup (g_ptr_array_index (uids, i));
+
+		/* Same as in fill_submenu */
+		if (!g_str_has_suffix (folder->name, "Templates"))
+			path = g_strdup_printf ("80.%s", folder->full_name);
+		else
+			path = "80.Templates";
+
+		/* If this uid is trashed, ignore it */
+		if (camel_folder_get_message_flags (folder, uid) & CAMEL_MESSAGE_DELETED)
+			continue;
+		
+		/* Get the message for this uid */
+		message = camel_folder_get_message (folder, 
+				uid, 
+				NULL);
+
+		subject = camel_mime_message_get_subject (message);
+
+		/* Create the menu item for it */
+		item = g_slice_alloc0(sizeof(*item));
+		item->type = E_POPUP_ITEM;
+		item->path = g_strdup_printf ("%s/%02d", path, i);
+		item->label = g_strdup ((strlen(subject) > 0) ? subject : _("No title"));
+		item->visible = EM_POPUP_SELECT_MANY | EM_POPUP_SELECT_ONE;
+
+		/* Make some info available to the callback */
+		UserData *user_data;
+		user_data = g_slice_new(UserData);
+		user_data->msg = message;
+		user_data->t = t;
+
+		item->user_data = user_data;
+		item->activate = reply_with_template;
+
+		list = g_slist_prepend (list, item);
+	}
+
+	return list;
+}
+
+static GSList 
+*fill_submenu (CamelStore *store, CamelFolderInfo *info, GSList *list, EMPopupTargetSelect *t)
+{
+	while (info) {
+		CamelFolder *folder;
+		GPtrArray *uids;
+		EPopupItem *item;
+
+		folder = camel_store_get_folder (store, info->full_name, 0, NULL);
+
+		item = g_slice_alloc0(sizeof(*item));
+		item->type = E_POPUP_SUBMENU;
+		item->label = folder->name; 
+		item->visible = EM_POPUP_SELECT_MANY | EM_POPUP_SELECT_ONE;
+
+		/* To avoid having a Templates dir, we ignore the top level */
+		if (!g_str_has_suffix (folder->name, "Templates"))
+			item->path = g_strdup_printf ("80.%s", folder->full_name);
+		else
+			item->path = "80.Templates";
+
+		list = g_slist_prepend (list, item);
+
+		/* Get the uids for this folder and fill them in the menu */
+		uids = camel_folder_get_uids (folder);
+		list = append_to_menu (folder, uids, list, t);
+		camel_folder_free_uids (folder, uids);
+
+		/* If the folder has a child, call this function again */
+		if (info->child) {
+			list = fill_submenu (store, info->child, list, t);
+		}
+
+		info = info->next;
+	}
+
+	return list;
+}
+
+void
+org_gnome_templates_popup (EPlugin *ep, EMPopupTargetSelect *t)
+{
+	CamelFolder *templates_folder;
+	CamelFolderInfo *templates_info;
+	CamelStore *store;
+	char *basedir;
+	char *url;
+
+	GSList *list = NULL;
+
+	/* We get the templates folder and all the uids of the messages in there */
+	basedir = g_build_filename (g_get_home_dir (), ".evolution", "mail", "local", NULL);
+	url = g_strdup_printf ("mbox://%s", basedir);
+
+	g_free (basedir);
+
+	store = (CamelStore *) camel_session_get_service (session, url, CAMEL_PROVIDER_STORE, NULL);
+	g_free (url);
+
+	templates_folder = camel_store_get_folder (store, _("Templates"), CAMEL_STORE_FOLDER_CREATE, NULL);
+
+	templates_info = camel_store_get_folder_info (store, 
+			templates_folder->full_name, 
+			CAMEL_STORE_FOLDER_INFO_RECURSIVE | CAMEL_STORE_FOLDER_INFO_FAST, 
+			NULL);
+
+	/* Get subfolders and fill it */
+	list = fill_submenu (store, templates_info, list, t);
+
+	e_popup_add_items (t->target.popup, list, NULL, popup_free, NULL);
+
+	return;
+}
+
+static void
+action_template_cb (GtkAction *action,
+		EMsgComposer *composer)
+{
+	CamelMessageInfo *info;
+	CamelMimeMessage *msg;
+	CamelStore *store;
+	CamelFolder *templates_folder;
+
+	char *basedir;
+	char *url;
+
+	/* We get the templates folder and all the uids of the messages in there */
+	basedir = g_build_filename (g_get_home_dir (), ".evolution", "mail", "local", NULL);
+	url = g_strdup_printf ("mbox://%s", basedir);
+	g_free (basedir);
+
+	store = (CamelStore *) camel_session_get_service (session, url, CAMEL_PROVIDER_STORE, NULL);
+	g_free (url);
+
+	templates_folder = camel_store_get_folder (store, _("Templates"), CAMEL_STORE_FOLDER_CREATE, NULL);
+
+	msg = e_msg_composer_get_message_draft (composer);
+	info = camel_message_info_new (NULL);
+
+	/* FIXME: what's the ~0 for? :) */
+	camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DRAFT, ~0);
+
+	mail_append_mail (templates_folder, msg, info, NULL, composer);
+
+	return;
+}
+
+static GtkActionEntry entries[] = {
+
+	{ "Template",
+	  GTK_STOCK_SAVE,
+	  N_("Save as _Template"),
+  	  "<Shift><Control>t",
+	  N_("Save as Template"),
+	  G_CALLBACK (action_template_cb) }
+};
+
+gboolean
+e_plugin_ui_init (GtkUIManager *manager,
+                  EMsgComposer *composer)
+{
+	GtkhtmlEditor *editor;
+
+	editor = GTKHTML_EDITOR (composer);
+
+	/* Add actions to the "composer" action group. */
+	gtk_action_group_add_actions (
+		gtkhtml_editor_get_action_group (editor, "composer"),
+		entries, G_N_ELEMENTS (entries), composer);
+
+	return TRUE;
+}

Added: trunk/plugins/templates/templates.glade
==============================================================================
--- (empty file)
+++ trunk/plugins/templates/templates.glade	Sun Jul 20 18:26:42 2008
@@ -0,0 +1,126 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd";>
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window1">
+  <property name="visible">True</property>
+  <property name="title">window1</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+
+  <child>
+    <widget class="GtkVBox" id="templates_configuration_box">
+      <property name="width_request">385</property>
+      <property name="height_request">189</property>
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">5</property>
+
+      <child>
+	<widget class="GtkHBox" id="clue_container">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">6</property>
+
+	  <child>
+	    <widget class="GtkScrolledWindow" id="scrolledwindow1">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+	      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+	      <property name="shadow_type">GTK_SHADOW_NONE</property>
+	      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+	      <child>
+		<widget class="GtkTreeView" id="clue_treeview">
+		  <property name="border_width">1</property>
+		  <property name="visible">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="headers_visible">True</property>
+		  <property name="rules_hint">False</property>
+		  <property name="reorderable">False</property>
+		  <property name="enable_search">True</property>
+		  <property name="fixed_height_mode">False</property>
+		  <property name="hover_selection">False</property>
+		  <property name="hover_expand">False</property>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkVButtonBox" id="vbuttonbox2">
+	      <property name="visible">True</property>
+	      <property name="layout_style">GTK_BUTTONBOX_START</property>
+	      <property name="spacing">6</property>
+
+	      <child>
+		<widget class="GtkButton" id="clue_add">
+		  <property name="visible">True</property>
+		  <property name="can_default">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="label">gtk-add</property>
+		  <property name="use_stock">True</property>
+		  <property name="relief">GTK_RELIEF_NORMAL</property>
+		  <property name="focus_on_click">True</property>
+		</widget>
+	      </child>
+
+	      <child>
+		<widget class="GtkButton" id="clue_edit">
+		  <property name="visible">True</property>
+		  <property name="can_default">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="label">gtk-edit</property>
+		  <property name="use_stock">True</property>
+		  <property name="relief">GTK_RELIEF_NORMAL</property>
+		  <property name="focus_on_click">True</property>
+		</widget>
+	      </child>
+
+	      <child>
+		<widget class="GtkButton" id="clue_remove">
+		  <property name="visible">True</property>
+		  <property name="can_default">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="label">gtk-remove</property>
+		  <property name="use_stock">True</property>
+		  <property name="relief">GTK_RELIEF_NORMAL</property>
+		  <property name="focus_on_click">True</property>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>

Modified: trunk/po/POTFILES.in
==============================================================================
--- trunk/po/POTFILES.in	(original)
+++ trunk/po/POTFILES.in	Sun Jul 20 18:26:42 2008
@@ -416,6 +416,10 @@
 plugins/startup-wizard/startup-wizard.c
 plugins/subject-thread/org-gnome-subject-thread.eplug.xml
 plugins/subject-thread/subject-thread.c
+plugins/templates/apps-evolution-template-placeholders.schemas.in
+plugins/templates/templates.c
+plugins/templates/templates.glade
+plugins/templates/org-gnome-templates.eplug.xml
 plugins/tnef-attachments/org-gnome-tnef-attachments.eplug.xml
 shell/GNOME_Evolution_Shell.server.in.in
 shell/test/GNOME_Evolution_Test.server.in.in



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