/* * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the program; if not, see * * * Authors: * Sankar P * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include "composer/e-msg-composer.h" #include #include #include #define d(x) #define SETTINGS_KEY "insert-face-picture" static gboolean get_include_face_by_default (void) { GSettings *settings = g_settings_new ("org.gnome.evolution.plugin.face-picture"); gboolean res; res = g_settings_get_boolean (settings, SETTINGS_KEY); g_object_unref (settings); return res; } static void set_include_face_by_default (gboolean value) { GSettings *settings = g_settings_new ("org.gnome.evolution.plugin.face-picture"); g_settings_set_boolean (settings, SETTINGS_KEY, value); g_object_unref (settings); } static gchar * get_filename (void) { return g_build_filename (e_get_user_data_dir (), "faces", NULL); } static gchar * get_face_base64 (void) { gchar *filename = get_filename (), *file_contents = NULL; gsize length = 0; 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; } } 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); g_free (data); 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_alert_run_dialog_for_args (NULL, "org.gnome.evolution.plugins.face:not-an-image", err, NULL); if (error) g_error_free (error); } else { gint width, height; height = gdk_pixbuf_get_height (pixbuf); width = gdk_pixbuf_get_width (pixbuf); if (height <= 0 || width <= 0) { if (can_claim) e_alert_run_dialog_for_args (NULL, "org.gnome.evolution.plugins.face:invalid-image-size", NULL, NULL); } else if (height != 48 || width != 48) { GdkPixbuf *copy, *scale; guchar *pixels; guint32 fill; if (width >= height) { if (width > 48) { gdouble ratio = (gdouble) width / 48.0; width = 48; height = height / ratio; if (height == 0) height = 1; } } else { 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 { res = TRUE; if (use_pixbuf) *use_pixbuf = g_object_ref (pixbuf); } } g_object_unref (loader); } else { if (can_claim) e_alert_run_dialog_for_args (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 Face Picture"), 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); } 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, GtkImage *image) { GdkPixbuf *face; face = choose_new_face (); if (face) { gtk_image_set_from_pixbuf (image, face); g_object_unref (face); } } static GtkWidget * get_cfg_widget (void) { GtkWidget *vbox, *check, *img, *butt; GdkPixbuf *face; vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 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")); g_signal_connect ( butt, "clicked", G_CALLBACK (click_load_face_cb), img); gtk_box_pack_start (GTK_BOX (vbox), butt, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), img, 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); } } } /* ----------------------------------------------------------------- */ gint e_plugin_lib_enable (EPlugin *ep, gint enable); 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); /* ----------------------------------------------------------------- */ gint e_plugin_lib_enable (EPlugin *ep, gint enable) { return 0; } gboolean e_plugin_ui_init (GtkUIManager *ui_manager, 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_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_set_header (target->composer, "Face", face); g_free (face); } }