3 * Pidgin 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 "gtksmiley-theme.h"
25 #include "glibcompat.h"
31 #include <glib/gstdio.h>
33 #define PIDGIN_SMILEY_THEME_MAX_LINES 1024
34 #define PIDGIN_SMILEY_THEME_MAX_TOKENS 1024
39 * An implementation of a smiley theme.
41 struct _PidginSmileyTheme
43 PurpleSmileyTheme parent
;
55 GdkPixbuf
*icon_pixbuf
;
57 GHashTable
*smiley_lists_map
;
58 } PidginSmileyThemePrivate
;
60 static gchar
**probe_dirs
;
61 static GList
*smiley_themes
= NULL
;
71 } PidginSmileyThemeIndex
;
77 } PidginSmileyThemeIndexProtocol
;
84 } PidginSmileyThemeIndexSmiley
;
86 G_DEFINE_TYPE_WITH_PRIVATE(PidginSmileyTheme
, pidgin_smiley_theme
,
87 PURPLE_TYPE_SMILEY_THEME
);
89 /*******************************************************************************
91 ******************************************************************************/
94 pidgin_smiley_theme_index_free(PidginSmileyThemeIndex
*index
)
98 g_return_if_fail(index
!= NULL
);
103 g_free(index
->author
);
105 for (it
= index
->protocols
; it
; it
= g_list_next(it
)) {
106 PidginSmileyThemeIndexProtocol
*proto
= it
->data
;
109 for (it2
= proto
->smileys
; it2
; it2
= g_list_next(it2
)) {
110 PidginSmileyThemeIndexSmiley
*smiley
= it2
->data
;
112 g_free(smiley
->file
);
113 g_list_free_full(smiley
->shortcuts
, g_free
);
116 g_list_free(proto
->smileys
);
119 g_list_free(index
->protocols
);
124 static PidginSmileyThemeIndex
*
125 pidgin_smiley_theme_index_parse(const gchar
*theme_path
, gboolean load_contents
)
127 PidginSmileyThemeIndex
*index
;
128 PidginSmileyThemeIndexProtocol
*proto
= NULL
;
132 gboolean inv_frm
= FALSE
;
134 index_path
= g_build_filename(theme_path
, "theme", NULL
);
135 file
= g_fopen(index_path
, "r");
137 purple_debug_error("gtksmiley-theme",
138 "Failed to open index file %s", index_path
);
143 index
= g_new0(PidginSmileyThemeIndex
, 1);
145 while (!feof(file
)) {
146 PidginSmileyThemeIndexSmiley
*smiley
;
152 if (++line_no
> PIDGIN_SMILEY_THEME_MAX_LINES
) {
153 purple_debug_warning("gtksmiley-theme", "file too big");
157 if (!fgets(buff
, sizeof(buff
), file
))
168 if (!g_utf8_validate(buff
, -1, NULL
)) {
169 purple_debug_error("gtksmiley-theme",
170 "%s:%d is invalid UTF-8",
171 index_path
, line_no
);
177 if (line
[0] == '[') {
183 end
= strchr(line
, ']');
190 proto
->smileys
= g_list_reverse(proto
->smileys
);
192 proto
= g_new0(PidginSmileyThemeIndexProtocol
, 1);
193 proto
->name
= g_strndup(line
, end
- line
);
196 g_list_prepend(index
->protocols
, proto
);
201 if ((eqchr
= strchr(line
, '='))) {
203 if (g_ascii_strcasecmp(line
, "name") == 0) {
205 index
->name
= g_strdup(eqchr
+ 1);
206 g_strchug(index
->name
);
208 } else if (g_ascii_strcasecmp(line
, "description") == 0) {
210 index
->desc
= g_strdup(eqchr
+ 1);
211 g_strchug(index
->desc
);
213 } else if (g_ascii_strcasecmp(line
, "icon") == 0) {
215 index
->icon
= g_strdup(eqchr
+ 1);
216 g_strchug(index
->icon
);
218 } else if (g_ascii_strcasecmp(line
, "author") == 0) {
219 g_free(index
->author
);
220 index
->author
= g_strdup(eqchr
+ 1);
221 g_strchug(index
->author
);
227 /* parsing section content */
234 smiley
= g_new0(PidginSmileyThemeIndexSmiley
, 1);
235 proto
->smileys
= g_list_prepend(proto
->smileys
, smiley
);
237 smiley
->hidden
= FALSE
;
238 if (line
[0] == '!') {
239 smiley
->hidden
= TRUE
;
243 split
= g_strsplit_set(line
, " \t",
244 PIDGIN_SMILEY_THEME_MAX_TOKENS
);
245 for (i
= 0; split
[i
]; i
++) {
246 gchar
*token
= split
[i
];
248 if (token
[0] == '\0')
250 if (i
== PIDGIN_SMILEY_THEME_MAX_TOKENS
- 1)
254 smiley
->file
= g_strdup(token
);
258 smiley
->shortcuts
= g_list_prepend(smiley
->shortcuts
,
262 smiley
->shortcuts
= g_list_reverse(smiley
->shortcuts
);
266 proto
->smileys
= g_list_reverse(proto
->smileys
);
271 purple_debug_error("gtksmiley-theme", "%s:%d"
272 " invalid format", index_path
, line_no
);
273 pidgin_smiley_theme_index_free(index
);
281 /*******************************************************************************
283 ******************************************************************************/
286 pidgin_smiley_theme_load(const gchar
*theme_path
)
288 PidginSmileyTheme
*theme
;
289 PidginSmileyThemePrivate
*priv
;
290 PidginSmileyThemeIndex
*index
;
293 /* it's not super-efficient, but we don't expect huge amount of
294 * installed themes */
295 for (it
= smiley_themes
; it
; it
= g_list_next(it
)) {
296 PidginSmileyThemePrivate
*priv
=
297 pidgin_smiley_theme_get_instance_private(it
->data
);
299 /* theme is already loaded */
300 if (g_strcmp0(priv
->path
, theme_path
) == 0)
304 theme
= g_object_new(PIDGIN_TYPE_SMILEY_THEME
, NULL
);
305 priv
= pidgin_smiley_theme_get_instance_private(theme
);
307 priv
->path
= g_strdup(theme_path
);
309 index
= pidgin_smiley_theme_index_parse(theme_path
, FALSE
);
311 if (!index
->name
|| index
->name
[0] == '\0') {
312 purple_debug_warning("gtksmiley-theme",
313 "incomplete theme %s", theme_path
);
314 pidgin_smiley_theme_index_free(index
);
315 g_object_unref(theme
);
319 priv
->name
= g_strdup(index
->name
);
320 if (index
->desc
&& index
->desc
[0])
321 priv
->desc
= g_strdup(index
->desc
);
322 if (index
->icon
&& index
->icon
[0])
323 priv
->icon
= g_strdup(index
->icon
);
324 if (index
->author
&& index
->author
[0])
325 priv
->author
= g_strdup(index
->author
);
327 pidgin_smiley_theme_index_free(index
);
329 smiley_themes
= g_list_append(smiley_themes
, theme
);
333 pidgin_smiley_theme_probe(void)
338 /* remove non-existing themes */
339 for (it
= smiley_themes
; it
; it
= next
) {
340 PidginSmileyTheme
*theme
= it
->data
;
341 PidginSmileyThemePrivate
*priv
=
342 pidgin_smiley_theme_get_instance_private(theme
);
344 next
= g_list_next(it
);
346 if (g_file_test(priv
->path
, G_FILE_TEST_EXISTS
))
348 smiley_themes
= g_list_delete_link(smiley_themes
, it
);
349 g_object_unref(theme
);
352 /* scan for themes */
353 for (i
= 0; probe_dirs
[i
]; i
++) {
354 GDir
*dir
= g_dir_open(probe_dirs
[i
], 0, NULL
);
355 const gchar
*theme_dir_name
;
360 while ((theme_dir_name
= g_dir_read_name(dir
))) {
363 /* Ignore Pidgin 2.x.y "none" theme. */
364 if (g_strcmp0(theme_dir_name
, "none") == 0)
367 theme_path
= g_build_filename(
368 probe_dirs
[i
], theme_dir_name
, NULL
);
370 if (g_file_test(theme_path
, G_FILE_TEST_IS_DIR
))
371 pidgin_smiley_theme_load(theme_path
);
381 /*******************************************************************************
383 ******************************************************************************/
386 pidgin_smiley_theme_get_name(PidginSmileyTheme
*theme
)
388 PidginSmileyThemePrivate
*priv
= NULL
;
390 g_return_val_if_fail(PIDGIN_IS_SMILEY_THEME(theme
), NULL
);
392 priv
= pidgin_smiley_theme_get_instance_private(theme
);
397 pidgin_smiley_theme_get_description(PidginSmileyTheme
*theme
)
399 PidginSmileyThemePrivate
*priv
= NULL
;
401 g_return_val_if_fail(PIDGIN_IS_SMILEY_THEME(theme
), NULL
);
403 priv
= pidgin_smiley_theme_get_instance_private(theme
);
408 pidgin_smiley_theme_get_icon(PidginSmileyTheme
*theme
)
410 PidginSmileyThemePrivate
*priv
= NULL
;
412 g_return_val_if_fail(PIDGIN_IS_SMILEY_THEME(theme
), NULL
);
413 priv
= pidgin_smiley_theme_get_instance_private(theme
);
415 if (priv
->icon
== NULL
)
418 if (!priv
->icon_pixbuf
) {
419 gchar
*icon_path
= g_build_filename(
420 priv
->path
, priv
->icon
, NULL
);
421 priv
->icon_pixbuf
= pidgin_pixbuf_new_from_file(icon_path
);
425 return priv
->icon_pixbuf
;
429 pidgin_smiley_theme_get_author(PidginSmileyTheme
*theme
)
431 PidginSmileyThemePrivate
*priv
= NULL
;
433 g_return_val_if_fail(PIDGIN_IS_SMILEY_THEME(theme
), NULL
);
435 priv
= pidgin_smiley_theme_get_instance_private(theme
);
440 pidgin_smiley_theme_for_conv(PurpleConversation
*conv
)
442 PurpleAccount
*acc
= NULL
;
443 PurpleSmileyTheme
*theme
;
444 const gchar
*proto_name
= NULL
;
446 theme
= purple_smiley_theme_get_current();
451 acc
= purple_conversation_get_account(conv
);
453 proto_name
= purple_account_get_protocol_name(acc
);
455 return purple_smiley_theme_get_smileys(theme
, (gpointer
)proto_name
);
459 pidgin_smiley_theme_activate_impl(PurpleSmileyTheme
*theme
)
461 PidginSmileyThemePrivate
*priv
=
462 pidgin_smiley_theme_get_instance_private(
463 PIDGIN_SMILEY_THEME(theme
));
464 PidginSmileyThemeIndex
*index
;
466 GList
*it
, *it2
, *it3
;
468 if (priv
->smiley_lists_map
)
471 priv
->smiley_lists_map
= smap
= g_hash_table_new_full(
472 g_str_hash
, g_str_equal
, g_free
, g_object_unref
);
474 index
= pidgin_smiley_theme_index_parse(priv
->path
, TRUE
);
476 for (it
= index
->protocols
; it
; it
= g_list_next(it
)) {
477 PidginSmileyThemeIndexProtocol
*proto_idx
= it
->data
;
478 PurpleSmileyList
*proto_smileys
;
480 proto_smileys
= g_hash_table_lookup(smap
, proto_idx
->name
);
481 if (!proto_smileys
) {
482 proto_smileys
= purple_smiley_list_new();
483 g_hash_table_insert(smap
,
484 g_strdup(proto_idx
->name
), proto_smileys
);
487 for (it2
= proto_idx
->smileys
; it2
; it2
= g_list_next(it2
)) {
488 PidginSmileyThemeIndexSmiley
*smiley_idx
= it2
->data
;
491 smiley_path
= g_build_filename(
492 priv
->path
, smiley_idx
->file
, NULL
);
493 if (!g_file_test(smiley_path
, G_FILE_TEST_EXISTS
)) {
494 purple_debug_warning("gtksmiley-theme",
495 "Smiley %s is missing", smiley_path
);
500 for (it3
= smiley_idx
->shortcuts
; it3
;
501 it3
= g_list_next(it3
))
503 PurpleSmiley
*smiley
;
504 gchar
*shortcut
= it3
->data
;
506 smiley
= purple_smiley_new(
507 shortcut
, smiley_path
);
508 g_object_set_data(G_OBJECT(smiley
),
509 "pidgin-smiley-hidden",
510 GINT_TO_POINTER(smiley_idx
->hidden
));
511 purple_smiley_list_add(proto_smileys
, smiley
);
512 g_object_unref(smiley
);
519 pidgin_smiley_theme_index_free(index
);
522 static PurpleSmileyList
*
523 pidgin_smiley_theme_get_smileys_impl(PurpleSmileyTheme
*theme
, gpointer ui_data
)
525 PidginSmileyThemePrivate
*priv
=
526 pidgin_smiley_theme_get_instance_private(
527 PIDGIN_SMILEY_THEME(theme
));
528 PurpleSmileyList
*smileys
= NULL
;
530 pidgin_smiley_theme_activate_impl(theme
);
533 smileys
= g_hash_table_lookup(priv
->smiley_lists_map
, ui_data
);
537 return g_hash_table_lookup(priv
->smiley_lists_map
, "default");
541 pidgin_smiley_theme_get_all(void)
543 pidgin_smiley_theme_probe();
545 return smiley_themes
;
549 _pidgin_smiley_theme_init(void)
552 const gchar
*user_smileys_dir
;
553 const gchar
*theme_name
;
555 probe_dirs
= g_new0(gchar
*, 3);
556 probe_dirs
[0] = g_build_filename(
557 PURPLE_DATADIR
, "pixmaps", "pidgin", "emotes", NULL
);
558 user_smileys_dir
= probe_dirs
[1] = g_build_filename(
559 purple_user_dir(), "smileys", NULL
);
561 if (!g_file_test(user_smileys_dir
, G_FILE_TEST_IS_DIR
)) {
562 if (g_mkdir(user_smileys_dir
, S_IRUSR
| S_IWUSR
| S_IXUSR
) == 0) {
563 purple_debug_error("gtksmiley-theme",
564 "Failed to create user smileys dir");
568 /* setting theme by name (copy-paste from gtkprefs) */
569 pidgin_smiley_theme_probe();
570 theme_name
= purple_prefs_get_string(
571 PIDGIN_PREFS_ROOT
"/smileys/theme");
572 for (it
= smiley_themes
; it
; it
= g_list_next(it
)) {
573 PidginSmileyTheme
*theme
= it
->data
;
575 if (g_strcmp0(pidgin_smiley_theme_get_name(theme
), theme_name
))
578 purple_smiley_theme_set_current(PURPLE_SMILEY_THEME(theme
));
583 _pidgin_smiley_theme_uninit(void)
585 g_strfreev(probe_dirs
);
588 /*******************************************************************************
590 ******************************************************************************/
593 pidgin_smiley_theme_finalize(GObject
*obj
)
595 PidginSmileyThemePrivate
*priv
=
596 pidgin_smiley_theme_get_instance_private(
597 PIDGIN_SMILEY_THEME(obj
));
603 g_free(priv
->author
);
604 if (priv
->icon_pixbuf
)
605 g_object_unref(priv
->icon_pixbuf
);
606 if (priv
->smiley_lists_map
)
607 g_hash_table_destroy(priv
->smiley_lists_map
);
609 G_OBJECT_CLASS(pidgin_smiley_theme_parent_class
)->finalize(obj
);
613 pidgin_smiley_theme_class_init(PidginSmileyThemeClass
*klass
)
615 GObjectClass
*gobj_class
= G_OBJECT_CLASS(klass
);
616 PurpleSmileyThemeClass
*pst_class
= PURPLE_SMILEY_THEME_CLASS(klass
);
618 gobj_class
->finalize
= pidgin_smiley_theme_finalize
;
620 pst_class
->get_smileys
= pidgin_smiley_theme_get_smileys_impl
;
621 pst_class
->activate
= pidgin_smiley_theme_activate_impl
;
625 pidgin_smiley_theme_init(PidginSmileyTheme
*theme
)