]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-smiley-manager.c
always show images in smiley menu items (GNOME #582751)
[empathy.git] / libempathy-gtk / empathy-smiley-manager.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007-2008 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library 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  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  * 
19  * Authors: Dafydd Harrie <dafydd.harries@collabora.co.uk>
20  *          Xavier Claessens <xclaesse@gmail.com>
21  */
22
23 #include <config.h>
24
25 #include <string.h>
26
27 #include <libempathy/empathy-utils.h>
28 #include "empathy-smiley-manager.h"
29 #include "empathy-ui-utils.h"
30
31 typedef struct _SmileyManagerTree SmileyManagerTree;
32
33 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathySmileyManager)
34 typedef struct {
35         SmileyManagerTree *tree;
36         GSList            *smileys;
37 } EmpathySmileyManagerPriv;
38
39 struct _SmileyManagerTree {
40         gunichar   c;
41         GdkPixbuf *pixbuf;
42         GSList    *childrens;
43 };
44
45 G_DEFINE_TYPE (EmpathySmileyManager, empathy_smiley_manager, G_TYPE_OBJECT);
46
47 static EmpathySmileyManager *manager_singleton = NULL;
48
49 static SmileyManagerTree *
50 smiley_manager_tree_new (gunichar c)
51 {
52         SmileyManagerTree *tree;
53
54         tree = g_slice_new0 (SmileyManagerTree);
55         tree->c = c;
56         tree->pixbuf = NULL;
57         tree->childrens = NULL;
58
59         return tree;
60 }
61
62 static void
63 smiley_manager_tree_free (SmileyManagerTree *tree)
64 {
65         GSList *l;
66
67         if (!tree) {
68                 return;
69         }
70
71         for (l = tree->childrens; l; l = l->next) {
72                 smiley_manager_tree_free (l->data);
73         }
74
75         if (tree->pixbuf) {
76                 g_object_unref (tree->pixbuf);
77         }
78         g_slist_free (tree->childrens);
79         g_slice_free (SmileyManagerTree, tree);
80 }
81
82 /* Note: This function takes the ownership of str */
83 static EmpathySmiley *
84 smiley_new (GdkPixbuf *pixbuf, gchar *str)
85 {
86         EmpathySmiley *smiley;
87
88         smiley = g_slice_new0 (EmpathySmiley);
89         if (pixbuf) {
90                 smiley->pixbuf = g_object_ref (pixbuf);
91         }
92         smiley->str = str;
93
94         return smiley;
95 }
96
97 void
98 empathy_smiley_free (EmpathySmiley *smiley)
99 {
100         if (!smiley) {
101                 return;
102         }
103
104         if (smiley->pixbuf) {
105                 g_object_unref (smiley->pixbuf);
106         }
107         g_free (smiley->str);
108         g_slice_free (EmpathySmiley, smiley);
109 }
110
111 static void
112 smiley_manager_finalize (GObject *object)
113 {
114         EmpathySmileyManagerPriv *priv = GET_PRIV (object);
115
116         smiley_manager_tree_free (priv->tree);
117         g_slist_foreach (priv->smileys, (GFunc) empathy_smiley_free, NULL);
118         g_slist_free (priv->smileys);
119 }
120
121 static GObject *
122 smiley_manager_constructor (GType type,
123                             guint n_props,
124                             GObjectConstructParam *props)
125 {
126         GObject *retval;
127
128         if (manager_singleton) {
129                 retval = g_object_ref (manager_singleton);
130         } else {
131                 retval = G_OBJECT_CLASS (empathy_smiley_manager_parent_class)->constructor
132                         (type, n_props, props);
133
134                 manager_singleton = EMPATHY_SMILEY_MANAGER (retval);
135                 g_object_add_weak_pointer (retval, (gpointer) &manager_singleton);
136         }
137
138         return retval;
139 }
140
141 static void
142 empathy_smiley_manager_class_init (EmpathySmileyManagerClass *klass)
143 {
144         GObjectClass *object_class = G_OBJECT_CLASS (klass);
145
146         object_class->finalize = smiley_manager_finalize;
147         object_class->constructor = smiley_manager_constructor;
148
149         g_type_class_add_private (object_class, sizeof (EmpathySmileyManagerPriv));
150 }
151
152 static void
153 empathy_smiley_manager_init (EmpathySmileyManager *manager)
154 {
155         EmpathySmileyManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
156                 EMPATHY_TYPE_SMILEY_MANAGER, EmpathySmileyManagerPriv);
157
158         manager->priv = priv;
159         priv->tree = smiley_manager_tree_new ('\0');
160         priv->smileys = NULL;
161
162         empathy_smiley_manager_load (manager);
163 }
164
165 EmpathySmileyManager *
166 empathy_smiley_manager_dup_singleton (void)
167 {
168         return g_object_new (EMPATHY_TYPE_SMILEY_MANAGER, NULL);
169 }
170
171 static SmileyManagerTree *
172 smiley_manager_tree_find_child (SmileyManagerTree *tree, gunichar c)
173 {
174         GSList *l;
175
176         for (l = tree->childrens; l; l = l->next) {
177                 SmileyManagerTree *child = l->data;
178
179                 if (child->c == c) {
180                         return child;
181                 }
182         }
183
184         return NULL;
185 }
186
187 static SmileyManagerTree *
188 smiley_manager_tree_find_or_insert_child (SmileyManagerTree *tree, gunichar c)
189 {
190         SmileyManagerTree *child;
191
192         child = smiley_manager_tree_find_child (tree, c);
193
194         if (!child) {
195                 child = smiley_manager_tree_new (c);
196                 tree->childrens = g_slist_prepend (tree->childrens, child);
197         }
198
199         return child;
200 }
201
202 static void
203 smiley_manager_tree_insert (SmileyManagerTree *tree,
204                             GdkPixbuf         *smiley,
205                             const gchar       *str)
206 {
207         SmileyManagerTree *child;
208
209         child = smiley_manager_tree_find_or_insert_child (tree, g_utf8_get_char (str));
210
211         str = g_utf8_next_char (str);
212         if (*str) {
213                 smiley_manager_tree_insert (child, smiley, str);
214                 return;
215         }
216
217         child->pixbuf = g_object_ref (smiley);
218 }
219
220 static void
221 smiley_manager_add_valist (EmpathySmileyManager *manager,
222                            GdkPixbuf            *smiley,
223                            const gchar          *first_str,
224                            va_list               var_args)
225 {
226         EmpathySmileyManagerPriv *priv = GET_PRIV (manager);
227         const gchar              *str;
228
229         for (str = first_str; str; str = va_arg (var_args, gchar*)) {
230                 smiley_manager_tree_insert (priv->tree, smiley, str);
231         }
232
233         priv->smileys = g_slist_prepend (priv->smileys,
234                 smiley_new (smiley, g_strdup (first_str)));
235 }
236
237 void
238 empathy_smiley_manager_add (EmpathySmileyManager *manager,
239                             const gchar          *icon_name,
240                             const gchar          *first_str,
241                             ...)
242 {
243         GdkPixbuf *smiley;
244         va_list    var_args;
245
246         g_return_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager));
247         g_return_if_fail (!EMP_STR_EMPTY (icon_name));
248         g_return_if_fail (!EMP_STR_EMPTY (first_str));
249
250         smiley = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
251         if (smiley) {
252                 va_start (var_args, first_str);
253                 smiley_manager_add_valist (manager, smiley, first_str, var_args);
254                 va_end (var_args);
255                 g_object_unref (smiley);
256         }
257 }
258
259 void
260 empathy_smiley_manager_add_from_pixbuf (EmpathySmileyManager *manager,
261                                         GdkPixbuf            *smiley,
262                                         const gchar          *first_str,
263                                         ...)
264 {
265         va_list var_args;
266
267         g_return_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager));
268         g_return_if_fail (GDK_IS_PIXBUF (smiley));
269         g_return_if_fail (!EMP_STR_EMPTY (first_str));
270
271         va_start (var_args, first_str);
272         smiley_manager_add_valist (manager, smiley, first_str, var_args);
273         va_end (var_args);
274 }
275
276 void
277 empathy_smiley_manager_load (EmpathySmileyManager *manager)
278 {
279         g_return_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager));
280
281         /* From fd.o icon-naming spec */
282         empathy_smiley_manager_add (manager, "face-angel",      "O:-)",  "O:)",  NULL);
283         empathy_smiley_manager_add (manager, "face-cool",       "B-)",   "B)",   NULL);
284         empathy_smiley_manager_add (manager, "face-crying",     ":'(", NULL);
285         empathy_smiley_manager_add (manager, "face-devilish",   ">:-)",  ">:)",  NULL);
286         empathy_smiley_manager_add (manager, "face-embarrassed",":-[",   ":[",   ":-$", ":$", NULL);
287         empathy_smiley_manager_add (manager, "face-kiss",       ":-*",   ":*",   NULL);
288         empathy_smiley_manager_add (manager, "face-monkey",     ":-(|)", ":(|)", NULL);
289         empathy_smiley_manager_add (manager, "face-plain",      ":-|",   ":|",   NULL);
290         empathy_smiley_manager_add (manager, "face-raspberry",  ":-P",   ":P",   ":-p", ":p", NULL);
291         empathy_smiley_manager_add (manager, "face-sad",        ":-(",   ":(",   NULL);
292         empathy_smiley_manager_add (manager, "face-smile",      ":-)",   ":)",   NULL);
293         empathy_smiley_manager_add (manager, "face-smile-big",  ":-D",   ":D",   ":-d", ":d", NULL);
294         empathy_smiley_manager_add (manager, "face-smirk",      ":-!",   ":!",   NULL);
295         empathy_smiley_manager_add (manager, "face-surprise",   ":-O",   ":O",   NULL);
296         empathy_smiley_manager_add (manager, "face-wink",       ";-)",   ";)",   NULL);
297 }
298
299 GSList *
300 empathy_smiley_manager_parse (EmpathySmileyManager *manager,
301                               const gchar          *text)
302 {
303         EmpathySmileyManagerPriv *priv = GET_PRIV (manager);
304         EmpathySmiley            *smiley;
305         SmileyManagerTree        *cur_tree = priv->tree;
306         const gchar              *t;
307         const gchar              *cur_str = text;
308         GSList                   *smileys = NULL;
309
310         g_return_val_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager), NULL);
311         g_return_val_if_fail (text != NULL, NULL);
312
313         for (t = text; *t; t = g_utf8_next_char (t)) {
314                 SmileyManagerTree *child;
315                 gunichar           c;
316                 
317                 c = g_utf8_get_char (t);
318                 child = smiley_manager_tree_find_child (cur_tree, c);
319
320                 if (cur_tree == priv->tree) {
321                         if (child) {
322                                 if (t > cur_str) {
323                                         smiley = smiley_new (NULL, g_strndup (cur_str, t - cur_str));
324                                         smileys = g_slist_prepend (smileys, smiley);
325                                 }
326                                 cur_str = t;
327                                 cur_tree = child;
328                         }
329
330                         continue;
331                 }
332
333                 if (child) {
334                         cur_tree = child;
335                         continue;
336                 }
337
338                 smiley = smiley_new (cur_tree->pixbuf, g_strndup (cur_str, t - cur_str));
339                 smileys = g_slist_prepend (smileys, smiley);
340                 if (cur_tree->pixbuf) {
341                         cur_str = t;
342                         cur_tree = smiley_manager_tree_find_child (priv->tree, c);
343
344                         if (!cur_tree) {
345                                 cur_tree = priv->tree;
346                         }
347                 } else {
348                         cur_str = t;
349                         cur_tree = priv->tree;
350                 }
351         }
352
353         smiley = smiley_new (cur_tree->pixbuf, g_strndup (cur_str, t - cur_str));
354         smileys = g_slist_prepend (smileys, smiley);
355
356         return g_slist_reverse (smileys);
357 }
358
359 GSList *
360 empathy_smiley_manager_get_all (EmpathySmileyManager *manager)
361 {
362         EmpathySmileyManagerPriv *priv = GET_PRIV (manager);
363
364         return priv->smileys;
365 }
366
367 typedef struct {
368         EmpathySmileyManager *manager;
369         EmpathySmiley        *smiley;
370         EmpathySmileyMenuFunc func;    
371         gpointer              user_data;         
372 } ActivateData;
373
374 static void
375 smiley_menu_data_free (gpointer  user_data,
376                        GClosure *closure)
377 {
378         ActivateData *data = (ActivateData*) user_data;
379
380         g_object_unref (data->manager);
381         g_slice_free (ActivateData, data);
382 }
383
384 static void
385 smiley_menu_activate_cb (GtkMenuItem *menuitem,
386                          gpointer     user_data)
387 {
388         ActivateData *data = (ActivateData*) user_data;
389
390         data->func (data->manager, data->smiley, data->user_data);
391 }
392
393 GtkWidget *
394 empathy_smiley_menu_new (EmpathySmileyManager *manager,
395                          EmpathySmileyMenuFunc func,
396                          gpointer              user_data)
397 {
398         EmpathySmileyManagerPriv *priv = GET_PRIV (manager);
399         GSList                   *l;
400         GtkWidget                *menu;
401         gint                      x = 0;
402         gint                      y = 0;
403
404         g_return_val_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager), NULL);
405         g_return_val_if_fail (func != NULL, NULL);
406
407         menu = gtk_menu_new ();
408
409         for (l = priv->smileys; l; l = l->next) {
410                 EmpathySmiley *smiley;
411                 GtkWidget     *item;
412                 GtkWidget     *image;
413                 ActivateData  *data;
414
415                 smiley = l->data;
416                 image = gtk_image_new_from_pixbuf (smiley->pixbuf);
417
418                 item = gtk_image_menu_item_new_with_label ("");
419                 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
420                 gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (item), TRUE);
421
422                 gtk_menu_attach (GTK_MENU (menu), item,
423                                  x, x + 1, y, y + 1);
424
425                 gtk_widget_set_tooltip_text (item, smiley->str);
426
427                 data = g_slice_new (ActivateData);
428                 data->manager = g_object_ref (manager);
429                 data->smiley = smiley;
430                 data->func = func;
431                 data->user_data = user_data;
432
433                 g_signal_connect_data (item, "activate",
434                                        G_CALLBACK (smiley_menu_activate_cb),
435                                        data,
436                                        smiley_menu_data_free,
437                                        0);
438
439                 if (x > 3) {
440                         y++;
441                         x = 0;
442                 } else {
443                         x++;
444                 }
445         }
446
447         gtk_widget_show_all (menu);
448
449         return menu;
450 }
451