]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-avatar-chooser.c
Change EMPATHY_AVATAR_CHOOSER_TYPE to EMPATHY_TYPE_AVATAR_CHOOSER
[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_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                 GnomeVFSHandle   *handle = NULL;
332                 GnomeVFSResult    result;
333                 GnomeVFSFileInfo  info;
334                 gchar            *uri;
335                 gchar            *nl;
336                 gchar            *data = NULL;
337
338                 nl = strstr (selection_data->data, "\r\n");
339                 if (nl) {
340                         uri = g_strndup (selection_data->data,
341                                          nl - (gchar*) selection_data->data);
342                 } else {
343                         uri = g_strdup (selection_data->data);
344                 }
345
346                 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
347                 if (result == GNOME_VFS_OK) {
348                         result = gnome_vfs_get_file_info_from_handle (handle,
349                                                                       &info,
350                                                                       GNOME_VFS_FILE_INFO_DEFAULT);
351                         if (result == GNOME_VFS_OK) {
352                                 GnomeVFSFileSize data_size;
353
354                                 data = g_malloc (info.size);
355
356                                 result = gnome_vfs_read (handle, data, info.size, &data_size);
357                                 if (result == GNOME_VFS_OK) {
358                                         avatar_chooser_set_image_from_data (chooser,
359                                                                             data,
360                                                                             data_size);
361                                         handled = TRUE;
362                                 } else {
363                                         g_free (data);
364                                 }
365                         }
366
367                         gnome_vfs_close (handle);
368                 }
369
370                 g_free (uri);
371         }
372
373         gtk_drag_finish (context, handled, FALSE, time);
374 }
375
376 static void
377 avatar_chooser_update_preview_cb (GtkFileChooser       *file_chooser,
378                                   EmpathyAvatarChooser *chooser)
379 {
380         gchar *filename;
381
382         filename = gtk_file_chooser_get_preview_filename (file_chooser);
383
384         if (filename) {
385                 GtkWidget *image;
386                 GdkPixbuf *pixbuf = NULL;
387                 GdkPixbuf *scaled_pixbuf;
388
389                 pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
390
391                 image = gtk_file_chooser_get_preview_widget (file_chooser);
392
393                 if (pixbuf) {
394                         scaled_pixbuf = empathy_pixbuf_scale_down_if_necessary (pixbuf, AVATAR_SIZE_SAVE);
395                         gtk_image_set_from_pixbuf (GTK_IMAGE (image), scaled_pixbuf);
396                         g_object_unref (scaled_pixbuf);
397                         g_object_unref (pixbuf);
398                 } else {
399                         gtk_image_set_from_stock (GTK_IMAGE (image),
400                                                   "gtk-dialog-question",
401                                                   GTK_ICON_SIZE_DIALOG);
402                 }
403         }
404
405         gtk_file_chooser_set_preview_widget_active (file_chooser, TRUE);
406 }
407
408 static void
409 avatar_chooser_response_cb (GtkWidget            *widget,
410                             gint                  response,
411                             EmpathyAvatarChooser *chooser)
412 {
413         if (response == GTK_RESPONSE_OK) {
414                 gchar *filename;
415                 gchar *path;
416
417                 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
418                 avatar_chooser_set_image_from_file (chooser, filename);
419                 g_free (filename);
420
421                 path = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (widget));
422                 if (path) {
423                         empathy_conf_set_string (empathy_conf_get (),
424                                                  EMPATHY_PREFS_UI_AVATAR_DIRECTORY,
425                                                  path);
426                         g_free (path);
427                 }
428         }
429         else if (response == GTK_RESPONSE_NO) {
430                 avatar_chooser_set_image_from_data (chooser, NULL, 0);
431         }
432
433         gtk_widget_destroy (widget);
434 }
435
436 static void
437 avatar_chooser_clicked_cb (GtkWidget            *button,
438                            EmpathyAvatarChooser *chooser)
439 {
440         GtkFileChooser *chooser_dialog;
441         GtkWidget      *image;
442         gchar          *saved_dir = NULL;
443         const gchar    *default_dir = DEFAULT_DIR;
444         const gchar    *pics_dir;
445         GtkFileFilter  *filter;
446
447         chooser_dialog = GTK_FILE_CHOOSER (
448                 gtk_file_chooser_dialog_new (_("Select Your Avatar Image"),
449                                              empathy_get_toplevel_window (GTK_WIDGET (chooser)),
450                                              GTK_FILE_CHOOSER_ACTION_OPEN,
451                                              _("No Image"),
452                                              GTK_RESPONSE_NO,
453                                              GTK_STOCK_CANCEL,
454                                              GTK_RESPONSE_CANCEL,
455                                              GTK_STOCK_OPEN,
456                                              GTK_RESPONSE_OK,
457                                              NULL));
458
459         /* Get special dirs */
460         empathy_conf_get_string (empathy_conf_get (),
461                                  EMPATHY_PREFS_UI_AVATAR_DIRECTORY,
462                                  &saved_dir);
463         if (saved_dir && !g_file_test (saved_dir, G_FILE_TEST_IS_DIR)) {
464                 g_free (saved_dir);
465                 saved_dir = NULL;
466         }
467         if (!g_file_test (default_dir, G_FILE_TEST_IS_DIR)) {
468                 default_dir = NULL;
469         }
470         pics_dir = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
471         if (pics_dir && !g_file_test (pics_dir, G_FILE_TEST_IS_DIR)) {
472                 pics_dir = NULL;
473         }
474
475         /* Set current dir to the last one or to DEFAULT_DIR or to home */
476         if (saved_dir) {
477                 gtk_file_chooser_set_current_folder (chooser_dialog, saved_dir);
478         }
479         else if (pics_dir) {
480                 gtk_file_chooser_set_current_folder (chooser_dialog, pics_dir);
481         }
482         else if (default_dir) {
483                 gtk_file_chooser_set_current_folder (chooser_dialog, default_dir);
484         } else {
485                 gtk_file_chooser_set_current_folder (chooser_dialog, g_get_home_dir ());
486         }
487
488         /* Add shortcuts to special dirs */
489         if (saved_dir) {
490                 gtk_file_chooser_add_shortcut_folder (chooser_dialog, saved_dir, NULL);
491         }
492         else if (pics_dir) {
493                 gtk_file_chooser_add_shortcut_folder (chooser_dialog, pics_dir, NULL);
494         }
495         if (default_dir) {
496                 gtk_file_chooser_add_shortcut_folder (chooser_dialog, default_dir, NULL);
497         }
498
499         /* Setup preview image */
500         image = gtk_image_new ();
501         gtk_file_chooser_set_preview_widget (chooser_dialog, image);
502         gtk_widget_set_size_request (image, AVATAR_SIZE_SAVE, AVATAR_SIZE_SAVE);
503         gtk_widget_show (image);
504         gtk_file_chooser_set_use_preview_label (chooser_dialog, FALSE);
505         g_signal_connect (chooser_dialog, "update-preview",
506                           G_CALLBACK (avatar_chooser_update_preview_cb),
507                           chooser);
508
509         /* Setup filers */
510         filter = gtk_file_filter_new ();
511         gtk_file_filter_set_name (filter, _("Images"));
512         gtk_file_filter_add_pixbuf_formats (filter);
513         gtk_file_chooser_add_filter (chooser_dialog, filter);
514         filter = gtk_file_filter_new ();
515         gtk_file_filter_set_name (filter, _("All Files"));
516         gtk_file_filter_add_pattern(filter, "*");
517         gtk_file_chooser_add_filter (chooser_dialog, filter);
518
519         /* Setup response */
520         gtk_dialog_set_default_response (GTK_DIALOG (chooser_dialog), GTK_RESPONSE_OK);
521         g_signal_connect (chooser_dialog, "response",
522                           G_CALLBACK (avatar_chooser_response_cb),
523                           chooser);
524
525         gtk_widget_show (GTK_WIDGET (chooser_dialog));
526         g_free (saved_dir);
527 }
528
529 GtkWidget *
530 empathy_avatar_chooser_new (void)
531 {
532         return g_object_new (EMPATHY_TYPE_AVATAR_CHOOSER, NULL);
533 }
534
535 void
536 empathy_avatar_chooser_set (EmpathyAvatarChooser *chooser,
537                             EmpathyAvatar        *avatar)
538 {
539         g_return_if_fail (EMPATHY_IS_AVATAR_CHOOSER (chooser));
540
541         avatar_chooser_set_image_from_data (chooser,
542                                             avatar ? avatar->data : NULL,
543                                             avatar ? avatar->len : 0);
544 }
545
546 void
547 empathy_avatar_chooser_get_image_data (EmpathyAvatarChooser  *chooser,
548                                        const gchar          **data,
549                                        gsize                 *data_size,
550                                        const gchar          **mime_type)
551 {
552         EmpathyAvatarChooserPriv *priv;
553
554         g_return_if_fail (EMPATHY_IS_AVATAR_CHOOSER (chooser));
555
556         priv = GET_PRIV (chooser);
557
558         if (data) {
559                 *data = priv->image_data;
560         }
561         if (data_size) {
562                 *data_size = priv->image_data_size;
563         }
564         if (mime_type) {
565                 *mime_type = "png";
566         }
567 }
568