4 Copyright (C) 1999-2000 Timo Sirainen
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "module-formats.h"
27 #include "special-vars.h"
28 #include "lib-config/iconfig.h"
32 #include "printtext.h"
34 #include "default-theme.h"
37 THEME_REC *current_theme;
38 GHashTable *default_formats;
40 static int init_finished;
41 static char *init_errors;
43 static int theme_read(THEME_REC *theme, const char *path, const char *data);
45 THEME_REC *theme_create(const char *path, const char *name)
49 g_return_val_if_fail(path != NULL, NULL);
50 g_return_val_if_fail(name != NULL, NULL);
52 rec = g_new0(THEME_REC, 1);
53 rec->path = g_strdup(path);
54 rec->name = g_strdup(name);
55 rec->abstracts = g_hash_table_new((GHashFunc) g_str_hash,
56 (GCompareFunc) g_str_equal);
57 rec->modules = g_hash_table_new((GHashFunc) g_istr_hash,
58 (GCompareFunc) g_istr_equal);
59 themes = g_slist_append(themes, rec);
60 signal_emit("theme created", 1, rec);
65 static void theme_abstract_destroy(char *key, char *value)
71 static void theme_module_destroy(const char *key, MODULE_THEME_REC *rec)
75 for (n = 0; n < rec->count; n++) {
76 g_free_not_null(rec->formats[n]);
77 g_free_not_null(rec->expanded_formats[n]);
80 g_free(rec->expanded_formats);
86 void theme_destroy(THEME_REC *rec)
88 themes = g_slist_remove(themes, rec);
90 signal_emit("theme destroyed", 1, rec);
92 g_hash_table_foreach(rec->abstracts, (GHFunc) theme_abstract_destroy, NULL);
93 g_hash_table_destroy(rec->abstracts);
94 g_hash_table_foreach(rec->modules, (GHFunc) theme_module_destroy, NULL);
95 g_hash_table_destroy(rec->modules);
97 g_slist_foreach(rec->replace_values, (GFunc) g_free, NULL);
98 g_slist_free(rec->replace_values);
105 static char *theme_replace_expand(THEME_REC *theme, int index,
106 char default_fg, char default_bg,
107 char *last_fg, char *last_bg,
111 char *ret, *abstract, data[2];
113 rec = g_slist_nth(theme->replace_values, index);
114 g_return_val_if_fail(rec != NULL, NULL);
116 data[0] = chr; data[1] = '\0';
118 abstract = rec->data;
119 abstract = theme_format_expand_data(theme, (const char **) &abstract,
120 default_fg, default_bg,
121 last_fg, last_bg, flags);
122 ret = parse_special_string(abstract, NULL, NULL, data, NULL, 0);
127 static const char *fgcolorformats = "nkrgybmpcwKRGYBMPCW";
128 static const char *bgcolorformats = "n01234567";
130 #define IS_FGCOLOR_FORMAT(c) \
131 ((c) != '\0' && strchr(fgcolorformats, c) != NULL)
132 #define IS_BGCOLOR_FORMAT(c) \
133 ((c) != '\0' && strchr(bgcolorformats, c) != NULL)
135 /* append "variable" part in $variable, ie. not the contents of the variable */
136 static void theme_format_append_variable(GString *str, const char **format)
139 char *value, *args[1] = { NULL };
145 value = parse_special((char **) format, NULL, NULL,
146 args, &free_ret, NULL, 0);
147 if (free_ret) g_free(value);
150 /* append the variable name */
151 value = g_strndup(orig, (int) (*format-orig));
152 g_string_append(str, value);
156 /* append next "item", either a character, $variable or %format */
157 static void theme_format_append_next(THEME_REC *theme, GString *str,
159 char default_fg, char default_bg,
160 char *last_fg, char *last_bg,
167 if ((chr == '$' || chr == '%') &&
168 (*format)[1] == '\0') {
169 /* last char, always append */
170 g_string_append_c(str, chr);
176 /* $variable .. we'll always need to skip this, since it
177 may contain characters that are in replace chars. */
178 theme_format_append_variable(str, format);
182 if (**format == '%') {
185 if (**format != '{' && **format != '}') {
187 if (**format == 'n') {
188 /* %n = change to default color */
189 g_string_append(str, "%n");
191 if (default_bg != 'n') {
192 g_string_append_c(str, '%');
193 g_string_append_c(str, default_bg);
195 if (default_fg != 'n') {
196 g_string_append_c(str, '%');
197 g_string_append_c(str, default_fg);
200 *last_fg = default_fg;
201 *last_bg = default_bg;
203 if (IS_FGCOLOR_FORMAT(chr))
205 if (IS_BGCOLOR_FORMAT(chr))
207 g_string_append_c(str, '%');
208 g_string_append_c(str, chr);
214 /* %{ or %} gives us { or } char */
218 index = (flags & EXPAND_FLAG_IGNORE_REPLACES) ? -1 :
219 theme->replace_keys[(int) chr];
221 g_string_append_c(str, chr);
225 value = theme_replace_expand(theme, index,
226 default_fg, default_bg,
227 last_fg, last_bg, chr, flags);
228 g_string_append(str, value);
235 /* expand a single {abstract ...data... } */
236 static char *theme_format_expand_abstract(THEME_REC *theme,
237 const char **formatp,
238 char default_fg, char default_bg,
241 const char *p, *format;
242 char *abstract, *data, *ret;
247 /* get abstract name first */
249 while (*p != '\0' && *p != ' ' &&
250 *p != '{' && *p != '}') p++;
251 if (*p == '\0' || p == format)
252 return NULL; /* error */
254 len = (int) (p-format);
255 abstract = g_strndup(format, len);
257 /* skip the following space, if there's any more spaces they're
258 treated as arguments */
261 if ((flags & EXPAND_FLAG_IGNORE_EMPTY)) {
262 /* if the data is empty, ignore the abstract */
264 while (*p == ' ') p++;
273 *formatp = format+len;
275 /* get the abstract data */
276 data = g_hash_table_lookup(theme->abstracts, abstract);
279 /* unknown abstract, just display the data */
282 abstract = g_strdup(data);
284 /* we'll need to get the data part. it may contain
285 more abstracts, they are automatically expanded. */
286 data = theme_format_expand_data(theme, formatp, default_fg, default_bg,
290 if (len > 1 && isdigit(data[len-1]) && data[len-2] == '$') {
291 /* ends with $<digit> .. this breaks things if next
292 character is digit or '-' */
299 data = g_strdup_printf("%s{%c}", tmp, digit);
303 ret = parse_special_string(abstract, NULL, NULL, data, NULL, 0);
308 /* abstract may itself contain abstracts or replaces */
310 ret = theme_format_expand_data(theme, &p, default_fg, default_bg,
311 &default_fg, &default_bg,
312 flags | EXPAND_FLAG_LASTCOLOR_ARG);
317 /* expand the data part in {abstract data} */
318 char *theme_format_expand_data(THEME_REC *theme, const char **format,
319 char default_fg, char default_bg,
320 char *save_last_fg, char *save_last_bg,
324 char *ret, *abstract;
325 char last_fg, last_bg;
328 last_fg = default_fg;
329 last_bg = default_bg;
330 recurse_flags = flags & EXPAND_FLAG_RECURSIVE_MASK;
332 str = g_string_new(NULL);
333 while (**format != '\0') {
334 if ((flags & EXPAND_FLAG_ROOT) == 0 && **format == '}') {
335 /* ignore } if we're expanding original string */
340 if (**format != '{') {
341 if ((flags & EXPAND_FLAG_LASTCOLOR_ARG) &&
342 **format == '$' && (*format)[1] == '0') {
343 /* save the color before $0 ..
344 this is for the %n replacing */
345 if (save_last_fg != NULL) {
346 *save_last_fg = last_fg;
349 if (save_last_bg != NULL) {
350 *save_last_bg = last_bg;
355 theme_format_append_next(theme, str, format,
356 default_fg, default_bg,
363 if (**format == '\0' || **format == '}')
366 /* get a single {...} */
367 abstract = theme_format_expand_abstract(theme, format,
370 if (abstract != NULL) {
371 g_string_append(str, abstract);
376 if ((flags & EXPAND_FLAG_LASTCOLOR_ARG) == 0) {
377 /* save the last color */
378 if (save_last_fg != NULL)
379 *save_last_fg = last_fg;
380 if (save_last_bg != NULL)
381 *save_last_bg = last_bg;
385 g_string_free(str, FALSE);
389 #define IS_OLD_FORMAT(code, last_fg, last_bg) \
390 (((code) == 'n' && (last_fg) == 'n' && (last_bg) == 'n') || \
391 ((code) != 'n' && ((code) == (last_fg) || (code) == (last_bg))))
393 static char *theme_format_compress_colors(THEME_REC *theme, const char *format)
396 char *ret, last_fg, last_bg;
398 str = g_string_new(NULL);
400 last_fg = last_bg = 'n';
401 while (*format != '\0') {
402 if (*format == '$') {
403 /* $variable, skrip it entirely */
404 theme_format_append_variable(str, &format);
405 last_fg = last_bg = '\0';
406 } else if (*format != '%') {
407 /* a normal character */
408 g_string_append_c(str, *format);
413 if (IS_OLD_FORMAT(*format, last_fg, last_bg)) {
414 /* active color set again */
415 } else if (IS_FGCOLOR_FORMAT(*format) &&
416 (*format != 'n' || format[2] == 'n') &&
418 IS_FGCOLOR_FORMAT(format[2])) {
419 /* two fg colors in a row. bg colors are
420 so rare that we don't bother checking
423 /* some format, add it */
424 g_string_append_c(str, '%');
425 g_string_append_c(str, *format);
427 if (IS_FGCOLOR_FORMAT(*format))
429 if (IS_BGCOLOR_FORMAT(*format))
437 g_string_free(str, FALSE);
441 char *theme_format_expand(THEME_REC *theme, const char *format)
445 g_return_val_if_fail(theme != NULL, NULL);
446 g_return_val_if_fail(format != NULL, NULL);
448 data = theme_format_expand_data(theme, &format, 'n', 'n', NULL, NULL,
450 ret = theme_format_compress_colors(theme, data);
455 static MODULE_THEME_REC *theme_module_create(THEME_REC *theme, const char *module)
457 MODULE_THEME_REC *rec;
460 rec = g_hash_table_lookup(theme->modules, module);
461 if (rec != NULL) return rec;
463 formats = g_hash_table_lookup(default_formats, module);
464 g_return_val_if_fail(formats != NULL, NULL);
466 rec = g_new0(MODULE_THEME_REC, 1);
467 rec->name = g_strdup(module);
469 for (rec->count = 0; formats[rec->count].def != NULL; rec->count++) ;
470 rec->formats = g_new0(char *, rec->count);
471 rec->expanded_formats = g_new0(char *, rec->count);
473 g_hash_table_insert(theme->modules, rec->name, rec);
477 static void theme_read_replaces(CONFIG_REC *config, THEME_REC *theme)
484 /* reset replace keys */
485 for (index = 0; index < 256; index++)
486 theme->replace_keys[index] = -1;
489 node = config_node_traverse(config, "replaces", FALSE);
490 if (node == NULL || node->type != NODE_TYPE_BLOCK) return;
492 for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
495 if (node->key != NULL && node->value != NULL) {
496 for (p = node->key; *p != '\0'; p++)
497 theme->replace_keys[(int) *p] = index;
499 theme->replace_values =
500 g_slist_append(theme->replace_values,
501 g_strdup(node->value));
507 static void theme_read_abstracts(CONFIG_REC *config, THEME_REC *theme)
511 gpointer oldkey, oldvalue;
513 node = config_node_traverse(config, "abstracts", FALSE);
514 if (node == NULL || node->type != NODE_TYPE_BLOCK) return;
516 for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
519 if (node->key == NULL || node->value == NULL)
522 if (g_hash_table_lookup_extended(theme->abstracts, node->key,
523 &oldkey, &oldvalue)) {
524 /* new values override old ones */
525 g_hash_table_remove(theme->abstracts, oldkey);
530 g_hash_table_insert(theme->abstracts, g_strdup(node->key),
531 g_strdup(node->value));
535 static void theme_set_format(THEME_REC *theme, MODULE_THEME_REC *rec,
537 const char *key, const char *value)
541 num = format_find_tag(module, key);
543 rec->formats[num] = g_strdup(value);
544 rec->expanded_formats[num] = theme_format_expand(theme, value);
548 static void theme_read_formats(THEME_REC *theme, const char *module,
549 CONFIG_REC *config, MODULE_THEME_REC *rec)
554 node = config_node_traverse(config, "formats", FALSE);
555 if (node == NULL) return;
556 node = config_node_section(node, module, -1);
557 if (node == NULL) return;
559 for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
562 if (node->key != NULL && node->value != NULL) {
563 theme_set_format(theme, rec, module,
564 node->key, node->value);
569 static void theme_init_module(THEME_REC *theme, const char *module,
572 MODULE_THEME_REC *rec;
576 formats = g_hash_table_lookup(default_formats, module);
577 g_return_if_fail(formats != NULL);
579 rec = theme_module_create(theme, module);
582 theme_read_formats(theme, module, config, rec);
584 /* expand the remaining formats */
585 for (n = 0; n < rec->count; n++) {
586 if (rec->expanded_formats[n] == NULL) {
587 rec->expanded_formats[n] =
588 theme_format_expand(theme, formats[n].def);
593 static void sig_print_errors(void)
595 init_finished = TRUE;
597 if (init_errors != NULL) {
598 signal_emit("gui dialog", 2, "error", init_errors);
603 static void theme_read_module(THEME_REC *theme, const char *module)
607 config = config_open(theme->path, -1);
609 config_parse(config);
611 theme_init_module(theme, module, config);
613 if (config != NULL) config_close(config);
616 static void themes_read_module(const char *module)
618 g_slist_foreach(themes, (GFunc) theme_read_module, (void *) module);
621 static void theme_remove_module(THEME_REC *theme, const char *module)
623 MODULE_THEME_REC *rec;
625 rec = g_hash_table_lookup(theme->modules, module);
626 if (rec == NULL) return;
628 g_hash_table_remove(theme->modules, module);
629 theme_module_destroy(module, rec);
632 static void themes_remove_module(const char *module)
634 g_slist_foreach(themes, (GFunc) theme_remove_module, (void *) module);
637 void theme_register_module(const char *module, FORMAT_REC *formats)
639 if (g_hash_table_lookup(default_formats, module) != NULL)
642 g_hash_table_insert(default_formats, g_strdup(module), formats);
643 themes_read_module(module);
646 void theme_unregister_module(const char *module)
650 if (default_formats == NULL)
651 return; /* already uninitialized */
653 if (!g_hash_table_lookup_extended(default_formats, module, &key, &value))
656 g_hash_table_remove(default_formats, key);
659 themes_remove_module(module);
662 static THEME_REC *theme_find(const char *name)
666 for (tmp = themes; tmp != NULL; tmp = tmp->next) {
667 THEME_REC *rec = tmp->data;
669 if (g_strcasecmp(rec->name, name) == 0)
676 static void window_themes_update(void)
680 for (tmp = windows; tmp != NULL; tmp = tmp->next) {
681 WINDOW_REC *rec = tmp->data;
683 if (rec->theme_name != NULL)
684 rec->theme = theme_load(rec->theme_name);
688 THEME_REC *theme_load(const char *setname)
690 THEME_REC *theme, *oldtheme;
692 char *fname, *name, *p;
694 name = g_strdup(setname);
695 p = strrchr(name, '.');
696 if (p != NULL && strcmp(p, ".theme") == 0) {
697 /* remove the trailing .theme */
701 theme = theme_find(name);
704 fname = g_strdup_printf("%s/.silc/%s.theme", g_get_home_dir(), name);
705 if (stat(fname, &statbuf) != 0) {
706 /* check global config dir */
708 fname = g_strdup_printf(SYSCONFDIR"/irssi/%s.theme", name);
709 if (stat(fname, &statbuf) != 0) {
710 /* theme not found */
713 return theme; /* use the one in memory if possible */
717 if (theme != NULL && theme->last_modify == statbuf.st_mtime) {
718 /* theme not modified, use the one already in memory */
725 theme = theme_create(fname, name);
726 theme->last_modify = statbuf.st_mtime;
727 if (!theme_read(theme, theme->path, NULL)) {
728 /* error reading .theme file */
729 theme_destroy(theme);
733 if (oldtheme != NULL && theme != NULL) {
734 theme_destroy(oldtheme);
735 window_themes_update();
748 static void theme_read_modules(const char *module, void *value,
751 theme_init_module(rec->theme, module, rec->config);
754 static void read_error(const char *str)
759 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
760 else if (init_errors == NULL)
761 init_errors = g_strdup(str);
764 init_errors = g_strconcat(init_errors, "\n", str, NULL);
769 static int theme_read(THEME_REC *theme, const char *path, const char *data)
775 config = config_open(data == NULL ? path : NULL, -1) ;
776 if (config == NULL) {
777 /* didn't exist or no access? */
778 str = g_strdup_printf("Error reading theme file %s: %s",
779 path, g_strerror(errno));
786 config_parse_data(config, data, "internal");
788 config_parse(config);
790 if (config_last_error(config) != NULL) {
791 str = g_strdup_printf("Ignored errors in theme %s:\n%s",
792 theme->name, config_last_error(config));
797 theme->default_color =
798 config_get_int(config, NULL, "default_color", 0);
799 theme->default_real_color =
800 config_get_int(config, NULL, "default_real_color", 7);
801 theme_read_replaces(config, theme);
804 /* get the default abstracts from default theme. */
805 CONFIG_REC *default_config;
807 default_config = config_open(NULL, -1);
808 config_parse_data(default_config, default_theme, "internal");
809 theme_read_abstracts(default_config, theme);
810 config_close(default_config);
812 theme_read_abstracts(config, theme);
816 g_hash_table_foreach(default_formats,
817 (GHFunc) theme_read_modules, &rec);
818 config_close(config);
828 static int theme_search_equal(THEME_SEARCH_REC *r1, THEME_SEARCH_REC *r2)
830 return g_strcasecmp(r1->short_name, r2->short_name);
833 static void theme_get_modules(char *module, FORMAT_REC *formats, GSList **list)
835 THEME_SEARCH_REC *rec;
837 rec = g_new(THEME_SEARCH_REC, 1);
839 rec->short_name = strrchr(module, '/');
840 if (rec->short_name != NULL)
841 rec->short_name++; else rec->short_name = module;
842 *list = g_slist_insert_sorted(*list, rec, (GCompareFunc) theme_search_equal);
845 static GSList *get_sorted_modules(void)
850 g_hash_table_foreach(default_formats, (GHFunc) theme_get_modules, &list);
854 static THEME_SEARCH_REC *theme_search(GSList *list, const char *module)
856 THEME_SEARCH_REC *rec;
858 while (list != NULL) {
861 if (g_strcasecmp(rec->short_name, module) == 0)
869 static void theme_show(THEME_SEARCH_REC *rec, const char *key, const char *value, int reset)
871 MODULE_THEME_REC *theme;
873 const char *text, *last_title;
876 formats = g_hash_table_lookup(default_formats, rec->name);
877 theme = g_hash_table_lookup(current_theme->modules, rec->name);
879 last_title = NULL; first = TRUE;
880 for (n = 1; formats[n].def != NULL; n++) {
881 text = theme != NULL && theme->formats[n] != NULL ?
882 theme->formats[n] : formats[n].def;
884 if (formats[n].tag == NULL)
886 else if ((value != NULL && key != NULL && g_strcasecmp(formats[n].tag, key) == 0) ||
887 (value == NULL && (key == NULL || stristr(formats[n].tag, key) != NULL))) {
889 printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_FORMAT_TITLE, rec->short_name, formats[0].def);
892 if (last_title != NULL)
893 printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_FORMAT_SUBTITLE, last_title);
894 if (reset || value != NULL) {
895 theme = theme_module_create(current_theme, rec->name);
896 g_free_not_null(theme->formats[n]);
897 g_free_not_null(theme->expanded_formats[n]);
899 text = reset ? formats[n].def : value;
900 theme->formats[n] = reset ? NULL : g_strdup(value);
901 theme->expanded_formats[n] = theme_format_expand(current_theme, text);
903 printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_FORMAT_ITEM, formats[n].tag, text);
909 /* SYNTAX: FORMAT [-delete | -reset] [<module>] [<key> [<value>]] */
910 static void cmd_format(const char *data)
913 GSList *tmp, *modules;
914 char *module, *key, *value;
918 if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST | PARAM_FLAG_OPTIONS,
919 "format", &optlist, &module, &key, &value))
922 modules = get_sorted_modules();
925 else if (theme_search(modules, module) == NULL) {
926 /* first argument isn't module.. */
927 cmd_params_free(free_arg);
928 if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_OPTIONS,
929 "format", &optlist, &key, &value))
935 if (*key == '\0') key = NULL;
936 if (g_hash_table_lookup(optlist, "reset"))
938 else if (g_hash_table_lookup(optlist, "delete"))
940 else if (*value == '\0')
943 for (tmp = modules; tmp != NULL; tmp = tmp->next) {
944 THEME_SEARCH_REC *rec = tmp->data;
946 if (module == NULL || g_strcasecmp(rec->short_name, module) == 0)
947 theme_show(rec, key, value, reset);
949 g_slist_foreach(modules, (GFunc) g_free, NULL);
950 g_slist_free(modules);
952 cmd_params_free(free_arg);
955 static void module_save(const char *module, MODULE_THEME_REC *rec,
958 CONFIG_NODE *fnode, *node;
962 formats = g_hash_table_lookup(default_formats, rec->name);
963 if (formats == NULL) return;
965 fnode = config_node_traverse(config, "formats", TRUE);
967 node = config_node_section(fnode, rec->name, NODE_TYPE_BLOCK);
968 for (n = 0; formats[n].def != NULL; n++) {
969 if (rec->formats[n] != NULL) {
970 config_node_set_str(config, node, formats[n].tag,
975 if (node->value == NULL) {
976 /* not modified, don't keep the empty section */
977 config_node_remove(config, fnode, node);
978 if (fnode->value == NULL)
979 config_node_remove(config, config->mainnode, fnode);
983 static void theme_save(THEME_REC *theme)
989 config = config_open(theme->path, -1);
991 config_parse(config);
993 if (g_strcasecmp(theme->name, "default") == 0) {
994 config = config_open(NULL, -1);
995 config_parse_data(config, default_theme, "internal");
996 config_change_file_name(config, theme->path, 0660);
998 config = config_open(theme->path, 0660);
1001 config_parse(config);
1005 g_hash_table_foreach(theme->modules, (GHFunc) module_save, config);
1007 /* always save the theme to ~/.silc/ */
1008 path = g_strdup_printf("%s/.silc/%s", g_get_home_dir(),
1009 g_basename(theme->path));
1010 ok = config_write(config, path, 0660) == 0;
1012 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
1013 ok ? TXT_THEME_SAVED : TXT_THEME_SAVE_FAILED,
1014 path, config_last_error(config));
1017 config_close(config);
1020 /* save changed formats */
1021 static void cmd_save(void)
1025 for (tmp = themes; tmp != NULL; tmp = tmp->next) {
1026 THEME_REC *theme = tmp->data;
1032 static void complete_format_list(THEME_SEARCH_REC *rec, const char *key, GList **list)
1034 FORMAT_REC *formats;
1037 formats = g_hash_table_lookup(default_formats, rec->name);
1040 for (n = 1; formats[n].def != NULL; n++) {
1041 const char *item = formats[n].tag;
1043 if (item != NULL && g_strncasecmp(item, key, len) == 0)
1044 *list = g_list_append(*list, g_strdup(item));
1048 static GList *completion_get_formats(const char *module, const char *key)
1050 GSList *modules, *tmp;
1053 g_return_val_if_fail(key != NULL, NULL);
1057 modules = get_sorted_modules();
1058 if (*module == '\0' || theme_search(modules, module) != NULL) {
1059 for (tmp = modules; tmp != NULL; tmp = tmp->next) {
1060 THEME_SEARCH_REC *rec = tmp->data;
1062 if (*module == '\0' || g_strcasecmp(rec->short_name, module) == 0)
1063 complete_format_list(rec, key, &list);
1066 g_slist_foreach(modules, (GFunc) g_free, NULL);
1067 g_slist_free(modules);
1072 static void sig_complete_format(GList **list, WINDOW_REC *window,
1073 const char *word, const char *line, int *want_space)
1078 g_return_if_fail(list != NULL);
1079 g_return_if_fail(word != NULL);
1080 g_return_if_fail(line != NULL);
1087 ptr = strchr(ptr, ' ');
1088 } while (ptr != NULL);
1093 *list = completion_get_formats(line, word);
1094 if (*list != NULL) signal_stop();
1097 static void change_theme(const char *name, int verbose)
1101 rec = theme_load(name);
1103 current_theme = rec;
1105 printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
1107 rec->name, rec->path);
1109 } else if (verbose) {
1110 printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
1111 TXT_THEME_NOT_FOUND, name);
1115 static void read_settings(void)
1119 theme = settings_get_str("theme");
1120 if (strcmp(current_theme->name, theme) != 0)
1121 change_theme(theme, TRUE);
1124 static void themes_read(void)
1128 while (themes != NULL)
1129 theme_destroy(themes->data);
1131 /* first there's default theme.. */
1132 current_theme = theme_load("default");
1133 if (current_theme == NULL) {
1134 fname = g_strdup_printf("%s/.silc/default.theme",
1136 current_theme = theme_create(fname, "default");
1137 current_theme->default_color = 0;
1138 current_theme->default_real_color = 7;
1139 theme_read(current_theme, NULL, default_theme);
1143 window_themes_update();
1144 change_theme(settings_get_str("theme"), FALSE);
1147 void themes_init(void)
1149 settings_add_str("lookandfeel", "theme", "default");
1151 default_formats = g_hash_table_new((GHashFunc) g_str_hash,
1152 (GCompareFunc) g_str_equal);
1154 init_finished = FALSE;
1160 command_bind("format", NULL, (SIGNAL_FUNC) cmd_format);
1161 command_bind("save", NULL, (SIGNAL_FUNC) cmd_save);
1162 signal_add("complete command format", (SIGNAL_FUNC) sig_complete_format);
1163 signal_add("irssi init finished", (SIGNAL_FUNC) sig_print_errors);
1164 signal_add("setup changed", (SIGNAL_FUNC) read_settings);
1165 signal_add("setup reread", (SIGNAL_FUNC) themes_read);
1167 command_set_options("format", "delete reset");
1170 void themes_deinit(void)
1172 while (themes != NULL)
1173 theme_destroy(themes->data);
1175 g_hash_table_destroy(default_formats);
1176 default_formats = NULL;
1178 command_unbind("format", (SIGNAL_FUNC) cmd_format);
1179 command_unbind("save", (SIGNAL_FUNC) cmd_save);
1180 signal_remove("complete command format", (SIGNAL_FUNC) sig_complete_format);
1181 signal_remove("irssi init finished", (SIGNAL_FUNC) sig_print_errors);
1182 signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
1183 signal_remove("setup reread", (SIGNAL_FUNC) themes_read);