X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=apps%2Firssi%2Fsrc%2Ffe-common%2Fcore%2Fthemes.c;h=437b118faeccf36a8a08eb1b6823c39164ecec22;hp=ca26f68f2b882e02c480f20ae7692203a61e606c;hb=382d15d447b7a95390decfa783836ae4fe255b3d;hpb=c46257aff172b7bff9c0fe560ae377543934ff64 diff --git a/apps/irssi/src/fe-common/core/themes.c b/apps/irssi/src/fe-common/core/themes.c index ca26f68f..437b118f 100644 --- a/apps/irssi/src/fe-common/core/themes.c +++ b/apps/irssi/src/fe-common/core/themes.c @@ -50,6 +50,7 @@ THEME_REC *theme_create(const char *path, const char *name) g_return_val_if_fail(name != NULL, NULL); rec = g_new0(THEME_REC, 1); + rec->refcount = 1; rec->path = g_strdup(path); rec->name = g_strdup(name); rec->abstracts = g_hash_table_new((GHashFunc) g_str_hash, @@ -83,12 +84,8 @@ static void theme_module_destroy(const char *key, MODULE_THEME_REC *rec) g_free(rec); } -void theme_destroy(THEME_REC *rec) +static void theme_real_destroy(THEME_REC *rec) { - themes = g_slist_remove(themes, rec); - - signal_emit("theme destroyed", 1, rec); - g_hash_table_foreach(rec->abstracts, (GHFunc) theme_abstract_destroy, NULL); g_hash_table_destroy(rec->abstracts); g_hash_table_foreach(rec->modules, (GHFunc) theme_module_destroy, NULL); @@ -102,6 +99,20 @@ void theme_destroy(THEME_REC *rec) g_free(rec); } +static void theme_unref(THEME_REC *rec) +{ + if (--rec->refcount == 0) + theme_real_destroy(rec); +} + +void theme_destroy(THEME_REC *rec) +{ + themes = g_slist_remove(themes, rec); + signal_emit("theme destroyed", 1, rec); + + theme_unref(rec); +} + static char *theme_replace_expand(THEME_REC *theme, int index, char default_fg, char default_bg, char *last_fg, char *last_bg, @@ -119,7 +130,8 @@ static char *theme_replace_expand(THEME_REC *theme, int index, abstract = theme_format_expand_data(theme, (const char **) &abstract, default_fg, default_bg, last_fg, last_bg, flags); - ret = parse_special_string(abstract, NULL, NULL, data, NULL, 0); + ret = parse_special_string(abstract, NULL, NULL, data, NULL, + PARSE_FLAG_ONLY_ARGS); g_free(abstract); return ret; } @@ -143,9 +155,11 @@ static void theme_format_append_variable(GString *str, const char **format) (*format)++; value = parse_special((char **) format, NULL, NULL, - args, &free_ret, NULL, 0); + args, &free_ret, NULL, PARSE_FLAG_ONLY_ARGS); if (free_ret) g_free(value); - (*format)++; + + if (**format != '\0') + (*format)++; /* append the variable name */ value = g_strndup(orig, (int) (*format-orig)); @@ -211,12 +225,14 @@ static void theme_format_append_next(THEME_REC *theme, GString *str, return; } - /* %{ or %} gives us { or } char */ + /* %{ or %} gives us { or } char - keep the % char + though to make sure {} isn't treated as abstract */ + g_string_append_c(str, '%'); chr = **format; } index = (flags & EXPAND_FLAG_IGNORE_REPLACES) ? -1 : - theme->replace_keys[(int) chr]; + theme->replace_keys[(int) (unsigned char) chr]; if (index == -1) g_string_append_c(str, chr); else { @@ -232,12 +248,104 @@ static void theme_format_append_next(THEME_REC *theme, GString *str, (*format)++; } +/* returns TRUE if data is empty, or the data is a $variable which is empty */ +static int data_is_empty(const char **data) +{ + /* since we don't know the real argument list, assume there's always + an argument in them */ + static char *arglist[] = { + "x", "x", "x", "x", "x", "x","x", "x", "x", "x", + NULL + }; + SERVER_REC *server; + const char *p; + char *ret; + int free_ret, empty; + + p = *data; + while (*p == ' ') p++; + + if (*p == '}') { + /* empty */ + *data = p+1; + return TRUE; + } + + if (*p != '$') { + /* not empty */ + return FALSE; + } + + /* variable - check if it's empty */ + p++; + + server = active_win == NULL ? NULL : + active_win->active_server != NULL ? + active_win->active_server : active_win->connect_server; + + ret = parse_special((char **) &p, server, + active_win == NULL ? NULL : active_win->active, + arglist, &free_ret, NULL, 0); + p++; + + while (*p == ' ') p++; + empty = *p == '}' && (ret == NULL || *ret == '\0'); + if (free_ret) g_free(ret); + + if (empty) { + /* empty */ + *data = p+1; + return TRUE; + } + + return FALSE; +} + +/* return "data" from {abstract data} string */ +char *theme_format_expand_get(THEME_REC *theme, const char **format) +{ + GString *str; + char *ret, dummy; + int braces = 1; /* we start with one brace opened */ + + str = g_string_new(NULL); + while ((**format != '\0') && (braces)) { + if (**format == '{') + braces++; + else if (**format == '}') + braces--; + else if ((braces > 1) && (**format == ' ')) { + g_string_append(str, "\\x20"); + (*format)++; + continue; + } else { + theme_format_append_next(theme, str, format, + 'n', 'n', + &dummy, &dummy, 0); + continue; + } + + if (!braces) { + (*format)++; + break; + } + + g_string_append_c(str, **format); + (*format)++; + } + + ret = str->str; + g_string_free(str, FALSE); + return ret; +} + /* expand a single {abstract ...data... } */ static char *theme_format_expand_abstract(THEME_REC *theme, const char **formatp, char default_fg, char default_bg, int flags) { + GString *str; const char *p, *format; char *abstract, *data, *ret; int len; @@ -258,17 +366,11 @@ static char *theme_format_expand_abstract(THEME_REC *theme, treated as arguments */ if (*p == ' ') { len++; - if ((flags & EXPAND_FLAG_IGNORE_EMPTY)) { - /* if the data is empty, ignore the abstract */ - p = format+len; - while (*p == ' ') p++; - if (*p == '}') { - *formatp = p+1; - g_free(abstract); - return NULL; - } + if ((flags & EXPAND_FLAG_IGNORE_EMPTY) && data_is_empty(&p)) { + *formatp = p; + g_free(abstract); + return NULL; } - } *formatp = format+len; @@ -282,12 +384,11 @@ static char *theme_format_expand_abstract(THEME_REC *theme, abstract = g_strdup(data); /* we'll need to get the data part. it may contain - more abstracts, they are automatically expanded. */ - data = theme_format_expand_data(theme, formatp, default_fg, default_bg, - NULL, NULL, flags); + more abstracts, they are _NOT_ expanded. */ + data = theme_format_expand_get(theme, formatp); len = strlen(data); - if (len > 1 && isdigit(data[len-1]) && data[len-2] == '$') { + if (len > 1 && i_isdigit(data[len-1]) && data[len-2] == '$') { /* ends with $ .. this breaks things if next character is digit or '-' */ char digit, *tmp; @@ -300,10 +401,25 @@ static char *theme_format_expand_abstract(THEME_REC *theme, g_free(tmp); } - ret = parse_special_string(abstract, NULL, NULL, data, NULL, 0); + ret = parse_special_string(abstract, NULL, NULL, data, NULL, + PARSE_FLAG_ONLY_ARGS); g_free(abstract); g_free(data); - abstract = ret; + str = g_string_new(NULL); + p = ret; + while (*p != '\0') { + if (*p == '\\') { + int chr; + p++; + chr = expand_escape(&p); + g_string_append_c(str, chr != -1 ? chr : *p); + } else + g_string_append_c(str, *p); + p++; + } + g_free(ret); + abstract = str->str; + g_string_free(str, FALSE); /* abstract may itself contain abstracts or replaces */ p = abstract; @@ -397,7 +513,7 @@ static char *theme_format_compress_colors(THEME_REC *theme, const char *format) str = g_string_new(NULL); - last_fg = last_bg = 'n'; + last_fg = last_bg = '\0'; while (*format != '\0') { if (*format == '$') { /* $variable, skrip it entirely */ @@ -413,9 +529,9 @@ static char *theme_format_compress_colors(THEME_REC *theme, const char *format) if (IS_OLD_FORMAT(*format, last_fg, last_bg)) { /* active color set again */ } else if (IS_FGCOLOR_FORMAT(*format) && - (*format != 'n' || format[2] == 'n') && format[1] == '%' && - IS_FGCOLOR_FORMAT(format[2])) { + IS_FGCOLOR_FORMAT(format[2]) && + (*format != 'n' || format[2] == 'n')) { /* two fg colors in a row. bg colors are so rare that we don't bother checking them */ @@ -494,7 +610,7 @@ static void theme_read_replaces(CONFIG_REC *config, THEME_REC *theme) if (node->key != NULL && node->value != NULL) { for (p = node->key; *p != '\0'; p++) - theme->replace_keys[(int) *p] = index; + theme->replace_keys[(int) (unsigned char) *p] = index; theme->replace_values = g_slist_append(theme->replace_values, @@ -701,11 +817,11 @@ THEME_REC *theme_load(const char *setname) theme = theme_find(name); /* check home dir */ - fname = g_strdup_printf("%s/.silc/%s.theme", g_get_home_dir(), name); + fname = g_strdup_printf("%s/%s.theme", get_irssi_dir(), name); if (stat(fname, &statbuf) != 0) { /* check global config dir */ g_free(fname); - fname = g_strdup_printf(SYSCONFDIR"/irssi/%s.theme", name); + fname = g_strdup_printf(THEMESDIR"/%s.theme", name); if (stat(fname, &statbuf) != 0) { /* theme not found */ g_free(fname); @@ -795,9 +911,13 @@ static int theme_read(THEME_REC *theme, const char *path, const char *data) } theme->default_color = - config_get_int(config, NULL, "default_color", 0); - theme->default_real_color = - config_get_int(config, NULL, "default_real_color", 7); + config_get_int(config, NULL, "default_color", -1); + theme->info_eol = config_get_bool(config, NULL, "info_eol", FALSE); + + /* FIXME: remove after 0.7.99 */ + if (theme->default_color == 0 && + config_get_int(config, NULL, "default_real_color", -1) != -1) + theme->default_color = -1; theme_read_replaces(config, theme); if (data == NULL) { @@ -952,8 +1072,13 @@ static void cmd_format(const char *data) cmd_params_free(free_arg); } +typedef struct { + CONFIG_REC *config; + int save_all; +} THEME_SAVE_REC; + static void module_save(const char *module, MODULE_THEME_REC *rec, - CONFIG_REC *config) + THEME_SAVE_REC *data) { CONFIG_NODE *fnode, *node; FORMAT_REC *formats; @@ -962,27 +1087,33 @@ static void module_save(const char *module, MODULE_THEME_REC *rec, formats = g_hash_table_lookup(default_formats, rec->name); if (formats == NULL) return; - fnode = config_node_traverse(config, "formats", TRUE); + fnode = config_node_traverse(data->config, "formats", TRUE); node = config_node_section(fnode, rec->name, NODE_TYPE_BLOCK); - for (n = 0; formats[n].def != NULL; n++) { + for (n = 1; formats[n].def != NULL; n++) { if (rec->formats[n] != NULL) { - config_node_set_str(config, node, formats[n].tag, + config_node_set_str(data->config, node, formats[n].tag, rec->formats[n]); - } + } else if (data->save_all && formats[n].tag != NULL) { + config_node_set_str(data->config, node, formats[n].tag, + formats[n].def); + } } if (node->value == NULL) { /* not modified, don't keep the empty section */ - config_node_remove(config, fnode, node); - if (fnode->value == NULL) - config_node_remove(config, config->mainnode, fnode); + config_node_remove(data->config, fnode, node); + if (fnode->value == NULL) { + config_node_remove(data->config, + data->config->mainnode, fnode); + } } } -static void theme_save(THEME_REC *theme) +static void theme_save(THEME_REC *theme, int save_all) { CONFIG_REC *config; + THEME_SAVE_REC data; char *path; int ok; @@ -1002,10 +1133,12 @@ static void theme_save(THEME_REC *theme) } } - g_hash_table_foreach(theme->modules, (GHFunc) module_save, config); + data.config = config; + data.save_all = save_all; + g_hash_table_foreach(theme->modules, (GHFunc) module_save, &data); - /* always save the theme to ~/.silc/ */ - path = g_strdup_printf("%s/.silc/%s", g_get_home_dir(), + /* always save the theme to ~/.irssi/ */ + path = g_strdup_printf("%s/%s", get_irssi_dir(), g_basename(theme->path)); ok = config_write(config, path, 0660) == 0; @@ -1017,16 +1150,27 @@ static void theme_save(THEME_REC *theme) config_close(config); } -/* save changed formats */ -static void cmd_save(void) +/* save changed formats, -format saves all */ +static void cmd_save(const char *data) { GSList *tmp; + GHashTable *optlist; + void *free_arg; + char *fname; + int saveall; + if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS, + "save", &optlist, &fname)) + return; + + saveall = g_hash_table_lookup(optlist, "formats") != NULL; for (tmp = themes; tmp != NULL; tmp = tmp->next) { THEME_REC *theme = tmp->data; - theme_save(theme); + theme_save(theme, saveall); } + + cmd_params_free(free_arg); } static void complete_format_list(THEME_SEARCH_REC *rec, const char *key, GList **list) @@ -1083,6 +1227,8 @@ static void sig_complete_format(GList **list, WINDOW_REC *window, words = 0; do { + ptr++; + words++; ptr = strchr(ptr, ' '); } while (ptr != NULL); @@ -1101,10 +1247,12 @@ static void change_theme(const char *name, int verbose) rec = theme_load(name); if (rec != NULL) { current_theme = rec; + signal_emit("theme changed", 1, rec); + if (verbose) { - printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, - TXT_THEME_CHANGED, - rec->name, rec->path); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + TXT_THEME_CHANGED, + rec->name, rec->path); } } else if (verbose) { printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, @@ -1115,33 +1263,50 @@ static void change_theme(const char *name, int verbose) static void read_settings(void) { const char *theme; + int len; theme = settings_get_str("theme"); - if (strcmp(current_theme->name, theme) != 0) + len = strlen(current_theme->name); + if (strcmp(current_theme->name, theme) != 0 && + (strncmp(current_theme->name, theme, len) != 0 || + strcmp(theme+len, ".theme") != 0)) change_theme(theme, TRUE); } static void themes_read(void) { + GSList *refs; char *fname; - while (themes != NULL) - theme_destroy(themes->data); + /* increase every theme's refcount, and destroy them. this way if + we want to use the theme before it's reloaded we don't crash. */ + refs = NULL; + while (themes != NULL) { + THEME_REC *theme = themes->data; + + refs = g_slist_prepend(refs, theme); + + theme->refcount++; + theme_destroy(theme); + } /* first there's default theme.. */ current_theme = theme_load("default"); if (current_theme == NULL) { - fname = g_strdup_printf("%s/.silc/default.theme", - g_get_home_dir()); + fname = g_strdup_printf("%s/default.theme", get_irssi_dir()); current_theme = theme_create(fname, "default"); - current_theme->default_color = 0; - current_theme->default_real_color = 7; + current_theme->default_color = -1; theme_read(current_theme, NULL, default_theme); g_free(fname); } window_themes_update(); - change_theme(settings_get_str("theme"), FALSE); + change_theme(settings_get_str("theme"), FALSE); + + while (refs != NULL) { + theme_unref(refs->data); + refs = g_slist_remove(refs, refs->data); + } } void themes_init(void) @@ -1165,6 +1330,7 @@ void themes_init(void) signal_add("setup reread", (SIGNAL_FUNC) themes_read); command_set_options("format", "delete reset"); + command_set_options("save", "formats"); } void themes_deinit(void)