evolution-3.6.4/plugins/face/face.c

No issues found

  1 /*
  2  *
  3  * This program is free software; you can redistribute it and/or
  4  * modify it under the terms of the GNU Lesser General Public
  5  * License as published by the Free Software Foundation; either
  6  * version 2 of the License, or (at your option) version 3.
  7  *
  8  * This program is distributed in the hope that it will be useful,
  9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 11  * Lesser General Public License for more details.
 12  *
 13  * You should have received a copy of the GNU Lesser General Public
 14  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 15  *
 16  *
 17  * Authors:
 18  *		Sankar P <psankar@novell.com>
 19  *
 20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 21  *
 22  */
 23 
 24 #ifdef HAVE_CONFIG_H
 25 #include <config.h>
 26 #endif
 27 
 28 #include "composer/e-msg-composer.h"
 29 #include <gtk/gtk.h>
 30 #include <glib/gi18n-lib.h>
 31 #include <mail/em-event.h>
 32 #include <libevolution-utils/e-alert-dialog.h>
 33 #include <e-util/e-util.h>
 34 #include <e-util/e-icon-factory.h>
 35 
 36 #define d(x)
 37 
 38 #define SETTINGS_KEY "insert-face-picture"
 39 
 40 static gboolean
 41 get_include_face_by_default (void)
 42 {
 43 	GSettings *settings = g_settings_new ("org.gnome.evolution.plugin.face-picture");
 44 	gboolean res;
 45 
 46 	res = g_settings_get_boolean (settings, SETTINGS_KEY);
 47 
 48 	g_object_unref (settings);
 49 
 50 	return res;
 51 }
 52 
 53 static void
 54 set_include_face_by_default (gboolean value)
 55 {
 56 	GSettings *settings = g_settings_new ("org.gnome.evolution.plugin.face-picture");
 57 
 58 	g_settings_set_boolean (settings, SETTINGS_KEY, value);
 59 
 60 	g_object_unref (settings);
 61 }
 62 
 63 static gchar *
 64 get_filename (void)
 65 {
 66 	return g_build_filename (e_get_user_data_dir (), "faces", NULL);
 67 }
 68 
 69 static gchar *
 70 get_face_base64 (void)
 71 {
 72 	gchar *filename = get_filename (), *file_contents = NULL;
 73 	gsize length = 0;
 74 
 75 	if (g_file_get_contents (filename, &file_contents, &length, NULL)) {
 76 		if (length > 0) {
 77 			file_contents = g_realloc (file_contents, length + 1);
 78 			file_contents[length] = 0;
 79 		} else {
 80 			g_free (file_contents);
 81 			file_contents = NULL;
 82 		}
 83 	} else {
 84 		file_contents = NULL;
 85 	}
 86 
 87 	g_free (filename);
 88 
 89 	return file_contents;
 90 }
 91 
 92 static void
 93 set_face_raw (gchar *content,
 94               gsize length)
 95 {
 96 	gchar *filename = get_filename ();
 97 
 98 	if (content) {
 99 		gchar *file_contents;
100 
101 		file_contents = g_base64_encode ((guchar *) content, length);
102 		g_file_set_contents (filename, file_contents, -1, NULL);
103 		g_free (file_contents);
104 	} else {
105 		g_file_set_contents (filename, "", -1, NULL);
106 	}
107 
108 	g_free (filename);
109 }
110 
111 /* g_object_unref returned pointer when done with it */
112 static GdkPixbuf *
113 get_active_face (void)
114 {
115 	GdkPixbufLoader *loader;
116 	GdkPixbuf *res = NULL;
117 	gchar *face;
118 	guchar *data;
119 	gsize data_len = 0;
120 
121 	face = get_face_base64 ();
122 
123 	if (!face || !*face) {
124 		g_free (face);
125 		return NULL;
126 	}
127 
128 	data = g_base64_decode (face, &data_len);
129 	if (!data || !data_len) {
130 		g_free (face);
131 		g_free (data);
132 		return NULL;
133 	}
134 
135 	g_free (face);
136 
137 	loader = gdk_pixbuf_loader_new ();
138 
139 	if (gdk_pixbuf_loader_write (loader, data, data_len, NULL)
140 	    && gdk_pixbuf_loader_close (loader, NULL)) {
141 		res = gdk_pixbuf_loader_get_pixbuf (loader);
142 		if (res)
143 			g_object_ref (res);
144 	}
145 
146 	g_object_unref (loader);
147 
148 	g_free (data);
149 
150 	return res;
151 }
152 
153 static gboolean
154 prepare_image (const gchar *image_filename,
155                gchar **file_contents,
156                gsize *length,
157                GdkPixbuf **use_pixbuf,
158                gboolean can_claim)
159 {
160 	gboolean res = FALSE;
161 
162 	g_return_val_if_fail (image_filename != NULL, FALSE);
163 	g_return_val_if_fail (file_contents != NULL, FALSE);
164 	g_return_val_if_fail (length != NULL, FALSE);
165 
166 	if (g_file_get_contents (image_filename, file_contents, length, NULL)) {
167 		GError *error = NULL;
168 		GdkPixbuf *pixbuf;
169 		GdkPixbufLoader *loader = gdk_pixbuf_loader_new ();
170 
171 		if (!gdk_pixbuf_loader_write (loader, (const guchar *)(*file_contents), *length, &error)
172 		    || !gdk_pixbuf_loader_close (loader, &error)
173 		    || (pixbuf = gdk_pixbuf_loader_get_pixbuf (loader)) == NULL) {
174 			const gchar *err = _("Unknown error");
175 
176 			if (error && error->message)
177 				err = error->message;
178 
179 			if (can_claim)
180 				e_alert_run_dialog_for_args (NULL, "org.gnome.evolution.plugins.face:not-an-image", err, NULL);
181 
182 			if (error)
183 				g_error_free (error);
184 		} else {
185 			gint width, height;
186 
187 			height = gdk_pixbuf_get_height (pixbuf);
188 			width = gdk_pixbuf_get_width (pixbuf);
189 
190 			if (height <= 0 || width <= 0) {
191 				if (can_claim)
192 					e_alert_run_dialog_for_args (NULL, "org.gnome.evolution.plugins.face:invalid-image-size", NULL, NULL);
193 			} else if (height != 48 || width != 48) {
194 				GdkPixbuf *copy, *scale;
195 				guchar *pixels;
196 				guint32 fill;
197 
198 				if (width >= height) {
199 					if (width > 48) {
200 						gdouble ratio = (gdouble) width / 48.0;
201 						width = 48;
202 						height = height / ratio;
203 
204 						if (height == 0)
205 							height = 1;
206 					}
207 				} else {
208 					if (height > 48) {
209 						gdouble ratio = (gdouble) height / 48.0;
210 						height = 48;
211 						width = width / ratio;
212 						if (width == 0)
213 							width = 1;
214 					}
215 				}
216 
217 				scale = e_icon_factory_pixbuf_scale (pixbuf, width, height);
218 				copy = e_icon_factory_pixbuf_scale (pixbuf, 48, 48);
219 
220 				width = gdk_pixbuf_get_width (scale);
221 				height = gdk_pixbuf_get_height (scale);
222 
223 				pixels = gdk_pixbuf_get_pixels (scale);
224 				/* fill with a pixel color at [0,0] */
225 				fill = (pixels[0] << 24) | (pixels[1] << 16) | (pixels[2] << 8) | (pixels[0]);
226 				gdk_pixbuf_fill (copy, fill);
227 
228 				gdk_pixbuf_copy_area (scale, 0, 0, width, height, copy, width < 48 ? (48 - width) / 2 : 0, height < 48 ? (48 - height) / 2 : 0);
229 
230 				g_free (*file_contents);
231 				*file_contents = NULL;
232 				*length = 0;
233 
234 				res = gdk_pixbuf_save_to_buffer (copy, file_contents, length, "png", NULL, "compression", "9", NULL);
235 
236 				if (res && use_pixbuf)
237 					*use_pixbuf = g_object_ref (copy);
238 				g_object_unref (copy);
239 				g_object_unref (scale);
240 			} else {
241 				res = TRUE;
242 				if (use_pixbuf)
243 					*use_pixbuf = g_object_ref (pixbuf);
244 			}
245 		}
246 
247 		g_object_unref (loader);
248 	} else {
249 		if (can_claim)
250 			e_alert_run_dialog_for_args (NULL, "org.gnome.evolution.plugins.face:file-not-found", NULL, NULL);
251 	}
252 
253 	return res;
254 }
255 
256 static void
257 update_preview_cb (GtkFileChooser *file_chooser,
258                    gpointer data)
259 {
260 	GtkWidget *preview;
261 	gchar *filename, *file_contents = NULL;
262 	GdkPixbuf *pixbuf = NULL;
263 	gboolean have_preview;
264 	gsize length = 0;
265 
266 	preview = GTK_WIDGET (data);
267 	filename = gtk_file_chooser_get_preview_filename (file_chooser);
268 
269 	have_preview = filename && prepare_image (filename, &file_contents, &length, &pixbuf, FALSE);
270 	if (have_preview) {
271 		g_free (file_contents);
272 		have_preview = pixbuf != NULL;
273 	}
274 
275 	g_free (filename);
276 
277 	gtk_image_set_from_pixbuf (GTK_IMAGE (preview), pixbuf);
278 	if (pixbuf)
279 		g_object_unref (pixbuf);
280 
281 	gtk_file_chooser_set_preview_widget_active (file_chooser, have_preview);
282 }
283 
284 static GdkPixbuf *
285 choose_new_face (void)
286 {
287 	GdkPixbuf *res = NULL;
288 	GtkWidget *filesel, *preview;
289 	GtkFileFilter *filter;
290 
291 	filesel = gtk_file_chooser_dialog_new (
292 				_("Select a Face Picture"),
293 				NULL,
294 				GTK_FILE_CHOOSER_ACTION_OPEN,
295 				GTK_STOCK_CANCEL,
296 				GTK_RESPONSE_CANCEL,
297 				GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
298 
299 	gtk_dialog_set_default_response (GTK_DIALOG (filesel), GTK_RESPONSE_OK);
300 
301 	filter = gtk_file_filter_new ();
302 	gtk_file_filter_set_name (filter, _("Image files"));
303 	gtk_file_filter_add_mime_type (filter, "image/*");
304 	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (filesel), filter);
305 
306 	preview = gtk_image_new ();
307 	gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (filesel), preview);
308 	g_signal_connect (
309 		filesel, "update-preview",
310 		G_CALLBACK (update_preview_cb), preview);
311 
312 	if (GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (filesel))) {
313 		gchar *image_filename, *file_contents = NULL;
314 		gsize length = 0;
315 
316 		image_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filesel));
317 		gtk_widget_destroy (filesel);
318 
319 		if (prepare_image (image_filename, &file_contents, &length, &res, TRUE)) {
320 			set_face_raw (file_contents, length);
321 		}
322 
323 		g_free (file_contents);
324 		g_free (image_filename);
325 	} else {
326 		gtk_widget_destroy (filesel);
327 	}
328 
329 	return res;
330 }
331 
332 static void
333 toggled_check_include_by_default_cb (GtkWidget *widget,
334                                      gpointer data)
335 {
336 	set_include_face_by_default (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
337 }
338 
339 static void
340 click_load_face_cb (GtkButton *butt,
341                     GtkImage *image)
342 {
343 	GdkPixbuf *face;
344 
345 	face = choose_new_face ();
346 
347 	if (face) {
348 		gtk_image_set_from_pixbuf (image, face);
349 		g_object_unref (face);
350 	}
351 }
352 
353 static GtkWidget *
354 get_cfg_widget (void)
355 {
356 	GtkWidget *vbox, *check, *img, *butt;
357 	GdkPixbuf *face;
358 
359 	vbox = gtk_vbox_new (FALSE, 6);
360 
361 	check = gtk_check_button_new_with_mnemonic (_("_Insert Face picture by default"));
362 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), get_include_face_by_default ());
363 	g_signal_connect (
364 		check, "toggled",
365 		G_CALLBACK (toggled_check_include_by_default_cb), NULL);
366 
367 	gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0);
368 
369 	face = get_active_face ();
370 	img = gtk_image_new_from_pixbuf (face);
371 	if (face)
372 		g_object_unref (face);
373 
374 	butt = gtk_button_new_with_mnemonic (_("Load new _Face picture"));
375 	g_signal_connect (
376 		butt, "clicked",
377 		G_CALLBACK (click_load_face_cb), img);
378 
379 	gtk_box_pack_start (GTK_BOX (vbox), butt, FALSE, FALSE, 0);
380 
381 	gtk_box_pack_start (GTK_BOX (vbox), img, FALSE, FALSE, 0);
382 
383 	gtk_widget_show_all (vbox);
384 
385 	return vbox;
386 }
387 
388 static void
389 action_toggle_face_cb (GtkToggleAction *action,
390                        EMsgComposer *composer)
391 {
392 	if (gtk_toggle_action_get_active (action)) {
393 		gchar *face = get_face_base64 ();
394 
395 		if (!face) {
396 			GdkPixbuf *pixbuf = choose_new_face ();
397 
398 			if (pixbuf) {
399 				g_object_unref (pixbuf);
400 			} else {
401 				/* cannot load a face image, uncheck the option */
402 				gtk_toggle_action_set_active (action, FALSE);
403 			}
404 		} else {
405 			g_free (face);
406 		}
407 	}
408 }
409 
410 /* ----------------------------------------------------------------- */
411 
412 gint e_plugin_lib_enable (EPlugin *ep, gint enable);
413 gboolean e_plugin_ui_init (GtkUIManager *ui_manager, EMsgComposer *composer);
414 GtkWidget *e_plugin_lib_get_configure_widget (EPlugin *epl);
415 void face_handle_send (EPlugin *ep, EMEventTargetComposer *target);
416 
417 /* ----------------------------------------------------------------- */
418 
419 gint
420 e_plugin_lib_enable (EPlugin *ep,
421                      gint enable)
422 {
423 	return 0;
424 }
425 
426 gboolean
427 e_plugin_ui_init (GtkUIManager *ui_manager,
428                   EMsgComposer *composer)
429 {
430 	GtkhtmlEditor *editor;
431 
432 	static GtkToggleActionEntry entries[] = {
433 		{ "face-plugin",
434 		NULL,
435 		N_("Include _Face"),
436 		NULL,
437 		NULL,
438 		G_CALLBACK (action_toggle_face_cb),
439 		FALSE }
440 	};
441 
442 	if (get_include_face_by_default ()) {
443 		gchar *face = get_face_base64 ();
444 
445 		/* activate it only if has a face image available */
446 		entries[0].is_active = face && *face;
447 
448 		g_free (face);
449 	}
450 
451 	editor = GTKHTML_EDITOR (composer);
452 
453 	/* Add actions to the "composer" action group. */
454 	gtk_action_group_add_toggle_actions (
455 		gtkhtml_editor_get_action_group (editor, "composer"),
456 		entries, G_N_ELEMENTS (entries), composer);
457 
458 	return TRUE;
459 }
460 
461 GtkWidget *
462 e_plugin_lib_get_configure_widget (EPlugin *epl)
463 {
464 	return get_cfg_widget ();
465 }
466 
467 void
468 face_handle_send (EPlugin *ep,
469                   EMEventTargetComposer *target)
470 {
471 	GtkhtmlEditor *editor;
472 	GtkAction *action;
473 
474 	editor = GTKHTML_EDITOR (target->composer);
475 	action = gtkhtml_editor_get_action (editor, "face-plugin");
476 
477 	g_return_if_fail (action != NULL);
478 
479 	if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
480 		gchar *face = get_face_base64 ();
481 
482 		if (face)
483 			e_msg_composer_set_header (target->composer, "Face", face);
484 
485 		g_free (face);
486 	}
487 }