]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-avatar-chooser.c
2ef3de4297d0b2785e25661367f4f493a874adcc
[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 #include <libempathy/empathy-conf.h>
34
35 #include "empathy-avatar-chooser.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_AVATAR_CHOOSER_TYPE, EmpathyAvatarChooserPriv))
42
43 #define AVATAR_MAX 96
44 #define DEFAULT_DIR DATADIR"/pixmaps/faces"
45
46 typedef struct {
47         GdkPixbuf *pixbuf;
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         if (priv->pixbuf) {
164                 g_object_unref (priv->pixbuf);
165         }
166         g_free (priv->image_data);
167
168         G_OBJECT_CLASS (empathy_avatar_chooser_parent_class)->finalize (object);
169 }
170
171 static void
172 avatar_chooser_set_pixbuf (EmpathyAvatarChooser *chooser,
173                            GdkPixbuf            *pixbuf)
174 {
175         EmpathyAvatarChooserPriv *priv = GET_PRIV (chooser);
176         GtkWidget                *image;
177         GError                   *error = NULL;
178
179         if (priv->pixbuf) {
180                 g_object_unref (priv->pixbuf);
181                 priv->pixbuf = NULL;
182         }
183         g_free (priv->image_data);
184         priv->image_data = NULL;
185         priv->image_data_size = 0;
186
187         if (pixbuf) {
188                 priv->pixbuf = empathy_pixbuf_scale_down_if_necessary (pixbuf, AVATAR_MAX);
189         }
190
191         if (priv->pixbuf) {
192                 if (!gdk_pixbuf_save_to_buffer (priv->pixbuf,
193                                                 &priv->image_data,
194                                                 &priv->image_data_size,
195                                                 "png",
196                                                 &error, NULL)) {
197                         empathy_debug (DEBUG_DOMAIN, "Failed to save pixbuf: %s",
198                                        error ? error->message : "No error given");
199                         g_clear_error (&error);
200                         g_object_unref (priv->pixbuf);
201                         priv->pixbuf = NULL;
202                 }
203         }
204
205         if (!priv->pixbuf) {
206                 image = gtk_image_new_from_icon_name ("stock_person",
207                                                       GTK_ICON_SIZE_DIALOG);
208         } else {
209                 image = gtk_image_new_from_pixbuf (priv->pixbuf);
210         }
211
212         gtk_button_set_image (GTK_BUTTON (chooser), image);
213         g_signal_emit (chooser, signals[CHANGED], 0);
214 }
215
216 static void
217 avatar_chooser_set_image_from_file (EmpathyAvatarChooser *chooser,
218                                     const gchar          *filename)
219 {
220         GdkPixbuf *pixbuf;
221         GError    *error = NULL;
222
223         if (!(pixbuf = gdk_pixbuf_new_from_file (filename, &error))) {
224                 empathy_debug (DEBUG_DOMAIN, "Failed to load pixbuf from file: %s",
225                                error ? error->message : "No error given");
226                 g_clear_error (&error);
227         }
228
229         avatar_chooser_set_pixbuf (chooser, pixbuf);
230         if (pixbuf) {
231                 g_object_unref (pixbuf);
232         }
233 }
234
235 static void
236 avatar_chooser_set_image_from_data (EmpathyAvatarChooser *chooser,
237                                     gchar                *data,
238                                     gsize                 size)
239 {
240         GdkPixbuf *pixbuf;
241
242         pixbuf = empathy_pixbuf_from_data (data, size);
243         avatar_chooser_set_pixbuf (chooser, pixbuf);
244         if (pixbuf) {
245                 g_object_unref (pixbuf);
246         }
247 }
248
249 static gboolean
250 avatar_chooser_drag_motion_cb (GtkWidget          *widget,
251                               GdkDragContext     *context,
252                               gint                x,
253                               gint                y,
254                               guint               time,
255                               EmpathyAvatarChooser *chooser)
256 {
257         EmpathyAvatarChooserPriv *priv;
258         GList                  *p;
259
260         priv = GET_PRIV (chooser);
261
262         for (p = context->targets; p != NULL; p = p->next) {
263                 gchar *possible_type;
264
265                 possible_type = gdk_atom_name (GDK_POINTER_TO_ATOM (p->data));
266
267                 if (!strcmp (possible_type, URI_LIST_TYPE)) {
268                         g_free (possible_type);
269                         gdk_drag_status (context, GDK_ACTION_COPY, time);
270
271                         return TRUE;
272                 }
273
274                 g_free (possible_type);
275         }
276
277         return FALSE;
278 }
279
280 static void
281 avatar_chooser_drag_leave_cb (GtkWidget          *widget,
282                              GdkDragContext     *context,
283                              guint               time,
284                              EmpathyAvatarChooser *chooser)
285 {
286 }
287
288 static gboolean
289 avatar_chooser_drag_drop_cb (GtkWidget          *widget,
290                             GdkDragContext     *context,
291                             gint                x,
292                             gint                y,
293                             guint               time,
294                             EmpathyAvatarChooser *chooser)
295 {
296         EmpathyAvatarChooserPriv *priv;
297         GList                  *p;
298
299         priv = GET_PRIV (chooser);
300
301         if (context->targets == NULL) {
302                 return FALSE;
303         }
304
305         for (p = context->targets; p != NULL; p = p->next) {
306                 char *possible_type;
307
308                 possible_type = gdk_atom_name (GDK_POINTER_TO_ATOM (p->data));
309                 if (!strcmp (possible_type, URI_LIST_TYPE)) {
310                         g_free (possible_type);
311                         gtk_drag_get_data (widget, context,
312                                            GDK_POINTER_TO_ATOM (p->data),
313                                            time);
314
315                         return TRUE;
316                 }
317
318                 g_free (possible_type);
319         }
320
321         return FALSE;
322 }
323
324 static void
325 avatar_chooser_drag_data_received_cb (GtkWidget          *widget,
326                                      GdkDragContext     *context,
327                                      gint                x,
328                                      gint                y,
329                                      GtkSelectionData   *selection_data,
330                                      guint               info,
331                                      guint               time,
332                                      EmpathyAvatarChooser *chooser)
333 {
334         gchar    *target_type;
335         gboolean  handled = FALSE;
336
337         target_type = gdk_atom_name (selection_data->target);
338         if (!strcmp (target_type, URI_LIST_TYPE)) {
339                 GnomeVFSHandle   *handle = NULL;
340                 GnomeVFSResult    result;
341                 GnomeVFSFileInfo  info;
342                 gchar            *uri;
343                 gchar            *nl;
344                 gchar            *data = NULL;
345
346                 nl = strstr (selection_data->data, "\r\n");
347                 if (nl) {
348                         uri = g_strndup (selection_data->data,
349                                          nl - (gchar*) selection_data->data);
350                 } else {
351                         uri = g_strdup (selection_data->data);
352                 }
353
354                 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
355                 if (result == GNOME_VFS_OK) {
356                         result = gnome_vfs_get_file_info_from_handle (handle,
357                                                                       &info,
358                                                                       GNOME_VFS_FILE_INFO_DEFAULT);
359                         if (result == GNOME_VFS_OK) {
360                                 GnomeVFSFileSize data_size;
361
362                                 data = g_malloc (info.size);
363
364                                 result = gnome_vfs_read (handle, data, info.size, &data_size);
365                                 if (result == GNOME_VFS_OK) {
366                                         avatar_chooser_set_image_from_data (chooser,
367                                                                             data,
368                                                                             data_size);
369                                         handled = TRUE;
370                                 } else {
371                                         g_free (data);
372                                 }
373                         }
374
375                         gnome_vfs_close (handle);
376                 }
377
378                 g_free (uri);
379         }
380
381         gtk_drag_finish (context, handled, FALSE, time);
382 }
383
384 static void
385 avatar_chooser_update_preview_cb (GtkFileChooser       *file_chooser,
386                                   EmpathyAvatarChooser *chooser)
387 {
388         gchar *filename;
389
390         filename = gtk_file_chooser_get_preview_filename (file_chooser);
391
392         if (filename) {
393                 GtkWidget *image;
394                 GdkPixbuf *pixbuf = NULL;
395                 GdkPixbuf *scaled_pixbuf;
396
397                 pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
398
399                 image = gtk_file_chooser_get_preview_widget (file_chooser);
400
401                 if (pixbuf) {
402                         scaled_pixbuf = empathy_pixbuf_scale_down_if_necessary (pixbuf, AVATAR_MAX);
403                         gtk_image_set_from_pixbuf (GTK_IMAGE (image), scaled_pixbuf);
404                         g_object_unref (scaled_pixbuf);
405                         g_object_unref (pixbuf);
406                 } else {
407                         gtk_image_set_from_stock (GTK_IMAGE (image),
408                                                   "gtk-dialog-question",
409                                                   GTK_ICON_SIZE_DIALOG);
410                 }
411         }
412
413         gtk_file_chooser_set_preview_widget_active (file_chooser, TRUE);
414 }
415
416 static void
417 avatar_chooser_response_cb (GtkWidget            *widget,
418                             gint                  response,
419                             EmpathyAvatarChooser *chooser)
420 {
421         if (response == GTK_RESPONSE_OK) {
422                 gchar *filename;
423                 gchar *path;
424
425                 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
426                 avatar_chooser_set_image_from_file (chooser, filename);
427                 g_free (filename);
428
429                 path = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (widget));
430                 if (path) {
431                         empathy_conf_set_string (empathy_conf_get (),
432                                                  EMPATHY_PREFS_UI_AVATAR_DIRECTORY,
433                                                  path);
434                         g_free (path);
435                 }
436         }
437         else if (response == GTK_RESPONSE_NO) {
438                 avatar_chooser_set_image_from_data (chooser, NULL, 0);
439         }
440
441         gtk_widget_destroy (widget);
442 }
443
444 static void
445 avatar_chooser_clicked_cb (GtkWidget            *button,
446                            EmpathyAvatarChooser *chooser)
447 {
448         GtkFileChooser *chooser_dialog;
449         GtkWidget      *image;
450         gchar          *saved_dir = NULL;
451         const gchar    *default_dir = DEFAULT_DIR;
452         const gchar    *pics_dir;
453         GtkFileFilter  *filter;
454
455         chooser_dialog = GTK_FILE_CHOOSER (
456                 gtk_file_chooser_dialog_new (_("Select Your Avatar Image"),
457                                              empathy_get_toplevel_window (GTK_WIDGET (chooser)),
458                                              GTK_FILE_CHOOSER_ACTION_OPEN,
459                                              _("No Image"),
460                                              GTK_RESPONSE_NO,
461                                              GTK_STOCK_CANCEL,
462                                              GTK_RESPONSE_CANCEL,
463                                              GTK_STOCK_OPEN,
464                                              GTK_RESPONSE_OK,
465                                              NULL));
466
467         /* Get special dirs */
468         empathy_conf_get_string (empathy_conf_get (),
469                                  EMPATHY_PREFS_UI_AVATAR_DIRECTORY,
470                                  &saved_dir);
471         if (saved_dir && !g_file_test (saved_dir, G_FILE_TEST_IS_DIR)) {
472                 g_free (saved_dir);
473                 saved_dir = NULL;
474         }
475         if (!g_file_test (default_dir, G_FILE_TEST_IS_DIR)) {
476                 default_dir = NULL;
477         }
478         pics_dir = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
479         if (pics_dir && !g_file_test (pics_dir, G_FILE_TEST_IS_DIR)) {
480                 pics_dir = NULL;
481         }
482
483         /* Set current dir to the last one or to DEFAULT_DIR or to home */
484         if (saved_dir) {
485                 gtk_file_chooser_set_current_folder (chooser_dialog, saved_dir);
486         }
487         else if (pics_dir) {
488                 gtk_file_chooser_set_current_folder (chooser_dialog, pics_dir);
489         }
490         else if (default_dir) {
491                 gtk_file_chooser_set_current_folder (chooser_dialog, default_dir);
492         } else {
493                 gtk_file_chooser_set_current_folder (chooser_dialog, g_get_home_dir ());
494         }
495
496         /* Add shortcuts to special dirs */
497         if (saved_dir) {
498                 gtk_file_chooser_add_shortcut_folder (chooser_dialog, saved_dir, NULL);
499         }
500         else if (pics_dir) {
501                 gtk_file_chooser_add_shortcut_folder (chooser_dialog, pics_dir, NULL);
502         }
503         if (default_dir) {
504                 gtk_file_chooser_add_shortcut_folder (chooser_dialog, default_dir, NULL);
505         }
506
507         /* Setup preview image */
508         image = gtk_image_new ();
509         gtk_file_chooser_set_preview_widget (chooser_dialog, image);
510         gtk_widget_set_size_request (image, AVATAR_MAX, AVATAR_MAX);
511         gtk_widget_show (image);
512         gtk_file_chooser_set_use_preview_label (chooser_dialog, FALSE);
513         g_signal_connect (chooser_dialog, "update-preview",
514                           G_CALLBACK (avatar_chooser_update_preview_cb),
515                           chooser);
516
517         /* Setup filers */
518         filter = gtk_file_filter_new ();
519         gtk_file_filter_set_name (filter, _("Images"));
520         gtk_file_filter_add_pixbuf_formats (filter);
521         gtk_file_chooser_add_filter (chooser_dialog, filter);
522         filter = gtk_file_filter_new ();
523         gtk_file_filter_set_name (filter, _("All Files"));
524         gtk_file_filter_add_pattern(filter, "*");
525         gtk_file_chooser_add_filter (chooser_dialog, filter);
526
527         /* Setup response */
528         gtk_dialog_set_default_response (GTK_DIALOG (chooser_dialog), GTK_RESPONSE_OK);
529         g_signal_connect (chooser_dialog, "response",
530                           G_CALLBACK (avatar_chooser_response_cb),
531                           chooser);
532
533         gtk_widget_show (GTK_WIDGET (chooser_dialog));
534         g_free (saved_dir);
535 }
536
537 GtkWidget *
538 empathy_avatar_chooser_new (void)
539 {
540         return g_object_new (EMPATHY_AVATAR_CHOOSER_TYPE, NULL);
541 }
542
543 void
544 empathy_avatar_chooser_set (EmpathyAvatarChooser *chooser,
545                             EmpathyAvatar        *avatar)
546 {
547         g_return_if_fail (EMPATHY_IS_AVATAR_CHOOSER (chooser));
548
549         avatar_chooser_set_image_from_data (chooser,
550                                             avatar ? avatar->data : NULL,
551                                             avatar ? avatar->len : 0);
552 }
553
554 void
555 empathy_avatar_chooser_get_image_data (EmpathyAvatarChooser  *chooser,
556                                        gchar                **data,
557                                        gsize                 *data_size)
558 {
559         EmpathyAvatarChooserPriv *priv;
560
561         g_return_if_fail (EMPATHY_IS_AVATAR_CHOOSER (chooser));
562
563         priv = GET_PRIV (chooser);
564
565         if (data) {
566                 *data = priv->image_data;
567         }
568         if (*data_size) {
569                 *data_size = priv->image_data_size;
570         }
571 }
572