aboutsummaryrefslogblamecommitdiffstats
path: root/plugins/face/face.c
blob: c9023dc69b6f12459a878679fb26311cd36708bb (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
  
  



                                                                
  



                                                                    
  
                                                                   
                                                                             
  




                                                        








                                    
                           
                          
                                              
                          
                                  
 
            
 
                                          
 

                                  
 
                                                                                         
                     
 
                                                              
 
                                  
 

                   
 


                                            
                                                                                         
 
                                                               
 
                                  
 
 




                                                                        
 




                                                                 
 


                                                                              
                                                  

                                               
                                             










                                     

                             




                                          
 


































                                                                             
 









                                                                  
 
                      
 



                   




                                           




















                                                                                                        
                                                                                                                               
 



                                                     
 

                                                                
 

                                                        
                                                                                                                                              



                                                                 
 




                                                                                       
 

                                                                   

                                         






                                                                                        

                                 






















                                                                                                                                                                
                                


                                                                            

                         



                                        
                                                                                                                          





                   

                                                








                                                                        
 





















                                                                                                     

                                                           














                                                                                  


                                                         














                                                                                            

                                             




                   

                                                       




                                                                                                

                                    

                        



                                  
                                                        













                                                                                                 


                                                                        








                                                                          


                                                      


                                                                   
                                                                  
 





                                   

                                               
















                                                                                  
 
 
                                                                       
 
                                                    




                                                                             
 
    

                                 



                 
        
                                           
                                         


                              


















                                                                    


                                                         
                                             




                                                                     







                                                

                                                












                                                                        
                                                                                   



                              
/*
 *
 * 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-lib.h>
#include <mail/em-event.h>
#include <libevolution-utils/e-alert-dialog.h>
#include <e-util/e-util.h>
#include <e-util/e-icon-factory.h>

#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_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"));
    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);
    }
}