No issues found
1 /*
2 * e-image-chooser.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19 *
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <string.h>
28
29 #include <glib/gi18n.h>
30
31 #include "e-image-chooser.h"
32 #include "e-util/e-util.h"
33 #include "e-util/e-icon-factory.h"
34
35 #define E_IMAGE_CHOOSER_GET_PRIVATE(obj) \
36 (G_TYPE_INSTANCE_GET_PRIVATE \
37 ((obj), E_TYPE_IMAGE_CHOOSER, EImageChooserPrivate))
38
39 struct _EImageChooserPrivate {
40 GtkWidget *frame;
41 GtkWidget *image;
42
43 gchar *image_buf;
44 gint image_buf_size;
45 gint image_width;
46 gint image_height;
47
48 /* Default Image */
49 gchar *icon_name;
50 };
51
52 enum {
53 PROP_0,
54 PROP_ICON_NAME
55 };
56
57 enum {
58 CHANGED,
59 LAST_SIGNAL
60 };
61
62 static guint signals[LAST_SIGNAL];
63
64 #define URI_LIST_TYPE "text/uri-list"
65
66 G_DEFINE_TYPE (
67 EImageChooser,
68 e_image_chooser,
69 GTK_TYPE_VBOX)
70
71 static gboolean
72 set_image_from_data (EImageChooser *chooser,
73 gchar *data,
74 gint length)
75 {
76 GdkPixbufLoader *loader;
77 GdkPixbuf *pixbuf;
78 gfloat scale;
79 gint new_height;
80 gint new_width;
81
82 loader = gdk_pixbuf_loader_new ();
83 gdk_pixbuf_loader_write (loader, (guchar *) data, length, NULL);
84 gdk_pixbuf_loader_close (loader, NULL);
85
86 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
87 if (pixbuf)
88 g_object_ref (pixbuf);
89
90 g_object_unref (loader);
91
92 if (pixbuf == NULL)
93 return FALSE;
94
95 new_height = gdk_pixbuf_get_height (pixbuf);
96 new_width = gdk_pixbuf_get_width (pixbuf);
97
98 if (chooser->priv->image_height == 0
99 && chooser->priv->image_width == 0) {
100 scale = 1.0;
101 } else if (chooser->priv->image_height < new_height
102 || chooser->priv->image_width < new_width) {
103 /* we need to scale down */
104 if (new_height > new_width)
105 scale = (gfloat) chooser->priv->image_height / new_height;
106 else
107 scale = (gfloat) chooser->priv->image_width / new_width;
108 } else {
109 /* we need to scale up */
110 if (new_height > new_width)
111 scale = (gfloat) new_height / chooser->priv->image_height;
112 else
113 scale = (gfloat) new_width / chooser->priv->image_width;
114 }
115
116 if (scale == 1.0) {
117 gtk_image_set_from_pixbuf (
118 GTK_IMAGE (chooser->priv->image), pixbuf);
119 chooser->priv->image_width = new_width;
120 chooser->priv->image_height = new_height;
121 } else {
122 GdkPixbuf *scaled;
123 GdkPixbuf *composite;
124
125 new_width *= scale;
126 new_height *= scale;
127 new_width = MIN (new_width, chooser->priv->image_width);
128 new_height = MIN (new_height, chooser->priv->image_height);
129
130 scaled = gdk_pixbuf_scale_simple (
131 pixbuf, new_width, new_height,
132 GDK_INTERP_BILINEAR);
133
134 composite = gdk_pixbuf_new (
135 GDK_COLORSPACE_RGB, TRUE,
136 gdk_pixbuf_get_bits_per_sample (pixbuf),
137 chooser->priv->image_width,
138 chooser->priv->image_height);
139
140 gdk_pixbuf_fill (composite, 0x00000000);
141
142 gdk_pixbuf_copy_area (
143 scaled, 0, 0, new_width, new_height,
144 composite,
145 chooser->priv->image_width / 2 - new_width / 2,
146 chooser->priv->image_height / 2 - new_height / 2);
147
148 gtk_image_set_from_pixbuf (
149 GTK_IMAGE (chooser->priv->image), composite);
150
151 g_object_unref (scaled);
152 g_object_unref (composite);
153 }
154
155 g_object_unref (pixbuf);
156
157 g_free (chooser->priv->image_buf);
158 chooser->priv->image_buf = data;
159 chooser->priv->image_buf_size = length;
160
161 g_signal_emit (chooser, signals[CHANGED], 0);
162
163 return TRUE;
164 }
165
166 static gboolean
167 image_drag_motion_cb (GtkWidget *widget,
168 GdkDragContext *context,
169 gint x,
170 gint y,
171 guint time,
172 EImageChooser *chooser)
173 {
174 GtkFrame *frame;
175 GList *targets, *p;
176
177 frame = GTK_FRAME (chooser->priv->frame);
178 targets = gdk_drag_context_list_targets (context);
179
180 for (p = targets; p != NULL; p = p->next) {
181 gchar *possible_type;
182
183 possible_type = gdk_atom_name (GDK_POINTER_TO_ATOM (p->data));
184 if (!strcmp (possible_type, URI_LIST_TYPE)) {
185 g_free (possible_type);
186 gdk_drag_status (context, GDK_ACTION_COPY, time);
187 gtk_frame_set_shadow_type (frame, GTK_SHADOW_IN);
188 return TRUE;
189 }
190
191 g_free (possible_type);
192 }
193
194 gtk_frame_set_shadow_type (frame, GTK_SHADOW_NONE);
195
196 return FALSE;
197 }
198
199 static void
200 image_drag_leave_cb (GtkWidget *widget,
201 GdkDragContext *context,
202 guint time,
203 EImageChooser *chooser)
204 {
205 GtkFrame *frame;
206
207 frame = GTK_FRAME (chooser->priv->frame);
208 gtk_frame_set_shadow_type (frame, GTK_SHADOW_NONE);
209 }
210
211 static gboolean
212 image_drag_drop_cb (GtkWidget *widget,
213 GdkDragContext *context,
214 gint x,
215 gint y,
216 guint time,
217 EImageChooser *chooser)
218 {
219 GtkFrame *frame;
220 GList *targets, *p;
221
222 frame = GTK_FRAME (chooser->priv->frame);
223 targets = gdk_drag_context_list_targets (context);
224
225 if (targets == NULL) {
226 gtk_frame_set_shadow_type (frame, GTK_SHADOW_NONE);
227 return FALSE;
228 }
229
230 for (p = targets; p != NULL; p = p->next) {
231 gchar *possible_type;
232
233 possible_type = gdk_atom_name (GDK_POINTER_TO_ATOM (p->data));
234 if (!strcmp (possible_type, URI_LIST_TYPE)) {
235 g_free (possible_type);
236 gtk_drag_get_data (
237 widget, context,
238 GDK_POINTER_TO_ATOM (p->data), time);
239 gtk_frame_set_shadow_type (frame, GTK_SHADOW_NONE);
240 return TRUE;
241 }
242
243 g_free (possible_type);
244 }
245
246 gtk_frame_set_shadow_type (frame, GTK_SHADOW_NONE);
247
248 return FALSE;
249 }
250
251 static void
252 image_chooser_file_loaded_cb (GFile *file,
253 GAsyncResult *result,
254 EImageChooser *chooser)
255 {
256 gchar *contents;
257 gsize length;
258 GError *error = NULL;
259
260 g_file_load_contents_finish (
261 file, result, &contents, &length, NULL, &error);
262
263 if (error != NULL) {
264 g_warning ("%s", error->message);
265 g_error_free (error);
266 goto exit;
267 }
268
269 set_image_from_data (chooser, contents, length);
270
271 g_free (contents);
272
273 exit:
274 g_object_unref (chooser);
275 }
276
277 static void
278 image_drag_data_received_cb (GtkWidget *widget,
279 GdkDragContext *context,
280 gint x,
281 gint y,
282 GtkSelectionData *selection_data,
283 guint info,
284 guint time,
285 EImageChooser *chooser)
286 {
287 GFile *file;
288 gboolean handled = FALSE;
289 gchar **uris;
290
291 uris = gtk_selection_data_get_uris (selection_data);
292
293 if (uris == NULL)
294 goto exit;
295
296 file = g_file_new_for_uri (uris[0]);
297
298 /* XXX Not cancellable. */
299 g_file_load_contents_async (
300 file, NULL, (GAsyncReadyCallback)
301 image_chooser_file_loaded_cb,
302 g_object_ref (chooser));
303
304 g_object_unref (file);
305 g_strfreev (uris);
306
307 /* Assume success. We won't know til later. */
308 handled = TRUE;
309
310 exit:
311 gtk_drag_finish (context, handled, FALSE, time);
312 }
313
314 static void
315 image_chooser_set_icon_name (EImageChooser *chooser,
316 const gchar *icon_name)
317 {
318 GtkIconTheme *icon_theme;
319 GtkIconInfo *icon_info;
320 const gchar *filename;
321 gint width, height;
322
323 g_return_if_fail (chooser->priv->icon_name == NULL);
324
325 chooser->priv->icon_name = g_strdup (icon_name);
326
327 icon_theme = gtk_icon_theme_get_default ();
328 gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &width, &height);
329
330 icon_info = gtk_icon_theme_lookup_icon (
331 icon_theme, icon_name, height, 0);
332 g_return_if_fail (icon_info != NULL);
333
334 filename = gtk_icon_info_get_filename (icon_info);
335 e_image_chooser_set_from_file (chooser, filename);
336 gtk_icon_info_free (icon_info);
337 }
338
339 static void
340 image_chooser_set_property (GObject *object,
341 guint property_id,
342 const GValue *value,
343 GParamSpec *pspec)
344 {
345 switch (property_id) {
346 case PROP_ICON_NAME:
347 image_chooser_set_icon_name (
348 E_IMAGE_CHOOSER (object),
349 g_value_get_string (value));
350 return;
351 }
352
353 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
354 }
355
356 static void
357 image_chooser_get_property (GObject *object,
358 guint property_id,
359 GValue *value,
360 GParamSpec *pspec)
361 {
362 switch (property_id) {
363 case PROP_ICON_NAME:
364 g_value_set_string (
365 value,
366 e_image_chooser_get_icon_name (
367 E_IMAGE_CHOOSER (object)));
368 return;
369 }
370
371 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
372 }
373
374 static void
375 image_chooser_dispose (GObject *object)
376 {
377 EImageChooserPrivate *priv;
378
379 priv = E_IMAGE_CHOOSER_GET_PRIVATE (object);
380
381 if (priv->frame != NULL) {
382 g_object_unref (priv->frame);
383 priv->frame = NULL;
384 }
385
386 if (priv->image != NULL) {
387 g_object_unref (priv->image);
388 priv->image = NULL;
389 }
390
391 /* Chain up to parent's dispose() method. */
392 G_OBJECT_CLASS (e_image_chooser_parent_class)->dispose (object);
393 }
394
395 static void
396 image_chooser_finalize (GObject *object)
397 {
398 EImageChooserPrivate *priv;
399
400 priv = E_IMAGE_CHOOSER_GET_PRIVATE (object);
401
402 g_free (priv->image_buf);
403 g_free (priv->icon_name);
404
405 /* Chain up to parent's finalize() method. */
406 G_OBJECT_CLASS (e_image_chooser_parent_class)->finalize (object);
407 }
408
409 static void
410 e_image_chooser_class_init (EImageChooserClass *class)
411 {
412 GObjectClass *object_class;
413
414 g_type_class_add_private (class, sizeof (EImageChooserPrivate));
415
416 object_class = G_OBJECT_CLASS (class);
417 object_class->set_property = image_chooser_set_property;
418 object_class->get_property = image_chooser_get_property;
419 object_class->dispose = image_chooser_dispose;
420 object_class->finalize = image_chooser_finalize;
421
422 g_object_class_install_property (
423 object_class,
424 PROP_ICON_NAME,
425 g_param_spec_string (
426 "icon-name",
427 "Icon Name",
428 NULL,
429 "avatar-default",
430 G_PARAM_READWRITE |
431 G_PARAM_CONSTRUCT_ONLY));
432
433 signals[CHANGED] = g_signal_new (
434 "changed",
435 G_OBJECT_CLASS_TYPE (object_class),
436 G_SIGNAL_RUN_FIRST,
437 G_STRUCT_OFFSET (EImageChooserClass, changed),
438 NULL, NULL,
439 g_cclosure_marshal_VOID__VOID,
440 G_TYPE_NONE, 0);
441 }
442
443 static void
444 e_image_chooser_init (EImageChooser *chooser)
445 {
446 GtkWidget *container;
447 GtkWidget *widget;
448
449 chooser->priv = E_IMAGE_CHOOSER_GET_PRIVATE (chooser);
450
451 container = GTK_WIDGET (chooser);
452
453 widget = gtk_frame_new ("");
454 gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_NONE);
455 gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
456 chooser->priv->frame = g_object_ref (widget);
457 gtk_widget_show (widget);
458
459 container = widget;
460
461 widget = gtk_alignment_new (0, 0, 0, 0);
462 gtk_container_add (GTK_CONTAINER (container), widget);
463 gtk_widget_show (widget);
464
465 container = widget;
466
467 widget = gtk_image_new ();
468 gtk_container_add (GTK_CONTAINER (container), widget);
469 chooser->priv->image = g_object_ref (widget);
470 gtk_widget_show (widget);
471
472 gtk_drag_dest_set (widget, 0, NULL, 0, GDK_ACTION_COPY);
473 gtk_drag_dest_add_uri_targets (widget);
474
475 g_signal_connect (
476 widget, "drag-motion",
477 G_CALLBACK (image_drag_motion_cb), chooser);
478 g_signal_connect (
479 widget, "drag-leave",
480 G_CALLBACK (image_drag_leave_cb), chooser);
481 g_signal_connect (
482 widget, "drag-drop",
483 G_CALLBACK (image_drag_drop_cb), chooser);
484 g_signal_connect (
485 widget, "drag-data-received",
486 G_CALLBACK (image_drag_data_received_cb), chooser);
487 }
488
489 const gchar *
490 e_image_chooser_get_icon_name (EImageChooser *chooser)
491 {
492 g_return_val_if_fail (E_IS_IMAGE_CHOOSER (chooser), NULL);
493
494 return chooser->priv->icon_name;
495 }
496
497 GtkWidget *
498 e_image_chooser_new (const gchar *icon_name)
499 {
500 g_return_val_if_fail (icon_name != NULL, NULL);
501
502 return g_object_new (
503 E_TYPE_IMAGE_CHOOSER,
504 "icon-name", icon_name, NULL);
505 }
506
507 gboolean
508 e_image_chooser_set_from_file (EImageChooser *chooser,
509 const gchar *filename)
510 {
511 gchar *data;
512 gsize data_length;
513
514 g_return_val_if_fail (E_IS_IMAGE_CHOOSER (chooser), FALSE);
515 g_return_val_if_fail (filename != NULL, FALSE);
516
517 if (!g_file_get_contents (filename, &data, &data_length, NULL))
518 return FALSE;
519
520 if (!set_image_from_data (chooser, data, data_length))
521 g_free (data);
522
523 return TRUE;
524 }
525
526 gboolean
527 e_image_chooser_get_image_data (EImageChooser *chooser,
528 gchar **data,
529 gsize *data_length)
530 {
531 g_return_val_if_fail (E_IS_IMAGE_CHOOSER (chooser), FALSE);
532 g_return_val_if_fail (data != NULL, FALSE);
533 g_return_val_if_fail (data_length != NULL, FALSE);
534
535 *data_length = chooser->priv->image_buf_size;
536 *data = g_malloc (*data_length);
537 memcpy (*data, chooser->priv->image_buf, *data_length);
538
539 return TRUE;
540 }
541
542 gboolean
543 e_image_chooser_set_image_data (EImageChooser *chooser,
544 gchar *data,
545 gsize data_length)
546 {
547 gchar *buf;
548
549 g_return_val_if_fail (E_IS_IMAGE_CHOOSER (chooser), FALSE);
550 g_return_val_if_fail (data != NULL, FALSE);
551
552 /* yuck, a copy... */
553 buf = g_malloc (data_length);
554 memcpy (buf, data, data_length);
555
556 if (!set_image_from_data (chooser, buf, data_length)) {
557 g_free (buf);
558 return FALSE;
559 }
560
561 return TRUE;
562 }