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