Files
evolution/plugins/face/face.c
Matthew Barnes 4118d671d4 Composer: Show cancellable operations and errors inline.
'Send' and 'Save Draft' are now asynchronous and run outside of
Evolution's MailMsg infrastructure.

Add an EActivityBar to the composer window so these asynchronous
operations can be tracked and cancelled even in the absense of a main
window.  Also add an EAlertBar to the composer window so error messages
can be shown directly in the window.

Instead of calling e_alert_dialog_run_for_args(), call e_alert_submit()
and pass the EMsgComposer as the widget argument.  The EMsgComposer will
decide whether to show an EAlertDialog or use the EAlertBar, depending
on the GtkMessageType of the alert.
2010-10-12 13:59:00 -04:00

474 lines
11 KiB
C

/*
*
* 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 <http://www.gnu.org/licenses/>
*
*
* Authors:
* Sankar P <psankar@novell.com>
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "composer/e-msg-composer.h"
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <mail/em-event.h>
#include <e-util/e-alert-dialog.h>
#include <e-util/e-util.h>
#include <e-util/e-icon-factory.h>
#define d(x)
#define SETTINGS_KEY "/apps/evolution/eplugin/face/insert_by_default"
static gboolean
get_include_face_by_default (void)
{
GConfClient *gconf = gconf_client_get_default ();
gboolean res;
res = gconf_client_get_bool (gconf, SETTINGS_KEY, NULL);
g_object_unref (gconf);
return res;
}
static void
set_include_face_by_default (gboolean value)
{
GConfClient *gconf = gconf_client_get_default ();
gconf_client_set_bool (gconf, SETTINGS_KEY, value, NULL);
g_object_unref (gconf);
}
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 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);
}
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);
}
}
}
/* ----------------------------------------------------------------- */
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);
}
}