Drop one more redundant if
[pidgin-git.git] / libpurple / smiley-list.c
blobc0131d6ed3c901ccde8be526e6999a668f27075e
1 /* purple
3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "smiley-list.h"
24 #include "debug.h"
25 #include "smiley-parser.h"
26 #include "trie.h"
28 #include <string.h>
30 /**
31 * PurpleSmileyList:
33 * A container for #PurpleSmiley's.
35 struct _PurpleSmileyList {
36 GObject parent;
39 typedef struct {
40 GList *smileys;
41 GList *smileys_end;
42 PurpleTrie *trie;
43 GHashTable *path_map;
44 GHashTable *shortcut_map;
46 gboolean drop_failed_remotes;
47 } PurpleSmileyListPrivate;
49 enum
51 PROP_0,
52 PROP_DROP_FAILED_REMOTES,
53 PROP_LAST
56 static GParamSpec *properties[PROP_LAST];
58 /*******************************************************************************
59 * Object stuff
60 ******************************************************************************/
61 G_DEFINE_TYPE_WITH_PRIVATE(PurpleSmileyList, purple_smiley_list, G_TYPE_OBJECT);
63 static void
64 purple_smiley_list_init(PurpleSmileyList *list) {
65 PurpleSmileyListPrivate *priv = purple_smiley_list_get_instance_private(list);
67 priv->trie = purple_trie_new();
68 priv->path_map = g_hash_table_new_full(g_str_hash, g_str_equal,
69 g_free, NULL);
70 priv->shortcut_map = g_hash_table_new_full(g_str_hash, g_str_equal,
71 g_free, NULL);
74 static void
75 purple_smiley_list_finalize(GObject *obj) {
76 PurpleSmileyList *sl = PURPLE_SMILEY_LIST(obj);
77 PurpleSmileyListPrivate *priv = purple_smiley_list_get_instance_private(sl);
78 GList *it;
80 g_object_unref(priv->trie);
81 g_hash_table_destroy(priv->path_map);
82 g_hash_table_destroy(priv->shortcut_map);
84 for (it = priv->smileys; it; it = g_list_next(it)) {
85 PurpleSmiley *smiley = it->data;
86 g_object_set_data(G_OBJECT(smiley), "purple-smiley-list", NULL);
87 g_object_set_data(G_OBJECT(smiley), "purple-smiley-list-elem", NULL);
88 g_object_unref(smiley);
90 g_list_free(priv->smileys);
92 G_OBJECT_CLASS(purple_smiley_list_parent_class)->finalize(obj);
95 static void
96 purple_smiley_list_get_property(GObject *obj, guint param_id, GValue *value,
97 GParamSpec *pspec)
99 PurpleSmileyList *sl = PURPLE_SMILEY_LIST(obj);
100 PurpleSmileyListPrivate *priv = purple_smiley_list_get_instance_private(sl);
102 switch (param_id) {
103 case PROP_DROP_FAILED_REMOTES:
104 g_value_set_boolean(value, priv->drop_failed_remotes);
105 break;
106 default:
107 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
108 break;
112 static void
113 purple_smiley_list_set_property(GObject *obj, guint param_id,
114 const GValue *value, GParamSpec *pspec)
116 PurpleSmileyList *sl = PURPLE_SMILEY_LIST(obj);
117 PurpleSmileyListPrivate *priv = purple_smiley_list_get_instance_private(sl);
119 switch (param_id) {
120 case PROP_DROP_FAILED_REMOTES:
121 priv->drop_failed_remotes = g_value_get_boolean(value);
122 /* XXX: we could scan for remote smiley's on our list
123 * and change the setting, but we don't care that much.
125 break;
126 default:
127 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
128 break;
132 static void
133 purple_smiley_list_class_init(PurpleSmileyListClass *klass) {
134 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
136 obj_class->get_property = purple_smiley_list_get_property;
137 obj_class->set_property = purple_smiley_list_set_property;
138 obj_class->finalize = purple_smiley_list_finalize;
140 properties[PROP_DROP_FAILED_REMOTES] = g_param_spec_boolean(
141 "drop-failed-remotes", "Drop failed remote PurpleSmileys",
142 "Watch added remote smileys and remove them from the list, "
143 "if they change their state to failed", FALSE,
144 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
146 g_object_class_install_properties(obj_class, PROP_LAST, properties);
149 /******************************************************************************
150 * Helpers
151 *****************************************************************************/
152 static void
153 _list_append2(GList **head_p, GList **tail_p, gpointer data)
155 GList *head = *head_p;
156 GList *tail = *tail_p;
157 GList *elem;
159 g_return_if_fail((head == NULL) == (tail == NULL));
160 g_return_if_fail((tail == NULL) || (tail->next == NULL));
162 elem = g_list_alloc();
163 elem->data = data;
164 elem->prev = tail;
165 elem->next = NULL;
167 if (head) {
168 tail->next = elem;
169 *tail_p = elem;
170 } else
171 *head_p = *tail_p = elem;
174 static void
175 _list_delete_link2(GList **head_p, GList **tail_p, GList *link)
177 GList *head = *head_p;
178 GList *tail = *tail_p;
180 g_return_if_fail(head != NULL);
181 g_return_if_fail(tail != NULL);
183 if (link == tail)
184 *tail_p = tail->prev;
185 *head_p = g_list_delete_link(head, link);
188 static const gchar *
189 smiley_get_uniqid(PurpleSmiley *smiley)
191 return purple_image_get_path(PURPLE_IMAGE(smiley));
194 /*******************************************************************************
195 * API implementation
196 ******************************************************************************/
197 PurpleSmileyList *
198 purple_smiley_list_new(void) {
199 return g_object_new(PURPLE_TYPE_SMILEY_LIST, NULL);
202 gboolean
203 purple_smiley_list_add(PurpleSmileyList *list, PurpleSmiley *smiley) {
204 PurpleSmileyListPrivate *priv = NULL;
205 const gchar *smiley_path;
206 gboolean succ;
207 gchar *shortcut_escaped;
208 const gchar *shortcut;
210 g_return_val_if_fail(PURPLE_IS_SMILEY_LIST(list), FALSE);
211 g_return_val_if_fail(PURPLE_IS_SMILEY(smiley), FALSE);
213 priv = purple_smiley_list_get_instance_private(list);
215 if (g_object_get_data(G_OBJECT(smiley), "purple-smiley-list") != NULL) {
216 purple_debug_warning("smiley-list",
217 "smiley is already associated with some list");
218 return FALSE;
221 shortcut = purple_smiley_get_shortcut(smiley);
223 if (g_hash_table_lookup(priv->shortcut_map, shortcut) != NULL)
224 return FALSE;
226 shortcut_escaped = g_markup_escape_text(shortcut, -1);
227 succ = purple_trie_add(priv->trie, shortcut_escaped, smiley);
229 /* A special-case for WebKit, which unescapes apos entity.
230 * Please, don't trust this hack - it may be removed in future releases.
232 if (succ && strstr(shortcut_escaped, "&apos;") != NULL) {
233 gchar *tmp = shortcut_escaped;
234 shortcut_escaped = purple_strreplace(shortcut_escaped,
235 "&apos;", "'");
236 g_free(tmp);
237 succ = purple_trie_add(priv->trie, shortcut_escaped, smiley);
240 g_free(shortcut_escaped);
241 if (!succ)
242 return FALSE;
244 g_object_ref(smiley);
245 _list_append2(&priv->smileys, &priv->smileys_end, smiley);
246 g_object_set_data(G_OBJECT(smiley), "purple-smiley-list", list);
247 g_object_set_data(G_OBJECT(smiley), "purple-smiley-list-elem",
248 priv->smileys_end);
250 g_hash_table_insert(priv->shortcut_map, g_strdup(shortcut), smiley);
252 smiley_path = smiley_get_uniqid(smiley);
254 /* TODO: add to the table, when the smiley sets the path */
255 if (!smiley_path)
256 return TRUE;
258 if (g_hash_table_lookup(priv->path_map, smiley_path) == NULL) {
259 g_hash_table_insert(priv->path_map,
260 g_strdup(smiley_path), smiley);
263 return TRUE;
266 void
267 purple_smiley_list_remove(PurpleSmileyList *list, PurpleSmiley *smiley) {
268 PurpleSmileyListPrivate *priv = NULL;
269 GList *list_elem, *it;
270 const gchar *shortcut, *path;
271 gchar *shortcut_escaped;
273 g_return_if_fail(PURPLE_IS_SMILEY_LIST(list));
274 g_return_if_fail(PURPLE_IS_SMILEY(smiley));
276 priv = purple_smiley_list_get_instance_private(list);
278 if (g_object_get_data(G_OBJECT(smiley), "purple-smiley-list") != list) {
279 purple_debug_warning("smiley-list", "remove: invalid list");
280 return;
283 list_elem = g_object_get_data(G_OBJECT(smiley),
284 "purple-smiley-list-elem");
286 shortcut = purple_smiley_get_shortcut(smiley);
287 path = smiley_get_uniqid(smiley);
289 g_hash_table_remove(priv->shortcut_map, shortcut);
290 if (path)
291 g_hash_table_remove(priv->path_map, path);
293 shortcut_escaped = g_markup_escape_text(shortcut, -1);
294 purple_trie_remove(priv->trie, shortcut);
295 g_free(shortcut_escaped);
297 _list_delete_link2(&priv->smileys, &priv->smileys_end, list_elem);
299 /* re-add entry to path_map if smiley was not unique */
300 for (it = priv->smileys; it && path; it = g_list_next(it)) {
301 PurpleSmiley *smiley = it->data;
303 if (g_strcmp0(smiley_get_uniqid(smiley), path) == 0) {
304 g_hash_table_insert(priv->path_map,
305 g_strdup(path), smiley);
306 break;
310 g_object_set_data(G_OBJECT(smiley), "purple-smiley-list", NULL);
311 g_object_set_data(G_OBJECT(smiley), "purple-smiley-list-elem", NULL);
312 g_object_unref(smiley);
315 gboolean
316 purple_smiley_list_is_empty(PurpleSmileyList *list)
318 PurpleSmileyListPrivate *priv = NULL;
320 g_return_val_if_fail(PURPLE_IS_SMILEY_LIST(list), TRUE);
322 priv = purple_smiley_list_get_instance_private(list);
324 return (priv->smileys == NULL);
327 PurpleSmiley *
328 purple_smiley_list_get_by_shortcut(PurpleSmileyList *list,
329 const gchar *shortcut)
331 PurpleSmileyListPrivate *priv = NULL;
333 g_return_val_if_fail(PURPLE_IS_SMILEY_LIST(list), NULL);
335 priv = purple_smiley_list_get_instance_private(list);
337 return g_hash_table_lookup(priv->shortcut_map, shortcut);
340 PurpleTrie *
341 purple_smiley_list_get_trie(PurpleSmileyList *list) {
342 PurpleSmileyListPrivate *priv = NULL;
344 g_return_val_if_fail(PURPLE_IS_SMILEY_LIST(list), NULL);
346 priv = purple_smiley_list_get_instance_private(list);
348 return priv->trie;
351 GList *
352 purple_smiley_list_get_unique(PurpleSmileyList *list) {
353 GList *unique = NULL, *it;
354 GHashTable *unique_map;
355 PurpleSmileyListPrivate *priv = NULL;
357 g_return_val_if_fail(PURPLE_IS_SMILEY_LIST(list), NULL);
359 priv = purple_smiley_list_get_instance_private(list);
361 /* We could just return g_hash_table_get_values(priv->path_map) here,
362 * but it won't be in order. */
364 unique_map = g_hash_table_new(g_str_hash, g_str_equal);
366 for (it = priv->smileys; it; it = g_list_next(it)) {
367 PurpleSmiley *smiley = it->data;
368 const gchar *path = smiley_get_uniqid(smiley);
370 if (g_hash_table_lookup(unique_map, path))
371 continue;
373 unique = g_list_prepend(unique, smiley);
374 g_hash_table_insert(unique_map, (gpointer)path, smiley);
377 g_hash_table_destroy(unique_map);
379 return g_list_reverse(unique);
382 GList *
383 purple_smiley_list_get_all(PurpleSmileyList *list) {
384 PurpleSmileyListPrivate *priv = NULL;
386 g_return_val_if_fail(PURPLE_IS_SMILEY_LIST(list), NULL);
388 priv = purple_smiley_list_get_instance_private(list);
390 return g_hash_table_get_values(priv->shortcut_map);