diff options
Diffstat (limited to 'plugins/face')
-rw-r--r-- | plugins/face/Makefile.am | 39 | ||||
-rw-r--r-- | plugins/face/apps_evolution_eplugin_face.schemas.in | 17 | ||||
-rw-r--r-- | plugins/face/face.c | 451 | ||||
-rw-r--r-- | plugins/face/org-gnome-face.eplug.xml | 10 | ||||
-rw-r--r-- | plugins/face/org-gnome-face.error.xml | 18 |
5 files changed, 450 insertions, 85 deletions
diff --git a/plugins/face/Makefile.am b/plugins/face/Makefile.am index 2d307f85c7..29444dd3f2 100644 --- a/plugins/face/Makefile.am +++ b/plugins/face/Makefile.am @@ -30,14 +30,43 @@ error_DATA = org-gnome-face.error errordir = $(privdatadir)/errors -EXTRA_DIST = \ - org-gnome-face.eplug.xml \ - org-gnome-face.error.xml +# GConf schemas -BUILT_SOURCES = \ - org-gnome-face.eplug \ +schemadir = $(GCONF_SCHEMA_FILE_DIR) +schema_in_files = apps_evolution_eplugin_face.schemas.in +schema_DATA = $(schema_in_files:.schemas.in=.schemas) + +@INTLTOOL_SCHEMAS_RULE@ + +EXTRA_DIST = \ + org-gnome-face.eplug.xml \ + org-gnome-face.error.xml \ + $(schema_in_files) + +BUILT_SOURCES = \ + org-gnome-face.eplug \ org-gnome-face.error CLEANFILES = $(BUILT_SOURCES) +DISTCLEANFILES = $(schema_DATA) + +if OS_WIN32 +install-data-local: + if test -z "$(DESTDIR)"; then \ + for p in $(schema_DATA); do \ + (echo set GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE); \ + echo $(GCONFTOOL) --makefile-install-rule $$p) >_temp.bat; \ + cmd /c _temp.bat; \ + done; \ + fi +else +install-data-local: + if test -z "$(DESTDIR)"; then \ + for p in $(schema_DATA); do \ + GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $$p; \ + done; \ + fi +endif + -include $(top_srcdir)/git.mk diff --git a/plugins/face/apps_evolution_eplugin_face.schemas.in b/plugins/face/apps_evolution_eplugin_face.schemas.in new file mode 100644 index 0000000000..7583172f22 --- /dev/null +++ b/plugins/face/apps_evolution_eplugin_face.schemas.in @@ -0,0 +1,17 @@ +<gconfschemafile> + <schemalist> + + <schema> + <key>/schemas/apps/evolution/eplugin/face/insert_by_default</key> + <applyto>/apps/evolution/eplugin/face/insert_by_default</applyto> + <owner>org.gnome.evolution.plugins.face</owner> + <type>bool</type> + <default>false</default> + <locale name="C"> + <short>Insert Face picture by default</short> + <long>Whether insert Face picture to outgoing messages by default. The picture should be set before checking this, otherwise nothing happens.</long> + </locale> + </schema> + + </schemalist> +</gconfschemafile> diff --git a/plugins/face/face.c b/plugins/face/face.c index 3e462da5b1..19856b3e58 100644 --- a/plugins/face/face.c +++ b/plugins/face/face.c @@ -28,118 +28,439 @@ #include "composer/e-msg-composer.h" #include <gtk/gtk.h> #include <glib/gi18n.h> +#include <mail/em-event.h> #include <e-util/e-error.h> #include <e-util/e-util.h> +#include <e-util/e-icon-factory.h> #define d(x) -gboolean e_plugin_ui_init (GtkUIManager *ui_manager, - EMsgComposer *composer); +#define SETTINGS_KEY "/apps/evolution/eplugin/face/insert_by_default" -static void -action_face_cb (GtkAction *action, - EMsgComposer *composer) +static gboolean +get_include_face_by_default (void) { - gchar *filename, *file_contents; - GError *error = NULL; + GConfClient *gconf = gconf_client_get_default (); + gboolean res; - filename = g_build_filename (e_get_user_data_dir (), "faces", NULL); - g_file_get_contents (filename, &file_contents, NULL, &error); + res = gconf_client_get_bool (gconf, SETTINGS_KEY, NULL); - if (error) { + g_object_unref (gconf); - GtkWidget *filesel; - const gchar *image_filename; - gsize length; + return res; +} - GtkFileFilter *filter; +static void +set_include_face_by_default (gboolean value) +{ + GConfClient *gconf = gconf_client_get_default (); - filesel = gtk_file_chooser_dialog_new (_ - ("Select a (48*48) png of size < 700bytes"), - NULL, - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); + gconf_client_set_bool (gconf, SETTINGS_KEY, value, NULL); - gtk_dialog_set_default_response (GTK_DIALOG (filesel), GTK_RESPONSE_OK); + g_object_unref (gconf); +} - filter = gtk_file_filter_new (); - gtk_file_filter_set_name (filter, _("PNG files")); - gtk_file_filter_add_mime_type (filter, "image/png"); - gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (filesel), filter); +static gchar * +get_filename (void) +{ + return g_build_filename (e_get_user_data_dir (), "faces", NULL); +} - if (GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (filesel))) { - image_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filesel)); +static gchar * +get_face_base64 (void) +{ + gchar *filename = get_filename (), *file_contents = NULL; + gsize length = 0; - error = NULL; + 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; - g_file_get_contents (image_filename, &file_contents, &length, &error); + } + } 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); - if (!error) { - error = NULL; - if (length < 720) { + g_free (data); - GdkPixbuf *pixbuf; - GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); + 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_error_run (NULL, "org.gnome.evolution.plugins.face:not-an-image", err, NULL); - gdk_pixbuf_loader_write (loader, (guchar *)file_contents, length, NULL); - gdk_pixbuf_loader_close (loader, NULL); + if (error) + g_error_free (error); + } else { + gint width, height; - pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); - if (pixbuf) { - gint width, height; + height = gdk_pixbuf_get_height (pixbuf); + width = gdk_pixbuf_get_width (pixbuf); - g_object_ref (pixbuf); + if (height <= 0 || width <= 0) { + if (can_claim) + e_error_run (NULL, "org.gnome.evolution.plugins.face:invalid-image-size", NULL, NULL); + } else if (height != 48 || width != 48) { + GdkPixbuf *copy, *scale; + guchar *pixels; + guint32 fill; - height = gdk_pixbuf_get_height (pixbuf); - width = gdk_pixbuf_get_width (pixbuf); + if (width >= height) { + if (width > 48) { + gdouble ratio = (gdouble) width / 48.0; + width = 48; + height = height / ratio; - if (height != 48 || width != 48) { - d (printf ("\n\a Invalid Image Size. Please choose a 48*48 image\n\a")); - e_error_run (GTK_WINDOW (filesel), "org.gnome.evolution.plugins.face:invalid-image-size", NULL, NULL); - } else { - file_contents = g_base64_encode ((guchar *) file_contents, length); - g_file_set_contents (filename, file_contents, -1, &error); - } + if (height == 0) + height = 1; } } else { - d (printf ("File too big")); - e_error_run (GTK_WINDOW (filesel), "org.gnome.evolution.plugins.face:invalid-file-size", NULL, NULL); + 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 { - d (printf ("\n\a File cannot be read\n\a")); - e_error_run (GTK_WINDOW (filesel), "org.gnome.evolution.plugins.face:file-not-found", NULL, NULL); + res = TRUE; + if (use_pixbuf) + *use_pixbuf = g_object_ref (pixbuf); } } + + g_object_unref (loader); + } else { + if (can_claim) + e_error_run (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); } - e_msg_composer_modify_header (composer, "Face", file_contents); + + 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); + } + } } -static GtkActionEntry entries[] = { +/* ----------------------------------------------------------------- */ - { "face", - NULL, - N_("_Face"), - NULL, - NULL, - G_CALLBACK (action_face_cb) } -}; +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); + +/* ----------------------------------------------------------------- */ gboolean e_plugin_ui_init (GtkUIManager *ui_manager, - EMsgComposer *composer) + 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_actions ( + 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_modify_header (target->composer, "Face", face); + + g_free (face); + } +} diff --git a/plugins/face/org-gnome-face.eplug.xml b/plugins/face/org-gnome-face.eplug.xml index 08e30b5948..da3159ae1b 100644 --- a/plugins/face/org-gnome-face.eplug.xml +++ b/plugins/face/org-gnome-face.eplug.xml @@ -3,20 +3,22 @@ <e-plugin id="org.gnome.evolution.face" type="shlib" _name="Face" location="@PLUGINDIR@/liborg-gnome-face@SOEXT@"> <author name="Sankar P" email="psankar@novell.com"/> - <_description xml:space="preserve">Attach a small picture of your face to outgoing messages. - -First time the user needs to configure a 48x48 PNG image. It is Base-64 encoded and stored in ~/.evolution/faces. This will be used in subsequent sent messages.</_description> + <_description xml:space="preserve">Attach a small picture of your face to outgoing messages.</_description> <hook class="org.gnome.evolution.ui:1.0"> <ui-manager id="org.gnome.evolution.composer"> <menubar name='main-menu'> <menu action='insert-menu'> <placeholder name="insert-menu-top"> - <menuitem action="face"/> + <menuitem action="face-plugin"/> </placeholder> </menu> </menubar> </ui-manager> </hook> + + <hook class="org.gnome.evolution.mail.events:1.0"> + <event id="composer.presendchecks" handle="face_handle_send" target="message"/> + </hook> </e-plugin> </e-plugin-list> diff --git a/plugins/face/org-gnome-face.error.xml b/plugins/face/org-gnome-face.error.xml index 02695dd758..7542c91af8 100644 --- a/plugins/face/org-gnome-face.error.xml +++ b/plugins/face/org-gnome-face.error.xml @@ -2,23 +2,19 @@ <error-list domain="org.gnome.evolution.plugins.face"> <error id="file-not-found" type="error"> - <primary>Failed Read </primary> - <secondary>The file cannot be read</secondary> + <_primary>Failed Read </_primary> + <_secondary>The file cannot be read</_secondary> </error> <error id="invalid-image-size" type="error"> - <primary>Invalid Image Size</primary> - <secondary>Please select an image of size 48 * 48 </secondary> - </error> - - <error id="invalid-file-size" type="error"> - <primary>Invalid File Size</primary> - <secondary>The file you selected is too big. Please select a file of size less than 720 bytes. </secondary> + <_primary>Invalid Image Size</_primary> + <_secondary>Please select an image of size 48 * 48 </_secondary> </error> <error id="not-an-image" type="error"> - <primary>Not an image </primary> - <secondary>The file you selected does not look like a valid .png image.</secondary> + <_primary>Not an image </_primary> + <_secondary>The file you selected does not look like a valid .png image. +Error: {0}</_secondary> </error> </error-list> |