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
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"
25 #include "smiley-parser.h"
33 * A container for #PurpleSmiley's.
35 struct _PurpleSmileyList
{
44 GHashTable
*shortcut_map
;
46 gboolean drop_failed_remotes
;
47 } PurpleSmileyListPrivate
;
52 PROP_DROP_FAILED_REMOTES
,
56 static GParamSpec
*properties
[PROP_LAST
];
58 /*******************************************************************************
60 ******************************************************************************/
61 G_DEFINE_TYPE_WITH_PRIVATE(PurpleSmileyList
, purple_smiley_list
, G_TYPE_OBJECT
);
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
,
70 priv
->shortcut_map
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
75 purple_smiley_list_finalize(GObject
*obj
) {
76 PurpleSmileyList
*sl
= PURPLE_SMILEY_LIST(obj
);
77 PurpleSmileyListPrivate
*priv
= purple_smiley_list_get_instance_private(sl
);
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
);
96 purple_smiley_list_get_property(GObject
*obj
, guint param_id
, GValue
*value
,
99 PurpleSmileyList
*sl
= PURPLE_SMILEY_LIST(obj
);
100 PurpleSmileyListPrivate
*priv
= purple_smiley_list_get_instance_private(sl
);
103 case PROP_DROP_FAILED_REMOTES
:
104 g_value_set_boolean(value
, priv
->drop_failed_remotes
);
107 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
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
);
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.
127 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
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 /******************************************************************************
151 *****************************************************************************/
153 _list_append2(GList
**head_p
, GList
**tail_p
, gpointer data
)
155 GList
*head
= *head_p
;
156 GList
*tail
= *tail_p
;
159 g_return_if_fail((head
== NULL
) == (tail
== NULL
));
160 g_return_if_fail((tail
== NULL
) || (tail
->next
== NULL
));
162 elem
= g_list_alloc();
171 *head_p
= *tail_p
= elem
;
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
);
184 *tail_p
= tail
->prev
;
185 *head_p
= g_list_delete_link(head
, link
);
189 smiley_get_uniqid(PurpleSmiley
*smiley
)
191 return purple_image_get_path(PURPLE_IMAGE(smiley
));
194 /*******************************************************************************
196 ******************************************************************************/
198 purple_smiley_list_new(void) {
199 return g_object_new(PURPLE_TYPE_SMILEY_LIST
, NULL
);
203 purple_smiley_list_add(PurpleSmileyList
*list
, PurpleSmiley
*smiley
) {
204 PurpleSmileyListPrivate
*priv
= NULL
;
205 const gchar
*smiley_path
;
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");
221 shortcut
= purple_smiley_get_shortcut(smiley
);
223 if (g_hash_table_lookup(priv
->shortcut_map
, shortcut
) != NULL
)
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
, "'") != NULL
) {
233 gchar
*tmp
= shortcut_escaped
;
234 shortcut_escaped
= purple_strreplace(shortcut_escaped
,
237 succ
= purple_trie_add(priv
->trie
, shortcut_escaped
, smiley
);
240 g_free(shortcut_escaped
);
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",
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 */
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
);
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");
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
);
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
);
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
);
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
);
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
);
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
);
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
))
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
);
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
);