]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-avatar-chooser.c
7ff34e812a2574952382fff12a7aca413a8b29cb
[empathy.git] / libempathy-gtk / empathy-avatar-chooser.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2006-2007 Imendio AB.
4  * Copyright (C) 2007-2008 Collabora Ltd.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 of the GNU General Public
8  * License as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Authors: Based on Novell's e-image-chooser.
21  *          Xavier Claessens <xclaesse@gmail.com>
22  */
23
24 #include "config.h"
25
26 #include <string.h>
27
28 #include <glib/gi18n.h>
29 #include <gtk/gtk.h>
30 #include <gio/gio.h>
31
32 #include <libempathy/empathy-debug.h>
33
34 #include "empathy-avatar-chooser.h"
35 #include "empathy-conf.h"
36 #include "empathy-preferences.h"
37 #include "empathy-ui-utils.h"
38
39 #define DEBUG_DOMAIN "AvatarChooser"
40
41 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_AVATAR_CHOOSER, EmpathyAvatarChooserPriv))
42
43 #define AVATAR_SIZE_SAVE 96
44 #define AVATAR_SIZE_VIEW 64
45 #define DEFAULT_DIR DATADIR"/pixmaps/faces"
46
47 typedef struct {
48         gchar *image_data;
49         gsize  image_data_size;
50 } EmpathyAvatarChooserPriv;
51
52 static void       avatar_chooser_finalize              (GObject              *object);
53 static void       avatar_chooser_set_image_from_data   (EmpathyAvatarChooser *chooser,
54                                                         gchar                *data,
55                                                         gsize                 size);
56 static gboolean   avatar_chooser_drag_motion_cb        (GtkWidget            *widget,
57                                                         GdkDragContext       *context,
58                                                         gint                  x,
59                                                         gint                  y,
60                                                         guint                 time,
61                                                         EmpathyAvatarChooser *chooser);
62 static void       avatar_chooser_drag_leave_cb         (GtkWidget            *widget,
63                                                         GdkDragContext       *context,
64                                                         guint                 time,
65                                                         EmpathyAvatarChooser *chooser);
66 static gboolean   avatar_chooser_drag_drop_cb          (GtkWidget            *widget,
67                                                         GdkDragContext       *context,
68                                                         gint                  x,
69                                                         gint                  y,
70                                                         guint                 time,
71                                                         EmpathyAvatarChooser *chooser);
72 static void       avatar_chooser_drag_data_received_cb (GtkWidget            *widget,
73                                                         GdkDragContext       *context,
74                                                         gint                  x,
75                                                         gint                  y,
76                                                         GtkSelectionData     *selection_data,
77                                                         guint                 info,
78                                                         guint                 time,
79                                                         EmpathyAvatarChooser *chooser);
80 static void       avatar_chooser_clicked_cb            (GtkWidget            *button,
81                                                         EmpathyAvatarChooser *chooser);
82
83 enum {
84         CHANGED,
85         LAST_SIGNAL
86 };
87
88 static guint signals [LAST_SIGNAL];
89
90 G_DEFINE_TYPE (EmpathyAvatarChooser, empathy_avatar_chooser, GTK_TYPE_BUTTON);
91
92 /*
93  * Drag and drop stuff
94  */
95 #define URI_LIST_TYPE "text/uri-list"
96
97 enum DndTargetType {
98         DND_TARGET_TYPE_URI_LIST
99 };
100
101 static const GtkTargetEntry drop_types[] = {
102         { URI_LIST_TYPE, 0, DND_TARGET_TYPE_URI_LIST },
103 };
104
105 static void
106 empathy_avatar_chooser_class_init (EmpathyAvatarChooserClass *klass)
107 {
108         GObjectClass *object_class = G_OBJECT_CLASS (klass);
109
110         object_class->finalize = avatar_chooser_finalize;
111
112         signals[CHANGED] =
113                 g_signal_new ("changed",
114                               G_TYPE_FROM_CLASS (klass),
115                               G_SIGNAL_RUN_LAST,
116                               0,
117                               NULL, NULL,
118                               g_cclosure_marshal_VOID__VOID,
119                               G_TYPE_NONE, 0);
120
121         g_type_class_add_private (object_class, sizeof (EmpathyAvatarChooserPriv));
122 }
123
124 static void
125 empathy_avatar_chooser_init (EmpathyAvatarChooser *chooser)
126 {
127         EmpathyAvatarChooserPriv *priv;
128
129         priv = GET_PRIV (chooser);
130
131         gtk_drag_dest_set (GTK_WIDGET (chooser),
132                            GTK_DEST_DEFAULT_ALL,
133                            drop_types,
134                            G_N_ELEMENTS (drop_types),
135                            GDK_ACTION_COPY);
136
137         g_signal_connect (chooser, "drag-motion",
138                           G_CALLBACK (avatar_chooser_drag_motion_cb),
139                           chooser);
140         g_signal_connect (chooser, "drag-leave",
141                           G_CALLBACK (avatar_chooser_drag_leave_cb),
142                           chooser);
143         g_signal_connect (chooser, "drag-drop",
144                           G_CALLBACK (avatar_chooser_drag_drop_cb),
145                           chooser);
146         g_signal_connect (chooser, "drag-data-received",
147                           G_CALLBACK (avatar_chooser_drag_data_received_cb),
148                           chooser);
149         g_signal_connect (chooser, "clicked",
150                           G_CALLBACK (avatar_chooser_clicked_cb),
151                           chooser);
152
153         empathy_avatar_chooser_set (chooser, NULL);
154 }
155
156 static void
157 avatar_chooser_finalize (GObject *object)
158 {
159         EmpathyAvatarChooserPriv *priv;
160
161         priv = GET_PRIV (object);
162
163         g_free (priv->image_data);
164
165         G_OBJECT_CLASS (empathy_avatar_chooser_parent_class)->finalize (object);
166 }
167
168 static void
169 avatar_chooser_set_pixbuf (EmpathyAvatarChooser *chooser,
170                            GdkPixbuf            *pixbuf)
171 {
172         EmpathyAvatarChooserPriv *priv = GET_PRIV (chooser);
173         GtkWidget                *image;
174         GdkPixbuf                *pixbuf_view = NULL;
175         GdkPixbuf                *pixbuf_save = NULL;
176         GError                   *error = NULL;
177
178         g_free (priv->image_data);
179         priv->image_data = NULL;
180         priv->image_data_size = 0;
181
182         if (pixbuf) {
183                 pixbuf_view = empathy_pixbuf_scale_down_if_necessary (pixbuf, AVATAR_SIZE_VIEW);
184                 pixbuf_save = empathy_pixbuf_scale_down_if_necessary (pixbuf, AVATAR_SIZE_SAVE);
185
186                 if (!gdk_pixbuf_save_to_buffer (pixbuf_save,
187                                                 &priv->image_data,
188                                                 &priv->image_data_size,
189                                                 "png",
190                                                 &error, NULL)) {
191                         empathy_debug (DEBUG_DOMAIN, "Failed to save pixbuf: %s",
192                                        error ? error->message : "No error given");
193                         g_clear_error (&error);
194                 }
195                 image = gtk_image_new_from_pixbuf (pixbuf_view);
196
197                 g_object_unref (pixbuf_save);
198                 g_object_unref (pixbuf_view);
199         } else {
200                 image = gtk_image_new_from_icon_name ("stock_person",
201                                                       GTK_ICON_SIZE_DIALOG);
202         }
203
204         gtk_button_set_image (GTK_BUTTON (chooser), image);
205         g_signal_emit (chooser, signals[CHANGED], 0);
206 }
207
208 static void
209 avatar_chooser_set_image_from_file (EmpathyAvatarChooser *chooser,
210                                     const gchar          *filename)
211 {
212         GdkPixbuf *pixbuf;
213         GError    *error = NULL;
214
215         if (!(pixbuf = gdk_pixbuf_new_from_file (filename, &error))) {
216                 empathy_debug (DEBUG_DOMAIN, "Failed to load pixbuf from file: %s",
217                                error ? error->message : "No error given");
218                 g_clear_error (&error);
219         }
220
221         avatar_chooser_set_pixbuf (chooser, pixbuf);
222         if (pixbuf) {
223                 g_object_unref (pixbuf);
224         }
225 }
226
227 static void
228 avatar_chooser_set_image_from_data (EmpathyAvatarChooser *chooser,
229                                     gchar                *data,
230                                     gsize                 size)
231 {
232         GdkPixbuf *pixbuf;
233
234         pixbuf = empathy_pixbuf_from_data (data, size);
235         avatar_chooser_set_pixbuf (chooser, pixbuf);
236         if (pixbuf) {
237                 g_object_unref (pixbuf);
238         }
239 }
240
241 static gboolean
242 avatar_chooser_drag_motion_cb (GtkWidget          *widget,
243                               GdkDragContext     *context,
244                               gint                x,
245                               gint                y,
246                               guint               time,
247                               EmpathyAvatarChooser *chooser)
248 {
249         EmpathyAvatarChooserPriv *priv;
250         GList                  *p;
251
252         priv = GET_PRIV (chooser);
253
254         for (p = context->targets; p != NULL; p = p->next) {
255                 gchar *possible_type;
256
257                 possible_type = gdk_atom_name (GDK_POINTER_TO_ATOM (p->data));
258
259                 if (!strcmp (possible_type, URI_LIST_TYPE)) {
260                         g_free (possible_type);
261                         gdk_drag_status (context, GDK_ACTION_COPY, time);
262
263                         return TRUE;
264                 }
265
266                 g_free (possible_type);
267         }
268
269         return FALSE;
270 }
271
272 static void
273 avatar_chooser_drag_leave_cb (GtkWidget          *widget,
274                              GdkDragContext     *context,
275                              guint               time,
276                              EmpathyAvatarChooser *chooser)
277 {
278 }
279
280 static gboolean
281 avatar_chooser_drag_drop_cb (GtkWidget          *widget,
282                             GdkDragContext     *context,
283                             gint                x,
284                             gint                y,
285                             guint               time,
286                             EmpathyAvatarChooser *chooser)
287 {
288         EmpathyAvatarChooserPriv *priv;
289         GList                  *p;
290
291         priv = GET_PRIV (chooser);
292
293         if (context->targets == NULL) {
294                 return FALSE;
295         }
296
297         for (p = context->targets; p != NULL; p = p->next) {
298                 char *possible_type;
299
300                 possible_type = gdk_atom_name (GDK_POINTER_TO_ATOM (p->data));
301                 if (!strcmp (possible_type, URI_LIST_TYPE)) {
302                         g_free (possible_type);
303                         gtk_drag_get_data (widget, context,
304                                            GDK_POINTER_TO_ATOM (p->data),
305                                            time);
306
307                         return TRUE;
308                 }
309
310                 g_free (possible_type);
311         }
312
313         return FALSE;
314 }
315
316 static void
317 avatar_chooser_drag_data_received_cb (GtkWidget          *widget,
318                                      GdkDragContext     *context,
319                                      gint                x,
320                                      gint                y,
321                                      GtkSelectionData   *selection_data,
322                                      guint               info,
323                                      guint               time,
324                                      EmpathyAvatarChooser *chooser)
325 {
326         gchar    *target_type;
327         gboolean  handled = FALSE;
328
329         target_type = gdk_atom_name (selection_data->target);
330         if (!strcmp (target_type, URI_LIST_TYPE)) {
331                 GFile            *file;
332                 GFileInputStream *input_stream;
333                 gchar            *nl;
334                 gchar            *data = NULL;
335
336                 nl = strstr (selection_data->data, "\r\n");
337                 if (nl) {
338                         gchar *uri;
339
340                         uri = g_strndup (selection_data->data,
341                                          nl - (gchar*) selection_data->data);
342
343                         file = g_file_new_for_uri (uri);
344                         g_free (uri);
345                 } else {
346                         file = g_file_new_for_uri (selection_data->data);
347                 }
348
349                 input_stream = g_file_read (file, NULL, NULL);
350
351                 if (input_stream != NULL) {
352                         GFileInfo *info;
353                         
354                         info = g_file_query_info (file,
355                                                   G_FILE_ATTRIBUTE_STANDARD_SIZE,
356                                                   0, NULL, NULL);
357                         if (info != NULL) {
358                                 goffset size;
359                                 gssize bytes_read;
360                                 
361                                 size = g_file_info_get_size (info);
362                                 data = g_malloc (size);
363
364                                 bytes_read = g_input_stream_read (G_INPUT_STREAM (input_stream),
365                                                                   data, size,
366                                                                   NULL, NULL);
367                                 if (bytes_read != -1) {
368                                         avatar_chooser_set_image_from_data (chooser,
369                                                                             data,
370                                                                             (gsize) bytes_read);
371                                         handled = TRUE;
372                                 }
373
374                                 g_free (data);
375                                 g_object_unref (info);
376                         }
377
378                         g_object_unref (input_stream);
379                 }
380                 
381                 g_object_unref (file);
382         }
383
384         gtk_drag_finish (context, handled, FALSE, time);
385 }
386
387 static void
388 avatar_chooser_update_preview_cb (GtkFileChooser       *file_chooser,
389                                   EmpathyAvatarChooser *chooser)
390 {
391         gchar *filename;
392
393         filename = gtk_file_chooser_get_preview_filename (file_chooser);
394
395         if (filename) {
396                 GtkWidget *image;
397                 GdkPixbuf *pixbuf = NULL;
398                 GdkPixbuf *scaled_pixbuf;
399
400                 pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
401
402                 image = gtk_file_chooser_get_preview_widget (file_chooser);
403
404                 if (pixbuf) {
405                         scaled_pixbuf = empathy_pixbuf_scale_down_if_necessary (pixbuf, AVATAR_SIZE_SAVE);
406                         gtk_image_set_from_pixbuf (GTK_IMAGE (image), scaled_pixbuf);
407                         g_object_unref (scaled_pixbuf);
408                         g_object_unref (pixbuf);
409                 } else {
410                         gtk_image_set_from_stock (GTK_IMAGE (image),
411                                                   "gtk-dialog-question",
412                                                   GTK_ICON_SIZE_DIALOG);
413                 }
414         }
415
416         gtk_file_chooser_set_preview_widget_active (file_chooser, TRUE);
417 }
418
419 static void
420 avatar_chooser_response_cb (GtkWidget            *widget,
421                             gint                  response,
422                             EmpathyAvatarChooser *chooser)
423 {
424         if (response == GTK_RESPONSE_OK) {
425                 gchar *filename;
426                 gchar *path;
427
428                 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
429                 avatar_chooser_set_image_from_file (chooser, filename);
430                 g_free (filename);
431
432                 path = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (widget));
433                 if (path) {
434                         empathy_conf_set_string (empathy_conf_get (),
435                                                  EMPATHY_PREFS_UI_AVATAR_DIRECTORY,
436                                                  path);
437                         g_free (path);
438                 }
439         }
440         else if (response == GTK_RESPONSE_NO) {
441                 avatar_chooser_set_image_from_data (chooser, NULL, 0);
442         }
443
444         gtk_widget_destroy (widget);
445 }
446
447 static void
448 avatar_chooser_clicked_cb (GtkWidget            *button,
449                            EmpathyAvatarChooser *chooser)
450 {
451         GtkFileChooser *chooser_dialog;
452         GtkWidget      *image;
453         gchar          *saved_dir = NULL;
454         const gchar    *default_dir = DEFAULT_DIR;
455         const gchar    *pics_dir;
456         GtkFileFilter  *filter;
457
458         chooser_dialog = GTK_FILE_CHOOSER (
459                 gtk_file_chooser_dialog_new (_("Select Your Avatar Image"),
460                                              empathy_get_toplevel_window (GTK_WIDGET (chooser)),
461                                              GTK_FILE_CHOOSER_ACTION_OPEN,
462                                              _("No Image"),
463                                              GTK_RESPONSE_NO,
464                                              GTK_STOCK_CANCEL,
465                                              GTK_RESPONSE_CANCEL,
466                                              GTK_STOCK_OPEN,
467                                              GTK_RESPONSE_OK,
468                                              NULL));
469
470         /* Get special dirs */
471         empathy_conf_get_string (empathy_conf_get (),
472                                  EMPATHY_PREFS_UI_AVATAR_DIRECTORY,
473                                  &saved_dir);
474         if (saved_dir && !g_file_test (saved_dir, G_FILE_TEST_IS_DIR)) {
475                 g_free (saved_dir);
476                 saved_dir = NULL;
477         }
478         if (!g_file_test (default_dir, G_FILE_TEST_IS_DIR)) {
479                 default_dir = NULL;
480         }
481         pics_dir = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
482         if (pics_dir && !g_file_test (pics_dir, G_FILE_TEST_IS_DIR)) {
483                 pics_dir = NULL;
484         }
485
486         /* Set current dir to the last one or to DEFAULT_DIR or to home */
487         if (saved_dir) {
488                 gtk_file_chooser_set_current_folder (chooser_dialog, saved_dir);
489         }
490         else if (pics_dir) {
491                 gtk_file_chooser_set_current_folder (chooser_dialog, pics_dir);
492         }
493         else if (default_dir) {
494                 gtk_file_chooser_set_current_folder (chooser_dialog, default_dir);
495         } else {
496                 gtk_file_chooser_set_current_folder (chooser_dialog, g_get_home_dir ());
497         }
498
499         /* Add shortcuts to special dirs */
500         if (saved_dir) {
501                 gtk_file_chooser_add_shortcut_folder (chooser_dialog, saved_dir, NULL);
502         }
503         else if (pics_dir) {
504                 gtk_file_chooser_add_shortcut_folder (chooser_dialog, pics_dir, NULL);
505         }
506         if (default_dir) {
507                 gtk_file_chooser_add_shortcut_folder (chooser_dialog, default_dir, NULL);
508         }
509
510         /* Setup preview image */
511         image = gtk_image_new ();
512         gtk_file_chooser_set_preview_widget (chooser_dialog, image);
513         gtk_widget_set_size_request (image, AVATAR_SIZE_SAVE, AVATAR_SIZE_SAVE);
514         gtk_widget_show (image);
515         gtk_file_chooser_set_use_preview_label (chooser_dialog, FALSE);
516         g_signal_connect (chooser_dialog, "update-preview",
517                           G_CALLBACK (avatar_chooser_update_preview_cb),
518                           chooser);
519
520         /* Setup filers */
521         filter = gtk_file_filter_new ();
522         gtk_file_filter_set_name (filter, _("Images"));
523         gtk_file_filter_add_pixbuf_formats (filter);
524         gtk_file_chooser_add_filter (chooser_dialog, filter);
525         filter = gtk_file_filter_new ();
526         gtk_file_filter_set_name (filter, _("All Files"));
527         gtk_file_filter_add_pattern(filter, "*");
528         gtk_file_chooser_add_filter (chooser_dialog, filter);
529
530         /* Setup response */
531         gtk_dialog_set_default_response (GTK_DIALOG (chooser_dialog), GTK_RESPONSE_OK);
532         g_signal_connect (chooser_dialog, "response",
533                           G_CALLBACK (avatar_chooser_response_cb),
534                           chooser);
535
536         gtk_widget_show (GTK_WIDGET (chooser_dialog));
537         g_free (saved_dir);
538 }
539
540 GtkWidget *
541 empathy_avatar_chooser_new (void)
542 {
543         return g_object_new (EMPATHY_TYPE_AVATAR_CHOOSER, NULL);
544 }
545
546 void
547 empathy_avatar_chooser_set (EmpathyAvatarChooser *chooser,
548                             EmpathyAvatar        *avatar)
549 {
550         g_return_if_fail (EMPATHY_IS_AVATAR_CHOOSER (chooser));
551
552         avatar_chooser_set_image_from_data (chooser,
553                                             avatar ? avatar->data : NULL,
554                                             avatar ? avatar->len : 0);
555 }
556
557 void
558 empathy_avatar_chooser_get_image_data (EmpathyAvatarChooser  *chooser,
559                                        const gchar          **data,
560                                        gsize                 *data_size,
561                                        const gchar          **mime_type)
562 {
563         EmpathyAvatarChooserPriv *priv;
564
565         g_return_if_fail (EMPATHY_IS_AVATAR_CHOOSER (chooser));
566
567         priv = GET_PRIV (chooser);
568
569         if (data) {
570                 *data = priv->image_data;
571         }
572         if (data_size) {
573                 *data_size = priv->image_data_size;
574         }
575         if (mime_type) {
576                 *mime_type = "png";
577         }
578 }
579