]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-avatar-image.c
Merge call branch from Elliot Fairweather with cleanups from Xavier Claessens.
[empathy.git] / libempathy-gtk / empathy-avatar-image.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 program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  *
21  * Authors: Xavier Claessens <xclaesse@gmail.com>
22  */
23
24 #include "config.h"
25
26 #include <glib/gi18n.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <gdk/gdk.h>
29 #include <gtk/gtk.h>
30 #include <gdk/gdkx.h>
31
32 #include <libempathy/empathy-debug.h>
33
34 #include "empathy-avatar-image.h"
35 #include "empathy-ui-utils.h"
36
37 #define DEBUG_DOMAIN "AvatarImage"
38
39 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_AVATAR_IMAGE, EmpathyAvatarImagePriv))
40
41 #define MAX_SMALL 64
42 #define MAX_LARGE 400
43
44 typedef struct {
45         GtkWidget   *image;
46         GtkWidget   *popup;
47         GdkPixbuf   *pixbuf;
48 } EmpathyAvatarImagePriv;
49
50 static void     avatar_image_finalize                (GObject           *object);
51 static void     avatar_image_add_filter              (EmpathyAvatarImage *avatar_image);
52 static void     avatar_image_remove_filter           (EmpathyAvatarImage *avatar_image);
53 static gboolean avatar_image_button_press_event      (GtkWidget         *widget,
54                                                       GdkEventButton    *event);
55 static gboolean avatar_image_button_release_event    (GtkWidget         *widget,
56                                                       GdkEventButton    *event);
57
58 G_DEFINE_TYPE (EmpathyAvatarImage, empathy_avatar_image, GTK_TYPE_EVENT_BOX);
59
60 static void
61 empathy_avatar_image_class_init (EmpathyAvatarImageClass *klass)
62 {
63         GObjectClass   *object_class;
64         GtkWidgetClass *widget_class;
65
66         object_class = G_OBJECT_CLASS (klass);
67         widget_class = GTK_WIDGET_CLASS (klass);
68
69         object_class->finalize = avatar_image_finalize;
70
71         widget_class->button_press_event   = avatar_image_button_press_event;
72         widget_class->button_release_event = avatar_image_button_release_event;
73
74         g_type_class_add_private (object_class, sizeof (EmpathyAvatarImagePriv));
75 }
76
77 static void
78 empathy_avatar_image_init (EmpathyAvatarImage *avatar_image)
79 {
80         EmpathyAvatarImagePriv *priv;
81
82         priv = GET_PRIV (avatar_image);
83
84         priv->image = gtk_image_new ();
85         gtk_container_add (GTK_CONTAINER (avatar_image), priv->image);
86         empathy_avatar_image_set (avatar_image, NULL);
87         gtk_widget_show (priv->image);
88
89         avatar_image_add_filter (avatar_image);
90 }
91
92 static void
93 avatar_image_finalize (GObject *object)
94 {
95         EmpathyAvatarImagePriv *priv;
96
97         priv = GET_PRIV (object);
98
99         avatar_image_remove_filter (EMPATHY_AVATAR_IMAGE (object));
100
101         if (priv->popup) {
102                 gtk_widget_destroy (priv->popup);
103         }
104
105         if (priv->pixbuf) {
106                 g_object_unref (priv->pixbuf);
107         }
108
109         G_OBJECT_CLASS (empathy_avatar_image_parent_class)->finalize (object);
110 }
111
112 static GdkFilterReturn
113 avatar_image_filter_func (GdkXEvent  *gdkxevent,
114                           GdkEvent   *event,
115                           gpointer    data)
116 {
117         XEvent                *xevent = gdkxevent;
118         Atom                   atom;
119         EmpathyAvatarImagePriv *priv;
120
121         priv = GET_PRIV (data);
122
123         switch (xevent->type) {
124         case PropertyNotify:
125                 atom = gdk_x11_get_xatom_by_name ("_NET_CURRENT_DESKTOP");
126                 if (xevent->xproperty.atom == atom) {
127                         if (priv->popup) {
128                                 gtk_widget_destroy (priv->popup);
129                                 priv->popup = NULL;
130                         }
131                 }
132                 break;
133         }
134
135         return GDK_FILTER_CONTINUE;
136 }
137
138 static void
139 avatar_image_add_filter (EmpathyAvatarImage *avatar_image)
140 {
141         Window     window;
142         GdkWindow *gdkwindow;
143         gint       mask;
144
145         mask = PropertyChangeMask;
146
147         window = GDK_ROOT_WINDOW ();
148         gdkwindow = gdk_xid_table_lookup (window);
149
150         gdk_error_trap_push ();
151         if (gdkwindow) {
152                 XWindowAttributes attrs;
153                 XGetWindowAttributes (gdk_display, window, &attrs);
154                 mask |= attrs.your_event_mask;
155         }
156
157         XSelectInput (gdk_display, window, mask);
158
159         gdk_error_trap_pop ();
160
161         gdk_window_add_filter (NULL, avatar_image_filter_func, avatar_image);
162 }
163
164 static void
165 avatar_image_remove_filter (EmpathyAvatarImage *avatar_image)
166 {
167         gdk_window_remove_filter (NULL, avatar_image_filter_func, avatar_image);
168 }
169
170 static gboolean
171 avatar_image_button_press_event (GtkWidget *widget, GdkEventButton *event)
172 {
173         EmpathyAvatarImagePriv *priv;
174         GtkWidget             *popup;
175         GtkWidget             *frame;
176         GtkWidget             *image;
177         gint                   x, y;
178         gint                   popup_width, popup_height;
179         gint                   width, height;
180         GdkPixbuf             *pixbuf;
181
182         priv = GET_PRIV (widget);
183
184         if (priv->popup) {
185                 gtk_widget_destroy (priv->popup);
186                 priv->popup = NULL;
187         }
188
189         if (event->button != 1 || event->type != GDK_BUTTON_PRESS || !priv->pixbuf) {
190                 return FALSE;
191         }
192
193         popup_width = gdk_pixbuf_get_width (priv->pixbuf);
194         popup_height = gdk_pixbuf_get_height (priv->pixbuf);
195
196         width = priv->image->allocation.width;
197         height = priv->image->allocation.height;
198
199         /* Don't show a popup if the popup is smaller then the currently avatar
200          * image.
201          */
202         if (popup_height <= height && popup_width <= width) {
203                 return TRUE;
204         }
205
206         pixbuf = empathy_pixbuf_scale_down_if_necessary (priv->pixbuf, MAX_LARGE);
207         popup_width = gdk_pixbuf_get_width (pixbuf);
208         popup_height = gdk_pixbuf_get_height (pixbuf);
209
210         popup = gtk_window_new (GTK_WINDOW_POPUP);
211
212         frame = gtk_frame_new (NULL);
213         gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
214
215         gtk_container_add (GTK_CONTAINER (popup), frame);
216
217         image = gtk_image_new ();
218         gtk_container_add (GTK_CONTAINER (frame), image);
219
220         gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf);
221         g_object_unref (pixbuf);
222
223         gdk_window_get_origin (priv->image->window, &x, &y);
224
225         x = x - (popup_width - width) / 2;
226         y = y - (popup_height - height) / 2;
227
228         gtk_window_move (GTK_WINDOW (popup), x, y);
229
230         priv->popup = popup;
231
232         gtk_widget_show_all (popup);
233
234         return TRUE;
235 }
236
237 static gboolean
238 avatar_image_button_release_event (GtkWidget *widget, GdkEventButton *event)
239 {
240         EmpathyAvatarImagePriv *priv;
241
242         priv = GET_PRIV (widget);
243
244         if (event->button != 1 || event->type != GDK_BUTTON_RELEASE) {
245                 return FALSE;
246         }
247
248         if (!priv->popup) {
249                 return TRUE;
250         }
251
252         gtk_widget_destroy (priv->popup);
253         priv->popup = NULL;
254
255         return TRUE;
256 }
257
258 GtkWidget *
259 empathy_avatar_image_new (void)
260 {
261         EmpathyAvatarImage *avatar_image;
262
263         avatar_image = g_object_new (EMPATHY_TYPE_AVATAR_IMAGE, NULL);
264
265         return GTK_WIDGET (avatar_image);
266 }
267
268 void
269 empathy_avatar_image_set (EmpathyAvatarImage *avatar_image,
270                           EmpathyAvatar      *avatar)
271 {
272         EmpathyAvatarImagePriv *priv = GET_PRIV (avatar_image);
273         GdkPixbuf              *scaled_pixbuf;
274
275         g_return_if_fail (EMPATHY_IS_AVATAR_IMAGE (avatar_image));
276
277         if (priv->pixbuf) {
278                 g_object_unref (priv->pixbuf);
279                 priv->pixbuf = NULL;
280         }
281
282         if (avatar) {
283                 priv->pixbuf = empathy_pixbuf_from_data (avatar->data, avatar->len);
284         }
285
286         if (!priv->pixbuf) {
287                 gtk_image_set_from_icon_name (GTK_IMAGE (priv->image),
288                                               "stock_person",
289                                               GTK_ICON_SIZE_DIALOG);
290                 return;
291         }
292
293         scaled_pixbuf = empathy_pixbuf_scale_down_if_necessary (priv->pixbuf, MAX_SMALL);
294         gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), scaled_pixbuf);
295
296         if (scaled_pixbuf != priv->pixbuf) {
297                 gtk_widget_set_tooltip_text (GTK_WIDGET (avatar_image),
298                                              _("Click to enlarge"));
299         } else {
300                 gtk_widget_set_tooltip_text (GTK_WIDGET (avatar_image),
301                                              NULL);
302         }
303
304         g_object_unref (scaled_pixbuf);
305 }
306