[evolution] Bug #573878 - [face] Plugin is unusable



commit b3b783f14644f1b736282649ad625a7ae2ea398e
Author: Milan Crha <mcrha redhat com>
Date:   Thu Oct 15 15:41:32 2009 +0200

    Bug #573878 - [face] Plugin is unusable

 plugins/face/Makefile.am                           |   39 ++-
 .../face/apps_evolution_eplugin_face.schemas.in    |   17 +
 plugins/face/face.c                                |  451 +++++++++++++++++---
 plugins/face/org-gnome-face.eplug.xml              |   10 +-
 plugins/face/org-gnome-face.error.xml              |   18 +-
 5 files changed, 450 insertions(+), 85 deletions(-)
---
diff --git a/plugins/face/Makefile.am b/plugins/face/Makefile.am
index 2d307f8..29444dd 100644
--- a/plugins/face/Makefile.am
+++ b/plugins/face/Makefile.am
@@ -30,14 +30,43 @@ error_DATA = org-gnome-face.error
 
 errordir = $(privdatadir)/errors
 
-EXTRA_DIST = 						\
-	org-gnome-face.eplug.xml			\
-	org-gnome-face.error.xml
+# GConf schemas
 
-BUILT_SOURCES = 				\
-	org-gnome-face.eplug 			\
+schemadir   = $(GCONF_SCHEMA_FILE_DIR)
+schema_in_files = apps_evolution_eplugin_face.schemas.in
+schema_DATA = $(schema_in_files:.schemas.in=.schemas)
+
+ INTLTOOL_SCHEMAS_RULE@
+
+EXTRA_DIST = 				\
+	org-gnome-face.eplug.xml	\
+	org-gnome-face.error.xml	\
+	$(schema_in_files)
+
+BUILT_SOURCES = 			\
+	org-gnome-face.eplug 		\
 	org-gnome-face.error
 
 CLEANFILES = $(BUILT_SOURCES)
 
+DISTCLEANFILES = $(schema_DATA)
+
+if OS_WIN32
+install-data-local:
+	if test -z "$(DESTDIR)"; then \
+		for p in $(schema_DATA); do \
+			(echo set GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE); \
+			 echo $(GCONFTOOL) --makefile-install-rule $$p) >_temp.bat; \
+			cmd /c _temp.bat; \
+		done; \
+	fi
+else
+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
+endif
+
 -include $(top_srcdir)/git.mk
diff --git a/plugins/face/apps_evolution_eplugin_face.schemas.in b/plugins/face/apps_evolution_eplugin_face.schemas.in
new file mode 100644
index 0000000..7583172
--- /dev/null
+++ b/plugins/face/apps_evolution_eplugin_face.schemas.in
@@ -0,0 +1,17 @@
+<gconfschemafile>
+  <schemalist>
+
+    <schema>
+      <key>/schemas/apps/evolution/eplugin/face/insert_by_default</key>
+      <applyto>/apps/evolution/eplugin/face/insert_by_default</applyto>
+      <owner>org.gnome.evolution.plugins.face</owner>
+      <type>bool</type>
+      <default>false</default>
+      <locale name="C">
+        <short>Insert Face picture by default</short>
+        <long>Whether insert Face picture to outgoing messages by default. The picture should be set before checking this, otherwise nothing happens.</long>
+      </locale>
+    </schema>
+
+  </schemalist>
+</gconfschemafile>
diff --git a/plugins/face/face.c b/plugins/face/face.c
index 3e462da..19856b3 100644
--- a/plugins/face/face.c
+++ b/plugins/face/face.c
@@ -28,118 +28,439 @@
 #include "composer/e-msg-composer.h"
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
+#include <mail/em-event.h>
 #include <e-util/e-error.h>
 #include <e-util/e-util.h>
+#include <e-util/e-icon-factory.h>
 
 #define d(x)
 
-gboolean	e_plugin_ui_init		(GtkUIManager *ui_manager,
-						 EMsgComposer *composer);
+#define SETTINGS_KEY "/apps/evolution/eplugin/face/insert_by_default"
 
-static void
-action_face_cb (GtkAction *action,
-                EMsgComposer *composer)
+static gboolean
+get_include_face_by_default (void)
 {
-	gchar *filename, *file_contents;
-	GError *error = NULL;
+	GConfClient *gconf = gconf_client_get_default ();
+	gboolean res;
 
-	filename = g_build_filename (e_get_user_data_dir (), "faces", NULL);
-	g_file_get_contents (filename, &file_contents, NULL, &error);
+	res = gconf_client_get_bool (gconf, SETTINGS_KEY, NULL);
 
-	if (error) {
+	g_object_unref (gconf);
 
-		GtkWidget *filesel;
-		const gchar *image_filename;
-		gsize length;
+	return res;
+}
 
-		GtkFileFilter *filter;
+static void
+set_include_face_by_default (gboolean value)
+{
+	GConfClient *gconf = gconf_client_get_default ();
 
-		filesel = gtk_file_chooser_dialog_new (_
-					("Select a (48*48) png of size < 700bytes"),
-					NULL,
-					GTK_FILE_CHOOSER_ACTION_OPEN,
-					GTK_STOCK_CANCEL,
-					GTK_RESPONSE_CANCEL,
-					GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
+	gconf_client_set_bool (gconf, SETTINGS_KEY, value, NULL);
 
-		gtk_dialog_set_default_response (GTK_DIALOG (filesel), GTK_RESPONSE_OK);
+	g_object_unref (gconf);
+}
 
-		filter = gtk_file_filter_new ();
-		gtk_file_filter_set_name (filter, _("PNG files"));
-		gtk_file_filter_add_mime_type (filter, "image/png");
-		gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (filesel), filter);
+static gchar *
+get_filename (void)
+{
+	return g_build_filename (e_get_user_data_dir (), "faces", NULL);
+}
 
-		if (GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (filesel))) {
-			image_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filesel));
+static gchar *
+get_face_base64 (void)
+{
+	gchar *filename = get_filename (), *file_contents = NULL;
+	gsize length = 0;
 
-			error = NULL;
+	if (g_file_get_contents (filename, &file_contents, &length, NULL)) {
+		if (length > 0) {
+			file_contents = g_realloc (file_contents, length + 1);
+			file_contents [length] = 0;
+		} else {
+			g_free (file_contents);
 			file_contents = NULL;
-			g_file_get_contents (image_filename, &file_contents, &length, &error);
+		}
+	} else {
+		file_contents = NULL;
+	}
+
+	g_free (filename);
+
+	return file_contents;
+}
+
+static void
+set_face_raw (gchar *content, gsize length)
+{
+	gchar *filename = get_filename ();
+
+	if (content) {
+		gchar *file_contents;
+		
+		file_contents = g_base64_encode ((guchar *) content, length);
+		g_file_set_contents (filename, file_contents, -1, NULL);
+		g_free (file_contents);
+	} else {
+		g_file_set_contents (filename, "", -1, NULL);
+	}
+
+	g_free (filename);
+}
+
+/* g_object_unref returned pointer when done with it */
+static GdkPixbuf *
+get_active_face (void)
+{
+	GdkPixbufLoader *loader;
+	GdkPixbuf *res = NULL;
+	gchar *face;
+	guchar *data;
+	gsize data_len = 0;
+
+	face = get_face_base64 ();
+
+	if (!face || !*face) {
+		g_free (face);
+		return NULL;
+	}
+
+	data = g_base64_decode (face, &data_len);
+	if (!data || !data_len) {
+		g_free (face);
+		g_free (data);
+		return NULL;
+	}
+
+	g_free (face);
+	
+	loader = gdk_pixbuf_loader_new ();
+
+	if (gdk_pixbuf_loader_write (loader, data, data_len, NULL)
+	    && gdk_pixbuf_loader_close (loader, NULL)) {
+		res = gdk_pixbuf_loader_get_pixbuf (loader);
+		if (res)
+			g_object_ref (res);
+	}
+
+	g_object_unref (loader);
 
-			if (!error) {
-				error = NULL;
-				if (length < 720) {
+	g_free (data);
 
-					GdkPixbuf *pixbuf;
-					GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
+	return res;
+}
+
+static gboolean
+prepare_image (const gchar *image_filename, gchar **file_contents, gsize *length, GdkPixbuf **use_pixbuf, gboolean can_claim)
+{
+	gboolean res = FALSE;
+
+	g_return_val_if_fail (image_filename != NULL, FALSE);
+	g_return_val_if_fail (file_contents != NULL, FALSE);
+	g_return_val_if_fail (length != NULL, FALSE);
+
+	if (g_file_get_contents (image_filename, file_contents, length, NULL)) {
+		GError *error = NULL;
+		GdkPixbuf *pixbuf;
+		GdkPixbufLoader *loader = gdk_pixbuf_loader_new ();
+
+		if (!gdk_pixbuf_loader_write (loader, (const guchar *)(*file_contents), *length, &error)
+		    || !gdk_pixbuf_loader_close (loader, &error)
+		    || (pixbuf = gdk_pixbuf_loader_get_pixbuf (loader)) == NULL) {
+			const gchar *err = _("Unknown error");
+
+			if (error && error->message)
+				err = error->message;
+
+			if (can_claim)
+				e_error_run (NULL, "org.gnome.evolution.plugins.face:not-an-image", err, NULL);
 
-					gdk_pixbuf_loader_write (loader, (guchar *)file_contents, length, NULL);
-					gdk_pixbuf_loader_close (loader, NULL);
+			if (error)
+				g_error_free (error);
+		} else {
+			gint width, height;
 
-					pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
-					if (pixbuf) {
-						gint width, height;
+			height = gdk_pixbuf_get_height (pixbuf);
+			width = gdk_pixbuf_get_width (pixbuf);
 
-						g_object_ref (pixbuf);
+			if (height <= 0 || width <= 0) {
+				if (can_claim)
+					e_error_run (NULL, "org.gnome.evolution.plugins.face:invalid-image-size", NULL, NULL);
+			} else if (height != 48 || width != 48) {
+				GdkPixbuf *copy, *scale;
+				guchar *pixels;
+				guint32 fill;
 
-						height = gdk_pixbuf_get_height (pixbuf);
-						width = gdk_pixbuf_get_width (pixbuf);
+				if (width >= height) {
+					if (width > 48) {
+						gdouble ratio = (gdouble) width / 48.0;
+						width = 48;
+						height = height / ratio;
 
-						if (height != 48 || width != 48) {
-							d (printf ("\n\a Invalid Image Size. Please choose a 48*48 image\n\a"));
-							e_error_run (GTK_WINDOW (filesel), "org.gnome.evolution.plugins.face:invalid-image-size", NULL, NULL);
-						} else {
-							file_contents = g_base64_encode ((guchar *) file_contents, length);
-							g_file_set_contents (filename, file_contents, -1, &error);
-						}
+						if (height == 0)
+							height = 1;
 					}
 				} else {
-					d (printf ("File too big"));
-					e_error_run (GTK_WINDOW (filesel), "org.gnome.evolution.plugins.face:invalid-file-size", NULL, NULL);
+					if (height > 48) {
+						gdouble ratio = (gdouble) height / 48.0;
+						height = 48;
+						width = width / ratio;
+						if (width == 0)
+							width = 1;
+					}
 				}
 
+				scale = e_icon_factory_pixbuf_scale (pixbuf, width, height);
+				copy = e_icon_factory_pixbuf_scale (pixbuf, 48, 48);
+
+				width = gdk_pixbuf_get_width (scale);
+				height = gdk_pixbuf_get_height (scale);
+
+				pixels = gdk_pixbuf_get_pixels (scale);
+				/* fill with a pixel color at [0,0] */
+				fill = (pixels[0] << 24) | (pixels[1] << 16) | (pixels[2] << 8) | (pixels[0]);
+				gdk_pixbuf_fill (copy, fill);
+
+				gdk_pixbuf_copy_area (scale, 0, 0, width, height, copy, width < 48 ? (48 - width) / 2 : 0, height < 48 ? (48 - height) / 2 : 0);
+
+				g_free (*file_contents);
+				*file_contents = NULL;
+				*length = 0;
+
+				res = gdk_pixbuf_save_to_buffer (copy, file_contents, length, "png", NULL, "compression", "9", NULL);
+
+				if (res && use_pixbuf)
+					*use_pixbuf = g_object_ref (copy);
+				g_object_unref (copy);
+				g_object_unref (scale);
 			} else {
-				d (printf ("\n\a File cannot be read\n\a"));
-				e_error_run (GTK_WINDOW (filesel), "org.gnome.evolution.plugins.face:file-not-found", NULL, NULL);
+				res = TRUE;
+				if (use_pixbuf)
+					*use_pixbuf = g_object_ref (pixbuf);
 			}
 		}
+
+		g_object_unref (loader);
+	} else {
+		if (can_claim)
+			e_error_run (NULL, "org.gnome.evolution.plugins.face:file-not-found", NULL, NULL);
+	}
+
+	return res;
+}
+
+static void
+update_preview_cb (GtkFileChooser *file_chooser, gpointer data)
+{
+	GtkWidget *preview;
+	gchar *filename, *file_contents = NULL;
+	GdkPixbuf *pixbuf = NULL;
+	gboolean have_preview;
+	gsize length = 0;
+
+	preview = GTK_WIDGET (data);
+	filename = gtk_file_chooser_get_preview_filename (file_chooser);
+ 
+	have_preview = filename && prepare_image (filename, &file_contents, &length, &pixbuf, FALSE);
+	if (have_preview) {
+		g_free (file_contents);
+		have_preview = pixbuf != NULL;
+	}
+
+	g_free (filename);
+
+	gtk_image_set_from_pixbuf (GTK_IMAGE (preview), pixbuf);
+	if (pixbuf)
+		g_object_unref (pixbuf);
+
+	gtk_file_chooser_set_preview_widget_active (file_chooser, have_preview);
+}
+
+static GdkPixbuf *
+choose_new_face (void)
+{
+	GdkPixbuf *res = NULL;
+	GtkWidget *filesel, *preview;
+	GtkFileFilter *filter;
+
+	filesel = gtk_file_chooser_dialog_new (_
+				("Select a png picture (the best 48*48 of size < 720 bytes)"),
+				NULL,
+				GTK_FILE_CHOOSER_ACTION_OPEN,
+				GTK_STOCK_CANCEL,
+				GTK_RESPONSE_CANCEL,
+				GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
+
+	gtk_dialog_set_default_response (GTK_DIALOG (filesel), GTK_RESPONSE_OK);
+
+	filter = gtk_file_filter_new ();
+	gtk_file_filter_set_name (filter, _("Image files"));
+	gtk_file_filter_add_mime_type (filter, "image/*");
+	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (filesel), filter);
+
+	preview = gtk_image_new ();
+	gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (filesel), preview);
+	g_signal_connect (filesel, "update-preview", G_CALLBACK (update_preview_cb), preview);
+
+	if (GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (filesel))) {
+		gchar *image_filename, *file_contents = NULL;
+		gsize length = 0;
+
+		image_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filesel));
+		gtk_widget_destroy (filesel);
+
+		if (prepare_image (image_filename, &file_contents, &length, &res, TRUE)) {
+			set_face_raw (file_contents, length);
+		}
+
+		g_free (file_contents);
+		g_free (image_filename);
+	} else {
 		gtk_widget_destroy (filesel);
 	}
-	e_msg_composer_modify_header (composer, "Face", file_contents);
+
+	return res;
+}
+
+static void
+toggled_check_include_by_default_cb (GtkWidget *widget, gpointer data)
+{
+	set_include_face_by_default (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
+}
+
+static void
+click_load_face_cb (GtkButton *butt, gpointer data)
+{
+	GdkPixbuf *face;
+	GtkWidget *img;
+
+	img = gtk_button_get_image (butt);
+	g_return_if_fail (img != NULL);
+
+	face = choose_new_face ();
+
+	if (face) {
+		gtk_image_set_from_pixbuf (GTK_IMAGE (img), face);
+		g_object_unref (face);
+	}
+}
+
+static GtkWidget *
+get_cfg_widget (void)
+{
+	GtkWidget *vbox, *check, *img, *butt;
+	GdkPixbuf *face;
+
+	vbox = gtk_vbox_new (FALSE, 6);
+
+	check = gtk_check_button_new_with_mnemonic (_("_Insert Face picture by default"));
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), get_include_face_by_default ());
+	g_signal_connect (check, "toggled", G_CALLBACK (toggled_check_include_by_default_cb), NULL);
+
+	gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0);
+
+	face = get_active_face ();
+	img = gtk_image_new_from_pixbuf (face);
+	if (face)
+		g_object_unref (face);
+
+	butt = gtk_button_new_with_mnemonic (_("Load new _Face picture"));
+	gtk_button_set_image (GTK_BUTTON (butt), img);
+	g_signal_connect (butt, "clicked", G_CALLBACK (click_load_face_cb), NULL);
+
+	gtk_box_pack_start (GTK_BOX (vbox), butt, FALSE, FALSE, 0);
+
+	gtk_widget_show_all (vbox);
+
+	return vbox;
+}
+
+static void
+action_toggle_face_cb (GtkToggleAction *action, EMsgComposer *composer)
+{
+	if (gtk_toggle_action_get_active (action)) {
+		gchar *face = get_face_base64 ();
+
+		if (!face) {
+			GdkPixbuf *pixbuf = choose_new_face ();
+
+			if (pixbuf) {
+				g_object_unref (pixbuf);
+			} else {
+				/* cannot load a face image, uncheck the option */
+				gtk_toggle_action_set_active (action, FALSE);
+			}
+		} else {
+			g_free (face);
+		}
+	}
 }
 
-static GtkActionEntry entries[] = {
+/* ----------------------------------------------------------------- */
 
-	{ "face",
-	  NULL,
-	  N_("_Face"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_face_cb) }
-};
+gboolean e_plugin_ui_init (GtkUIManager *ui_manager, EMsgComposer *composer);
+GtkWidget *e_plugin_lib_get_configure_widget (EPlugin *epl);
+void face_handle_send (EPlugin *ep, EMEventTargetComposer *target);
+
+/* ----------------------------------------------------------------- */
 
 gboolean
 e_plugin_ui_init (GtkUIManager *ui_manager,
-                  EMsgComposer *composer)
+		  EMsgComposer *composer)
 {
 	GtkhtmlEditor *editor;
 
+	static GtkToggleActionEntry entries[] = {
+		{ "face-plugin",
+		NULL,
+		N_("Include _Face"),
+		NULL,
+		NULL,
+		G_CALLBACK (action_toggle_face_cb),
+		FALSE }
+	};
+
+	if (get_include_face_by_default ()) {
+		gchar *face = get_face_base64 ();
+
+		/* activate it only if has a face image available */
+		entries[0].is_active = face && *face;
+
+		g_free (face);
+	}
+
 	editor = GTKHTML_EDITOR (composer);
 
 	/* Add actions to the "composer" action group. */
-	gtk_action_group_add_actions (
+	gtk_action_group_add_toggle_actions (
 		gtkhtml_editor_get_action_group (editor, "composer"),
 		entries, G_N_ELEMENTS (entries), composer);
 
 	return TRUE;
 }
+
+GtkWidget *
+e_plugin_lib_get_configure_widget (EPlugin *epl)
+{
+	return get_cfg_widget ();
+}
+
+void
+face_handle_send (EPlugin *ep, EMEventTargetComposer *target)
+{
+	GtkhtmlEditor *editor;
+	GtkAction *action;
+
+	editor = GTKHTML_EDITOR (target->composer);
+	action = gtkhtml_editor_get_action (editor, "face-plugin");
+
+	g_return_if_fail (action != NULL);
+
+	if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
+		gchar *face = get_face_base64 ();
+
+		if (face)
+			e_msg_composer_modify_header (target->composer, "Face", face);
+
+		g_free (face);
+	}
+}
diff --git a/plugins/face/org-gnome-face.eplug.xml b/plugins/face/org-gnome-face.eplug.xml
index 08e30b5..da3159a 100644
--- a/plugins/face/org-gnome-face.eplug.xml
+++ b/plugins/face/org-gnome-face.eplug.xml
@@ -3,20 +3,22 @@
   <e-plugin id="org.gnome.evolution.face" type="shlib" _name="Face" location="@PLUGINDIR@/liborg-gnome-face SOEXT@">
 
     <author name="Sankar P" email="psankar novell com"/>
-    <_description xml:space="preserve">Attach a small picture of your face to outgoing messages.
-
-First time the user needs to configure a 48x48 PNG image. It is Base-64 encoded and stored in ~/.evolution/faces. This will be used in subsequent sent messages.</_description>
+    <_description xml:space="preserve">Attach a small picture of your face to outgoing messages.</_description>
 
     <hook class="org.gnome.evolution.ui:1.0">
       <ui-manager id="org.gnome.evolution.composer">
         <menubar name='main-menu'>
           <menu action='insert-menu'>
             <placeholder name="insert-menu-top">
-	      <menuitem action="face"/>
+	      <menuitem action="face-plugin"/>
             </placeholder>
           </menu>
         </menubar>
       </ui-manager>
     </hook>
+
+    <hook class="org.gnome.evolution.mail.events:1.0">
+	<event id="composer.presendchecks" handle="face_handle_send" target="message"/>
+    </hook>
   </e-plugin>
 </e-plugin-list>
diff --git a/plugins/face/org-gnome-face.error.xml b/plugins/face/org-gnome-face.error.xml
index 02695dd..7542c91 100644
--- a/plugins/face/org-gnome-face.error.xml
+++ b/plugins/face/org-gnome-face.error.xml
@@ -2,23 +2,19 @@
 <error-list domain="org.gnome.evolution.plugins.face">
 
 	<error id="file-not-found" type="error">
-		<primary>Failed Read </primary>
-		<secondary>The file cannot be read</secondary>
+		<_primary>Failed Read </_primary>
+		<_secondary>The file cannot be read</_secondary>
 	</error>
 
 	<error id="invalid-image-size" type="error">
-		<primary>Invalid Image Size</primary>
-		<secondary>Please select an image of size 48 * 48 </secondary>
-	</error>
-
-	<error id="invalid-file-size" type="error">
-		<primary>Invalid File Size</primary>
-		<secondary>The file you selected is too big. Please select a file of size less than 720 bytes. </secondary>
+		<_primary>Invalid Image Size</_primary>
+		<_secondary>Please select an image of size 48 * 48 </_secondary>
 	</error>
 
 	<error id="not-an-image" type="error">
-		<primary>Not an image </primary>
-		<secondary>The file you selected does not look like a valid .png image.</secondary>
+		<_primary>Not an image </_primary>
+		<_secondary>The file you selected does not look like a valid .png image.
+Error: {0}</_secondary>
 	</error>
 
 </error-list>



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