4 * Pidgin is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
26 #include "conversation.h"
32 #include "gtkdialogs.h"
33 #include "gtkimhtml.h"
34 #include "gtkthemes.h"
36 GSList
*smiley_themes
= NULL
;
37 struct smiley_theme
*current_smiley_theme
;
39 static void pidgin_themes_destroy_smiley_theme_smileys(struct smiley_theme
*theme
);
41 gboolean
pidgin_themes_smileys_disabled()
43 if (!current_smiley_theme
)
46 return strcmp(current_smiley_theme
->name
, "none") == 0;
50 pidgin_themes_destroy_smiley_theme(struct smiley_theme
*theme
)
52 pidgin_themes_destroy_smiley_theme_smileys(theme
);
56 g_free(theme
->author
);
62 static void pidgin_themes_remove_theme_dir(const char *theme_dir_name
)
65 const char *file_name
= NULL
;
66 GDir
*theme_dir
= NULL
;
68 if ((theme_dir
= g_dir_open(theme_dir_name
, 0, NULL
)) != NULL
) {
69 if ((str
= g_string_new(theme_dir_name
)) != NULL
) {
70 while ((file_name
= g_dir_read_name(theme_dir
)) != NULL
) {
71 g_string_printf(str
, "%s%s%s", theme_dir_name
, G_DIR_SEPARATOR_S
, file_name
);
74 g_string_free(str
, TRUE
);
76 g_dir_close(theme_dir
);
77 g_rmdir(theme_dir_name
);
81 void pidgin_themes_remove_smiley_theme(const char *file
)
83 char *theme_dir
= NULL
, *last_slash
= NULL
;
84 g_return_if_fail(NULL
!= file
);
86 if (!g_file_test(file
, G_FILE_TEST_EXISTS
)) return;
87 if ((theme_dir
= g_strdup(file
)) == NULL
) return ;
89 if ((last_slash
= g_strrstr(theme_dir
, G_DIR_SEPARATOR_S
)) != NULL
) {
91 struct smiley_theme
*theme
= NULL
, *new_theme
= NULL
;
95 /* Delete files on disk */
96 pidgin_themes_remove_theme_dir(theme_dir
);
98 /* Find theme in themes list and remove it */
99 for (iter
= smiley_themes
; iter
; iter
= iter
->next
) {
100 theme
= ((struct smiley_theme
*)(iter
->data
));
101 if (!strcmp(theme
->path
, file
))
105 if (theme
== current_smiley_theme
) {
106 new_theme
= ((struct smiley_theme
*)(NULL
== iter
->next
? (smiley_themes
== iter
? NULL
: smiley_themes
->data
) : iter
->next
->data
));
108 purple_prefs_set_string(PIDGIN_PREFS_ROOT
"/smileys/theme", new_theme
->name
);
110 current_smiley_theme
= NULL
;
112 smiley_themes
= g_slist_delete_link(smiley_themes
, iter
);
114 /* Destroy theme structure */
115 pidgin_themes_destroy_smiley_theme(theme
);
122 void pidgin_themes_smiley_themeize(GtkWidget
*imhtml
)
124 struct smiley_list
*list
;
125 if (!current_smiley_theme
)
128 gtk_imhtml_remove_smileys(GTK_IMHTML(imhtml
));
129 list
= current_smiley_theme
->list
;
131 char *sml
= !strcmp(list
->sml
, "default") ? NULL
: list
->sml
;
132 GSList
*icons
= list
->smileys
;
134 gtk_imhtml_associate_smiley(GTK_IMHTML(imhtml
), sml
, icons
->data
);
142 pidgin_themes_destroy_smiley_theme_smileys(struct smiley_theme
*theme
)
144 GHashTable
*already_freed
;
145 struct smiley_list
*wer
;
147 already_freed
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
148 for (wer
= theme
->list
; wer
!= NULL
; wer
= theme
->list
) {
149 while (wer
->smileys
) {
150 GtkIMHtmlSmiley
*uio
= wer
->smileys
->data
;
152 g_object_unref(uio
->icon
);
153 if (g_hash_table_lookup(already_freed
, uio
->file
) == NULL
) {
155 g_hash_table_insert(already_freed
, uio
->file
, GINT_TO_POINTER(1));
159 wer
->smileys
= g_slist_remove(wer
->smileys
, uio
);
161 theme
->list
= wer
->next
;
167 g_hash_table_destroy(already_freed
);
171 pidgin_smiley_themes_remove_non_existing()
173 static struct smiley_theme
*theme
= NULL
;
176 if (!smiley_themes
) return ;
178 for (iter
= smiley_themes
; iter
; iter
= iter
->next
) {
179 theme
= ((struct smiley_theme
*)(iter
->data
));
180 if (!g_file_test(theme
->path
, G_FILE_TEST_EXISTS
)) {
181 if (theme
== current_smiley_theme
)
182 current_smiley_theme
= ((struct smiley_theme
*)(NULL
== iter
->next
? NULL
: iter
->next
->data
));
183 pidgin_themes_destroy_smiley_theme(theme
);
187 /* Remove all elements whose data is NULL */
188 smiley_themes
= g_slist_remove_all(smiley_themes
, NULL
);
190 if (!current_smiley_theme
&& smiley_themes
) {
191 struct smiley_theme
*smile
= g_slist_last(smiley_themes
)->data
;
192 pidgin_themes_load_smiley_theme(smile
->path
, TRUE
);
196 void pidgin_themes_load_smiley_theme(const char *file
, gboolean load
)
198 FILE *f
= g_fopen(file
, "r");
201 struct smiley_theme
*theme
=NULL
;
202 struct smiley_list
*list
= NULL
;
203 GSList
*lst
= smiley_themes
;
205 gboolean new_theme
= FALSE
;
211 struct smiley_theme
*thm
= lst
->data
;
212 if (!strcmp(thm
->path
, file
)) {
221 theme
= g_new0(struct smiley_theme
, 1);
222 theme
->path
= g_strdup(file
);
223 } else if (theme
== current_smiley_theme
) {
224 /* Don't reload the theme if it is already loaded */
229 dirname
= g_path_get_dirname(file
);
232 if (!fgets(buf
, sizeof(buf
), f
)) {
236 if (buf
[0] == '#' || buf
[0] == '\0')
243 if (*i
== '[' && strchr(i
, ']') && load
) {
244 struct smiley_list
*child
= g_new0(struct smiley_list
, 1);
245 child
->sml
= g_strndup(i
+1, strchr(i
, ']') - i
- 1);
250 /* Reverse the Smiley list since it was built in reverse order for efficiency reasons */
252 list
->smileys
= g_slist_reverse(list
->smileys
);
254 } else if (!g_ascii_strncasecmp(i
, "Name=", strlen("Name="))) {
257 theme
->name
= g_strdup(i
+ strlen("Name="));
258 len
= strlen(theme
->name
);
259 theme
->name
[len
-1] = 0;
260 if(len
> 2 && theme
->name
[len
-2] == '\r')
261 theme
->name
[len
-2] = 0;
262 } else if (!g_ascii_strncasecmp(i
, "Description=", strlen("Description="))) {
264 theme
->desc
= g_strdup(i
+ strlen("Description="));
265 theme
->desc
[strlen(theme
->desc
)-1] = 0;
266 } else if (!g_ascii_strncasecmp(i
, "Icon=", strlen("Icon="))) {
268 theme
->icon
= g_build_filename(dirname
, i
+ strlen("Icon="), NULL
);
269 theme
->icon
[strlen(theme
->icon
)-1] = 0;
270 } else if (!g_ascii_strncasecmp(i
, "Author=", strlen("Author="))) {
271 g_free(theme
->author
);
272 theme
->author
= g_strdup(i
+ strlen("Author="));
273 theme
->author
[strlen(theme
->author
)-1] = 0;
274 } else if (load
&& list
) {
275 gboolean hidden
= FALSE
;
277 gboolean have_used_sfile
= FALSE
;
279 if (*i
== '!' && *(i
+ 1) == ' ') {
286 while (!isspace(*i
) && li
< sizeof(l
) - 1) {
287 if (*i
== '\\' && *(i
+1) != '\0' && *(i
+1) != '\n' && *(i
+1) != '\r')
293 sfile
= g_build_filename(dirname
, l
, NULL
);
295 GtkIMHtmlSmiley
*smiley
= g_new0(GtkIMHtmlSmiley
, 1);
297 smiley
->file
= sfile
;
298 smiley
->smile
= g_strdup(l
);
299 smiley
->hidden
= hidden
;
300 list
->smileys
= g_slist_prepend(list
->smileys
, smiley
);
301 have_used_sfile
= TRUE
;
309 if (!have_used_sfile
)
314 /* Reverse the Smiley list since it was built in reverse order for efficiency reasons */
316 list
->smileys
= g_slist_reverse(list
->smileys
);
321 if (!theme
->name
|| !theme
->desc
|| !theme
->author
) {
322 purple_debug_error("gtkthemes", "Invalid file format, not loading smiley theme from '%s'\n", file
);
324 pidgin_themes_destroy_smiley_theme(theme
);
329 smiley_themes
= g_slist_prepend(smiley_themes
, theme
);
335 if (current_smiley_theme
)
336 pidgin_themes_destroy_smiley_theme_smileys(current_smiley_theme
);
337 current_smiley_theme
= theme
;
339 for (cnv
= purple_get_conversations(); cnv
!= NULL
; cnv
= cnv
->next
) {
340 PurpleConversation
*conv
= cnv
->data
;
342 if (PIDGIN_IS_PIDGIN_CONVERSATION(conv
)) {
343 pidgin_themes_smiley_themeize(PIDGIN_CONVERSATION(conv
)->imhtml
);
344 pidgin_themes_smiley_themeize(PIDGIN_CONVERSATION(conv
)->entry
);
350 void pidgin_themes_smiley_theme_probe()
354 gchar
*path
, *test_path
;
358 pidgin_smiley_themes_remove_non_existing();
360 probedirs
[0] = g_build_filename(DATADIR
, "pixmaps", "pidgin", "emotes", NULL
);
361 probedirs
[1] = g_build_filename(purple_user_dir(), "smileys", NULL
);
363 for (l
=0; probedirs
[l
]; l
++) {
364 dir
= g_dir_open(probedirs
[l
], 0, NULL
);
366 while ((file
= g_dir_read_name(dir
))) {
367 test_path
= g_build_filename(probedirs
[l
], file
, NULL
);
368 if (g_file_test(test_path
, G_FILE_TEST_IS_DIR
)) {
369 path
= g_build_filename(probedirs
[l
], file
, "theme", NULL
);
371 /* Here we check to see that the theme has proper syntax.
372 * We set the second argument to FALSE so that it doesn't load
375 pidgin_themes_load_smiley_theme(path
, FALSE
);
382 g_mkdir(probedirs
[l
], S_IRUSR
| S_IWUSR
| S_IXUSR
);
384 g_free(probedirs
[l
]);
387 if (!current_smiley_theme
&& smiley_themes
) {
388 struct smiley_theme
*smile
= smiley_themes
->data
;
389 pidgin_themes_load_smiley_theme(smile
->path
, TRUE
);
393 GSList
*pidgin_themes_get_proto_smileys(const char *id
) {
395 struct smiley_list
*list
, *def
;
397 if ((current_smiley_theme
== NULL
) || (current_smiley_theme
->list
== NULL
))
400 def
= list
= current_smiley_theme
->list
;
405 proto
= purple_find_prpl(id
);
408 if (!strcmp(list
->sml
, "default"))
410 else if (proto
&& !strcmp(proto
->info
->name
, list
->sml
))
416 return list
? list
->smileys
: def
->smileys
;
419 void pidgin_themes_init()
422 const char *current_theme
=
423 purple_prefs_get_string(PIDGIN_PREFS_ROOT
"/smileys/theme");
425 pidgin_themes_smiley_theme_probe();
427 for (l
= smiley_themes
; l
; l
= l
->next
) {
428 struct smiley_theme
*smile
= l
->data
;
429 if (smile
->name
&& strcmp(current_theme
, smile
->name
) == 0) {
430 pidgin_themes_load_smiley_theme(smile
->path
, TRUE
);
435 /* If we still don't have a smiley theme, choose the first one */
436 if (!current_smiley_theme
&& smiley_themes
) {
437 struct smiley_theme
*smile
= smiley_themes
->data
;
438 pidgin_themes_load_smiley_theme(smile
->path
, TRUE
);