Merged default into xdg-dirs
[pidgin-git.git] / libpurple / smiley-parser.c
blob05b2e1c3d5f0cff89f7835900365b748995a2a32
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-parser.h"
24 #include "smiley-custom.h"
25 #include "smiley-theme.h"
27 #define DISPLAY_OUR_CUSTOM_SMILEYS_FOR_INCOMING_MESSAGES 1
29 typedef struct
31 union {
32 struct {
33 PurpleConversation *conv;
34 PurpleSmileyParseCb cb;
35 gpointer ui_data;
36 } replace;
37 struct {
38 GHashTable *found_smileys;
39 } find;
40 } job;
42 gboolean in_html_tag;
43 } PurpleSmileyParseData;
45 static PurpleTrie *html_sentry;
47 static inline void
48 purple_smiley_parse_check_html(const gchar *word,
49 PurpleSmileyParseData *parse_data)
51 if (G_LIKELY(word[0] == '<'))
52 parse_data->in_html_tag = TRUE;
53 else if (G_LIKELY(word[0] == '>'))
54 parse_data->in_html_tag = FALSE;
55 else
56 g_return_if_reached();
57 g_warn_if_fail(word[1] == '\0');
60 static gboolean
61 purple_smiley_parse_cb(GString *out, const gchar *word, gpointer _smiley,
62 gpointer _parse_data)
64 PurpleSmileyParseData *parse_data = _parse_data;
65 PurpleSmiley *smiley = _smiley;
67 /* a special-case for html_sentry */
68 if (smiley == NULL) {
69 purple_smiley_parse_check_html(word, parse_data);
70 return FALSE;
73 if (parse_data->in_html_tag)
74 return FALSE;
76 return parse_data->job.replace.cb(out, smiley,
77 parse_data->job.replace.conv, parse_data->job.replace.ui_data);
80 /* XXX: this shouldn't be a conv for incoming messages - see
81 * PurpleConversationPrivate.remote_smileys.
82 * For outgoing messages, we could pass conv in ui_data (or something).
83 * Or maybe we should use two distinct functions?
84 * To be reconsidered when we had PurpleDude-like objects.
86 gchar *
87 purple_smiley_parser_smileify(PurpleConversation *conv, const gchar *html_message,
88 gboolean use_remote_smileys, PurpleSmileyParseCb cb, gpointer ui_data)
90 PurpleSmileyTheme *theme;
91 PurpleSmileyList *theme_smileys = NULL, *remote_smileys = NULL;
92 PurpleTrie *theme_trie = NULL, *custom_trie = NULL, *remote_trie = NULL;
93 GSList *tries = NULL;
94 GSList tries_sentry, tries_theme, tries_custom, tries_remote;
95 PurpleSmileyParseData parse_data;
97 if (html_message == NULL || html_message[0] == '\0')
98 return g_strdup(html_message);
100 /* get remote smileys */
101 if (use_remote_smileys)
102 remote_smileys = purple_conversation_get_remote_smileys(conv);
103 if (remote_smileys)
104 remote_trie = purple_smiley_list_get_trie(remote_smileys);
105 if (remote_trie && purple_trie_get_size(remote_trie) == 0)
106 remote_trie = NULL;
108 /* get custom smileys */
109 if (purple_conversation_get_features(conv) &
110 PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY)
112 custom_trie = purple_smiley_list_get_trie(
113 purple_smiley_custom_get_list());
114 #if !DISPLAY_OUR_CUSTOM_SMILEYS_FOR_INCOMING_MESSAGES
115 if (use_remote_smileys)
116 custom_trie = NULL;
117 #endif
119 if (custom_trie && purple_trie_get_size(custom_trie) == 0)
120 custom_trie = NULL;
122 /* get theme smileys */
123 theme = purple_smiley_theme_get_current();
124 if (theme != NULL)
125 theme_smileys = purple_smiley_theme_get_smileys(theme, ui_data);
126 if (theme_smileys != NULL)
127 theme_trie = purple_smiley_list_get_trie(theme_smileys);
129 /* we have absolutely no smileys */
130 if (theme_trie == NULL && custom_trie == NULL && remote_trie == NULL)
131 return g_strdup(html_message);
133 /* Create a tries list on the stack. */
134 tries_sentry.data = html_sentry;
135 tries_theme.data = theme_trie;
136 tries_custom.data = custom_trie;
137 tries_remote.data = remote_trie;
138 tries_sentry.next = NULL;
139 tries_theme.next = tries_custom.next = tries_remote.next = NULL;
140 tries = &tries_sentry;
141 if (remote_trie != NULL)
142 g_slist_last(tries)->next = &tries_remote;
143 if (custom_trie != NULL)
144 g_slist_last(tries)->next = &tries_custom;
145 if (theme_trie != NULL)
146 g_slist_last(tries)->next = &tries_theme;
148 parse_data.job.replace.conv = conv;
149 parse_data.job.replace.cb = cb;
150 parse_data.job.replace.ui_data = ui_data;
151 parse_data.in_html_tag = FALSE;
153 /* TODO: parse greedily (as much as possible) when PurpleTrie
154 * provides support for it. */
155 return purple_trie_multi_replace(tries, html_message,
156 purple_smiley_parse_cb, &parse_data);
159 gchar *
160 purple_smiley_parser_replace(PurpleSmileyList *smileys,
161 const gchar *html_message, PurpleSmileyParseCb cb, gpointer ui_data)
163 PurpleTrie *smileys_trie = NULL;
164 GSList trie_1, trie_2;
165 PurpleSmileyParseData parse_data;
167 if (html_message == NULL || html_message[0] == '\0')
168 return g_strdup(html_message);
170 if (smileys == NULL || purple_smiley_list_is_empty(smileys))
171 return g_strdup(html_message);
173 smileys_trie = purple_smiley_list_get_trie(smileys);
174 g_return_val_if_fail(smileys_trie != NULL, NULL);
176 trie_1.data = html_sentry;
177 trie_2.data = smileys_trie;
178 trie_1.next = &trie_2;
179 trie_2.next = NULL;
181 parse_data.job.replace.conv = NULL;
182 parse_data.job.replace.cb = cb;
183 parse_data.job.replace.ui_data = ui_data;
184 parse_data.in_html_tag = FALSE;
186 return purple_trie_multi_replace(&trie_1, html_message,
187 purple_smiley_parse_cb, &parse_data);
190 static gboolean
191 smiley_find_cb(const gchar *word, gpointer _smiley, gpointer _parse_data)
193 PurpleSmiley *smiley = _smiley;
194 PurpleSmileyParseData *parse_data = _parse_data;
196 /* a special-case for html_sentry */
197 if (smiley == NULL) {
198 purple_smiley_parse_check_html(word, parse_data);
199 return FALSE;
202 if (parse_data->in_html_tag)
203 return FALSE;
205 g_hash_table_insert(parse_data->job.find.found_smileys, smiley, smiley);
207 return TRUE;
210 GList *
211 purple_smiley_parser_find(PurpleSmileyList *smileys, const gchar *message,
212 gboolean is_html)
214 PurpleTrie *smileys_trie;
215 GList *found_list;
216 gchar *escaped_message = NULL;
217 PurpleSmileyParseData parse_data;
218 GSList trie_1, trie_2;
220 if (message == NULL || message[0] == '\0')
221 return NULL;
223 if (smileys == NULL || purple_smiley_list_is_empty(smileys))
224 return NULL;
226 smileys_trie = purple_smiley_list_get_trie(smileys);
227 g_return_val_if_fail(smileys_trie != NULL, NULL);
229 if (!is_html)
230 message = escaped_message = g_markup_escape_text(message, -1);
232 parse_data.job.find.found_smileys =
233 g_hash_table_new(g_direct_hash, g_direct_equal);
234 parse_data.in_html_tag = FALSE;
236 trie_1.data = html_sentry;
237 trie_2.data = smileys_trie;
238 trie_1.next = &trie_2;
239 trie_2.next = NULL;
240 purple_trie_multi_find(&trie_1, message, smiley_find_cb, &parse_data);
242 g_free(escaped_message);
244 found_list = g_hash_table_get_values(parse_data.job.find.found_smileys);
245 g_hash_table_destroy(parse_data.job.find.found_smileys);
247 return found_list;
250 void
251 _purple_smiley_parser_init(void)
253 html_sentry = purple_trie_new();
254 purple_trie_add(html_sentry, "<", NULL);
255 purple_trie_add(html_sentry, ">", NULL);
258 void
259 _purple_smiley_parser_uninit(void)
261 g_object_unref(html_sentry);