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 }