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