diff options
Diffstat (limited to 'capplets/about-me/e-image-chooser.c')
-rw-r--r-- | capplets/about-me/e-image-chooser.c | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/capplets/about-me/e-image-chooser.c b/capplets/about-me/e-image-chooser.c new file mode 100644 index 00000000..31a189f1 --- /dev/null +++ b/capplets/about-me/e-image-chooser.c @@ -0,0 +1,455 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* e-image-chooser.c + * Copyright (C) 2004 Novell, Inc. + * Author: Chris Toshok <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> + +#include <string.h> + +#include <glib-object.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <gtk/gtk.h> + +#include "e-image-chooser.h" + +struct _EImageChooserPrivate { + + GtkWidget *image; + GtkWidget *browse_button; + + char *image_buf; + int image_buf_size; + int image_width; + int image_height; + + gboolean editable; +}; + +enum { + CHANGED, + LAST_SIGNAL +}; + + +static gint image_chooser_signals [LAST_SIGNAL] = { 0 }; + +static void e_image_chooser_init (EImageChooser *chooser); +static void e_image_chooser_class_init (EImageChooserClass *klass); +static void e_image_chooser_dispose (GObject *object); + +static gboolean image_drag_motion_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, guint time, EImageChooser *chooser); +static gboolean image_drag_drop_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, guint time, EImageChooser *chooser); +static void image_drag_data_received_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, + GtkSelectionData *selection_data, + guint info, guint time, EImageChooser *chooser); + +static GtkObjectClass *parent_class = NULL; +#define PARENT_TYPE GTK_TYPE_VBOX + +enum DndTargetType { + DND_TARGET_TYPE_URI_LIST +}; +#define URI_LIST_TYPE "text/uri-list" + +static GtkTargetEntry image_drag_types[] = { + { URI_LIST_TYPE, 0, DND_TARGET_TYPE_URI_LIST }, +}; +static const int num_image_drag_types = sizeof (image_drag_types) / sizeof (image_drag_types[0]); + +GtkWidget * +e_image_chooser_new (void) +{ + return g_object_new (E_TYPE_IMAGE_CHOOSER, NULL); +} + +GType +e_image_chooser_get_type (void) +{ + static GType eic_type = 0; + + if (!eic_type) { + static const GTypeInfo eic_info = { + sizeof (EImageChooserClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) e_image_chooser_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (EImageChooser), + 0, /* n_preallocs */ + (GInstanceInitFunc) e_image_chooser_init, + }; + + eic_type = g_type_register_static (PARENT_TYPE, "EImageChooser", &eic_info, 0); + } + + return eic_type; +} + + +static void +e_image_chooser_class_init (EImageChooserClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_ref (PARENT_TYPE); + + image_chooser_signals [CHANGED] = + g_signal_new ("changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EImageChooserClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + GTK_TYPE_NONE, 0); + + object_class->dispose = e_image_chooser_dispose; +} + +static void +e_image_chooser_init (EImageChooser *chooser) +{ + EImageChooserPrivate *priv; + + priv = chooser->priv = g_new0 (EImageChooserPrivate, 1); + + priv->image = gtk_image_new (); + + gtk_box_set_homogeneous (GTK_BOX (chooser), FALSE); + gtk_box_pack_start (GTK_BOX (chooser), priv->image, TRUE, TRUE, 0); + + gtk_drag_dest_set (priv->image, 0, image_drag_types, num_image_drag_types, GDK_ACTION_COPY); + g_signal_connect (priv->image, + "drag_motion", G_CALLBACK (image_drag_motion_cb), chooser); + g_signal_connect (priv->image, + "drag_drop", G_CALLBACK (image_drag_drop_cb), chooser); + g_signal_connect (priv->image, + "drag_data_received", G_CALLBACK (image_drag_data_received_cb), chooser); + + gtk_widget_show_all (priv->image); + + /* we default to being editable */ + priv->editable = TRUE; +} + +static void +e_image_chooser_dispose (GObject *object) +{ + EImageChooser *eic = E_IMAGE_CHOOSER (object); + + if (eic->priv) { + EImageChooserPrivate *priv = eic->priv; + + if (priv->image_buf) { + g_free (priv->image_buf); + priv->image_buf = NULL; + } + + g_free (eic->priv); + eic->priv = NULL; + } + + if (G_OBJECT_CLASS (parent_class)->dispose) + (* G_OBJECT_CLASS (parent_class)->dispose) (object); +} + + +static gboolean +set_image_from_data (EImageChooser *chooser, + char *data, int length) +{ + gboolean rv = FALSE; + GdkPixbufLoader *loader = gdk_pixbuf_loader_new (); + GdkPixbuf *pixbuf; + + gdk_pixbuf_loader_write (loader, data, length, NULL); + gdk_pixbuf_loader_close (loader, NULL); + + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + if (pixbuf) + g_object_ref (pixbuf); + g_object_unref (loader); + + if (pixbuf) { + GdkPixbuf *scaled; + GtkRequisition chooser_size; + + float scale; + int new_height, new_width; + + gtk_widget_size_request (gtk_widget_get_parent (GTK_WIDGET (chooser)), + &chooser_size); + chooser_size.width -= 5; + chooser_size.height -= 5; + + new_height = gdk_pixbuf_get_height (pixbuf); + new_width = gdk_pixbuf_get_width (pixbuf); + + if (chooser->priv->image_height == 0 + && chooser->priv->image_width == 0) { + scale = 1.0; + } + else if (chooser->priv->image_height < new_height + || chooser->priv->image_width < new_width) { + /* we need to scale down */ + if (new_height > new_width) + scale = (float)chooser_size.height / new_height; + else + scale = (float)chooser_size.width / new_width; + } + else { + /* we need to scale up */ + if (new_height > new_width) + scale = (float)new_height / chooser_size.height; + else + scale = (float)new_width / chooser_size.width; + } + + if (scale == 1.0) { + gtk_image_set_from_pixbuf (GTK_IMAGE (chooser->priv->image), pixbuf); + + chooser->priv->image_width = new_width; + chooser->priv->image_height = new_height; + } + else { + new_width *= scale; + new_height *= scale; + new_width = MIN (new_width, chooser_size.width); + new_height = MIN (new_height, chooser_size.height); + + scaled = gdk_pixbuf_scale_simple (pixbuf, + new_width, new_height, + GDK_INTERP_BILINEAR); + + gtk_image_set_from_pixbuf (GTK_IMAGE (chooser->priv->image), scaled); + g_object_unref (scaled); + } + + g_object_unref (pixbuf); + + g_free (chooser->priv->image_buf); + chooser->priv->image_buf = data; + chooser->priv->image_buf_size = length; + + g_signal_emit (chooser, + image_chooser_signals [CHANGED], 0); + + rv = TRUE; + } + + return rv; +} + +static gboolean +image_drag_motion_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, guint time, EImageChooser *chooser) +{ + GList *p; + + if (!chooser->priv->editable) + return FALSE; + + for (p = context->targets; p != NULL; p = p->next) { + char *possible_type; + + possible_type = gdk_atom_name (GDK_POINTER_TO_ATOM (p->data)); + if (!strcmp (possible_type, URI_LIST_TYPE)) { + g_free (possible_type); + gdk_drag_status (context, GDK_ACTION_COPY, time); + return TRUE; + } + + g_free (possible_type); + } + + return FALSE; +} + +static gboolean +image_drag_drop_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, guint time, EImageChooser *chooser) +{ + GList *p; + + if (!chooser->priv->editable) + return FALSE; + + if (context->targets == NULL) { + return FALSE; + } + + for (p = context->targets; p != NULL; p = p->next) { + char *possible_type; + + possible_type = gdk_atom_name (GDK_POINTER_TO_ATOM (p->data)); + if (!strcmp (possible_type, URI_LIST_TYPE)) { + g_free (possible_type); + gtk_drag_get_data (widget, context, + GDK_POINTER_TO_ATOM (p->data), + time); + return TRUE; + } + + g_free (possible_type); + } + + return FALSE; +} + +static void +image_drag_data_received_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, gint y, + GtkSelectionData *selection_data, + guint info, guint time, EImageChooser *chooser) +{ + char *target_type; + gboolean handled = FALSE; + + target_type = gdk_atom_name (gtk_selection_data_get_target (selection_data)); + + if (!strcmp (target_type, URI_LIST_TYPE)) { + const char *data = gtk_selection_data_get_data (selection_data); + char *uri; + GFile *file; + GInputStream *istream; + char *nl = strstr (data, "\r\n"); + + if (nl) + uri = g_strndup (data, nl - (char *) data); + else + uri = g_strdup (data); + + file = g_file_new_for_uri (uri); + istream = G_INPUT_STREAM (g_file_read (file, NULL, NULL)); + + if (istream != NULL) { + GFileInfo *info; + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_SIZE, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + + if (info != NULL) { + gsize size; + gboolean success; + gchar *buf; + + size = g_file_info_get_size (info); + g_object_unref (info); + + buf = g_malloc (size); + + success = g_input_stream_read_all (istream, + buf, + size, + &size, + NULL, + NULL); + g_input_stream_close (istream, NULL, NULL); + + if (success && + set_image_from_data (chooser, buf, size)) + handled = TRUE; + else + g_free (buf); + } + + g_object_unref (istream); + } + + g_object_unref (file); + g_free (uri); + } + + gtk_drag_finish (context, handled, FALSE, time); +} + +gboolean +e_image_chooser_set_from_file (EImageChooser *chooser, const char *filename) +{ + gchar *data; + gsize data_length; + + g_return_val_if_fail (E_IS_IMAGE_CHOOSER (chooser), FALSE); + g_return_val_if_fail (filename, FALSE); + + if (!g_file_get_contents (filename, &data, &data_length, NULL)) { + return FALSE; + } + + if (!set_image_from_data (chooser, data, data_length)) + g_free (data); + + return TRUE; +} + +void +e_image_chooser_set_editable (EImageChooser *chooser, gboolean editable) +{ + g_return_if_fail (E_IS_IMAGE_CHOOSER (chooser)); + + chooser->priv->editable = editable; + + gtk_widget_set_sensitive (chooser->priv->browse_button, editable); +} + +gboolean +e_image_chooser_get_image_data (EImageChooser *chooser, char **data, gsize *data_length) +{ + g_return_val_if_fail (E_IS_IMAGE_CHOOSER (chooser), FALSE); + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (data_length != NULL, FALSE); + + *data_length = chooser->priv->image_buf_size; + *data = g_malloc (*data_length); + memcpy (*data, chooser->priv->image_buf, *data_length); + + return TRUE; +} + +gboolean +e_image_chooser_set_image_data (EImageChooser *chooser, char *data, gsize data_length) +{ + char *buf; + + g_return_val_if_fail (E_IS_IMAGE_CHOOSER (chooser), FALSE); + g_return_val_if_fail (data != NULL, FALSE); + + /* yuck, a copy... */ + buf = g_malloc (data_length); + memcpy (buf, data, data_length); + + if (!set_image_from_data (chooser, buf, data_length)) { + g_free (buf); + return FALSE; + } + + return TRUE; +} |