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