| 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
| 20 */ |
20 */ |
| 21 |
21 |
| 22 #include "gtksmiley-theme.h" |
22 #include "gtksmiley-theme.h" |
| 23 |
23 |
| |
24 #include "internal.h" |
| |
25 #include "glibcompat.h" |
| |
26 |
| 24 #include "debug.h" |
27 #include "debug.h" |
| 25 |
28 |
| 26 #include <glib/gstdio.h> |
29 #include <glib/gstdio.h> |
| 27 |
30 |
| 28 #define PIDGIN_SMILEY_THEME_GET_PRIVATE(obj) \ |
31 #define PIDGIN_SMILEY_THEME_GET_PRIVATE(obj) \ |
| 29 (G_TYPE_INSTANCE_GET_PRIVATE((obj), PIDGIN_TYPE_SMILEY_THEME, \ |
32 (G_TYPE_INSTANCE_GET_PRIVATE((obj), PIDGIN_TYPE_SMILEY_THEME, \ |
| 30 PidginSmileyThemePrivate)) |
33 PidginSmileyThemePrivate)) |
| 31 |
34 |
| 32 #define PIDGIN_SMILEY_THEME_MAX_INDEX_SIZE 102400 |
35 #define PIDGIN_SMILEY_THEME_MAX_LINES 1024 |
| |
36 #define PIDGIN_SMILEY_THEME_MAX_TOKENS 1024 |
| 33 |
37 |
| 34 typedef struct |
38 typedef struct |
| 35 { |
39 { |
| 36 gchar *path; |
40 gchar *path; |
| |
41 |
| |
42 gchar *name; |
| |
43 gchar *desc; |
| |
44 gchar *icon; |
| |
45 gchar *author; |
| 37 } PidginSmileyThemePrivate; |
46 } PidginSmileyThemePrivate; |
| 38 |
47 |
| 39 static GObjectClass *parent_class; |
48 static GObjectClass *parent_class; |
| 40 |
49 |
| 41 static gchar **probe_dirs; |
50 static gchar **probe_dirs; |
| 42 static GList *smiley_themes = NULL; |
51 static GList *smiley_themes = NULL; |
| |
52 |
| |
53 typedef struct |
| |
54 { |
| |
55 gchar *name; |
| |
56 gchar *desc; |
| |
57 gchar *icon; |
| |
58 gchar *author; |
| |
59 |
| |
60 GList *protocols; |
| |
61 } PidginSmileyThemeIndex; |
| |
62 |
| |
63 typedef struct |
| |
64 { |
| |
65 gchar *name; |
| |
66 GList *smileys; |
| |
67 } PidginSmileyThemeIndexProtocol; |
| |
68 |
| |
69 typedef struct |
| |
70 { |
| |
71 gchar *file; |
| |
72 gboolean hidden; |
| |
73 GList *shortcuts; |
| |
74 } PidginSmileyThemeIndexSmiley; |
| |
75 |
| |
76 /******************************************************************************* |
| |
77 * Theme index parsing |
| |
78 ******************************************************************************/ |
| |
79 |
| |
80 static void |
| |
81 pidgin_smiley_theme_index_free(PidginSmileyThemeIndex *index) |
| |
82 { |
| |
83 GList *it, *it2; |
| |
84 |
| |
85 g_return_if_fail(index != NULL); |
| |
86 |
| |
87 g_free(index->name); |
| |
88 g_free(index->desc); |
| |
89 g_free(index->icon); |
| |
90 g_free(index->author); |
| |
91 |
| |
92 for (it = index->protocols; it; it = g_list_next(it)) { |
| |
93 PidginSmileyThemeIndexProtocol *proto = it->data; |
| |
94 |
| |
95 g_free(proto->name); |
| |
96 for (it2 = proto->smileys; it2; it2 = g_list_next(it2)) { |
| |
97 PidginSmileyThemeIndexSmiley *smiley = it2->data; |
| |
98 |
| |
99 g_free(smiley->file); |
| |
100 g_list_free_full(smiley->shortcuts, g_free); |
| |
101 g_free(smiley); |
| |
102 } |
| |
103 g_list_free(proto->smileys); |
| |
104 g_free(proto); |
| |
105 } |
| |
106 g_list_free(index->protocols); |
| |
107 } |
| |
108 |
| |
109 static PidginSmileyThemeIndex * |
| |
110 pidgin_smiley_theme_index_parse(const gchar *index_path, gboolean load_contents) |
| |
111 { |
| |
112 PidginSmileyThemeIndex *index; |
| |
113 PidginSmileyThemeIndexProtocol *proto = NULL; |
| |
114 FILE *file; |
| |
115 int line_no = 0; |
| |
116 gboolean inv_frm = FALSE; |
| |
117 |
| |
118 file = g_fopen(index_path, "r"); |
| |
119 if (!file) { |
| |
120 purple_debug_error("gtksmiley-theme", |
| |
121 "Failed to open index file %s", index_path); |
| |
122 return NULL; |
| |
123 } |
| |
124 |
| |
125 index = g_new0(PidginSmileyThemeIndex, 1); |
| |
126 |
| |
127 while (!feof(file)) { |
| |
128 PidginSmileyThemeIndexSmiley *smiley; |
| |
129 gchar buff[1024]; |
| |
130 gchar *line, *eqchr; |
| |
131 gchar **split; |
| |
132 int i; |
| |
133 |
| |
134 if (++line_no > PIDGIN_SMILEY_THEME_MAX_LINES) { |
| |
135 purple_debug_warning("gtksmiley-theme", "file too big"); |
| |
136 break; |
| |
137 } |
| |
138 |
| |
139 if (!fgets(buff, sizeof(buff), file)) |
| |
140 break; |
| |
141 |
| |
142 /* strip comments */ |
| |
143 if (buff[0] == '#') |
| |
144 continue; |
| |
145 |
| |
146 g_strstrip(buff); |
| |
147 if (buff[0] == '\0') |
| |
148 continue; |
| |
149 |
| |
150 if (!g_utf8_validate(buff, -1, NULL)) { |
| |
151 purple_debug_error("gtksmiley-theme", "%s:%" |
| |
152 G_GSIZE_FORMAT " is invalid UTF-8", |
| |
153 index_path, line_no); |
| |
154 continue; |
| |
155 } |
| |
156 |
| |
157 line = buff; |
| |
158 |
| |
159 if (line[0] == '[') { |
| |
160 gchar *end; |
| |
161 |
| |
162 if (!load_contents) |
| |
163 break; |
| |
164 line++; |
| |
165 end = strchr(line, ']'); |
| |
166 if (!end) { |
| |
167 inv_frm = TRUE; |
| |
168 break; |
| |
169 } |
| |
170 |
| |
171 proto = g_new0(PidginSmileyThemeIndexProtocol, 1); |
| |
172 proto->name = g_strndup(line, end - line); |
| |
173 |
| |
174 index->protocols = |
| |
175 g_list_prepend(index->protocols, proto); |
| |
176 |
| |
177 continue; |
| |
178 } |
| |
179 |
| |
180 if ((eqchr = strchr(line, '='))) { |
| |
181 *eqchr = '\0'; |
| |
182 if (g_ascii_strcasecmp(line, "name") == 0) { |
| |
183 g_free(index->name); |
| |
184 index->name = g_strdup(eqchr + 1); |
| |
185 g_strchug(index->name); |
| |
186 continue; |
| |
187 } else if (g_ascii_strcasecmp(line, "description") == 0) { |
| |
188 g_free(index->desc); |
| |
189 index->desc = g_strdup(eqchr + 1); |
| |
190 g_strchug(index->desc); |
| |
191 continue; |
| |
192 } else if (g_ascii_strcasecmp(line, "icon") == 0) { |
| |
193 g_free(index->icon); |
| |
194 index->icon = g_strdup(eqchr + 1); |
| |
195 g_strchug(index->icon); |
| |
196 continue; |
| |
197 } else if (g_ascii_strcasecmp(line, "author") == 0) { |
| |
198 g_free(index->author); |
| |
199 index->author = g_strdup(eqchr + 1); |
| |
200 g_strchug(index->author); |
| |
201 continue; |
| |
202 } |
| |
203 *eqchr = '='; |
| |
204 } |
| |
205 |
| |
206 /* parsing section content */ |
| |
207 |
| |
208 if (proto == NULL) { |
| |
209 inv_frm = FALSE; |
| |
210 break; |
| |
211 } |
| |
212 |
| |
213 smiley = g_new0(PidginSmileyThemeIndexSmiley, 1); |
| |
214 proto->smileys = g_list_prepend(proto->smileys, smiley); |
| |
215 |
| |
216 smiley->hidden = FALSE; |
| |
217 if (line[0] == '!') { |
| |
218 smiley->hidden = TRUE; |
| |
219 line++; |
| |
220 } |
| |
221 |
| |
222 split = g_strsplit_set(line, " \t", |
| |
223 PIDGIN_SMILEY_THEME_MAX_TOKENS); |
| |
224 for (i = 0; split[i]; i++) { |
| |
225 gchar *token = split[i]; |
| |
226 |
| |
227 if (token[0] == '\0') |
| |
228 continue; |
| |
229 if (i == PIDGIN_SMILEY_THEME_MAX_TOKENS - 1) |
| |
230 break; |
| |
231 |
| |
232 if (!smiley->file) { |
| |
233 smiley->file = g_strdup(token); |
| |
234 continue; |
| |
235 } |
| |
236 |
| |
237 smiley->shortcuts = g_list_prepend(smiley->shortcuts, |
| |
238 g_strdup(token)); |
| |
239 } |
| |
240 g_strfreev(split); |
| |
241 } |
| |
242 |
| |
243 fclose(file); |
| |
244 |
| |
245 if (inv_frm) { |
| |
246 purple_debug_error("gtksmiley-theme", "%s:%" G_GSIZE_FORMAT |
| |
247 " invalid format", index_path, line_no); |
| |
248 pidgin_smiley_theme_index_free(index); |
| |
249 index = NULL; |
| |
250 } |
| |
251 |
| |
252 return index; |
| |
253 } |
| 43 |
254 |
| 44 /******************************************************************************* |
255 /******************************************************************************* |
| 45 * Theme loading |
256 * Theme loading |
| 46 ******************************************************************************/ |
257 ******************************************************************************/ |
| 47 |
258 |
| 48 static void |
259 static void |
| 49 pidgin_smiley_theme_load(const gchar *theme_path) |
260 pidgin_smiley_theme_load(const gchar *theme_path) |
| 50 { |
261 { |
| 51 PidginSmileyTheme *theme; |
262 PidginSmileyTheme *theme; |
| 52 PidginSmileyThemePrivate *priv; |
263 PidginSmileyThemePrivate *priv; |
| |
264 PidginSmileyThemeIndex *index; |
| 53 GList *it; |
265 GList *it; |
| 54 gchar *index_path; |
266 gchar *index_path; |
| 55 |
267 |
| 56 /* it's not super-efficient, but we don't expect huge amount of |
268 /* it's not super-efficient, but we don't expect huge amount of |
| 57 * installed themes */ |
269 * installed themes */ |
| 69 theme = g_object_new(PIDGIN_TYPE_SMILEY_THEME, NULL); |
281 theme = g_object_new(PIDGIN_TYPE_SMILEY_THEME, NULL); |
| 70 priv = PIDGIN_SMILEY_THEME_GET_PRIVATE(theme); |
282 priv = PIDGIN_SMILEY_THEME_GET_PRIVATE(theme); |
| 71 |
283 |
| 72 priv->path = g_strdup(theme_path); |
284 priv->path = g_strdup(theme_path); |
| 73 |
285 |
| |
286 index = pidgin_smiley_theme_index_parse(index_path, FALSE); |
| |
287 |
| 74 g_free(index_path); |
288 g_free(index_path); |
| 75 |
289 |
| 76 purple_debug_fatal("tomo", "loading not implemented"); |
290 if (!index->name || index->name[0] == '\0') { |
| |
291 purple_debug_warning("gtksmiley-theme", |
| |
292 "incomplete theme %s", theme_path); |
| |
293 pidgin_smiley_theme_index_free(index); |
| |
294 g_object_unref(theme); |
| |
295 return; |
| |
296 } |
| |
297 |
| |
298 priv->name = g_strdup(index->name); |
| |
299 if (index->desc && index->desc[0]) |
| |
300 priv->desc = g_strdup(index->desc); |
| |
301 if (index->icon && index->icon[0]) |
| |
302 priv->icon = g_strdup(index->icon); |
| |
303 if (index->author && index->author[0]) |
| |
304 priv->author = g_strdup(index->author); |
| |
305 |
| |
306 pidgin_smiley_theme_index_free(index); |
| |
307 |
| |
308 smiley_themes = g_list_append(smiley_themes, theme); |
| 77 } |
309 } |
| 78 |
310 |
| 79 static void |
311 static void |
| 80 pidgin_smiley_theme_probe(void) |
312 pidgin_smiley_theme_probe(void) |
| 81 { |
313 { |