static int init_finished;
static char *init_errors;
+static THEME_REC *internal_theme;
-static int theme_read(THEME_REC *theme, const char *path, const char *data);
+static int theme_read(THEME_REC *theme, const char *path);
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,
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);
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,
value = parse_special((char **) format, NULL, NULL,
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));
}
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 {
{
/* since we don't know the real argument list, assume there's always
an argument in them */
- char *arglist[] = {
+ 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;
/* variable - check if it's empty */
p++;
- ret = parse_special((char **) &p,
- active_win == NULL ? NULL : active_win->active_server,
+
+ 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++;
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 != 0) {
+ 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 == 0) {
+ (*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;
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 && i_isdigit(data[len-1]) && data[len-2] == '$') {
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;
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,
themes_remove_module(module);
}
+void theme_set_default_abstract(const char *key, const char *value)
+{
+ gpointer oldkey, oldvalue;
+
+ if (g_hash_table_lookup_extended(internal_theme->abstracts, key,
+ &oldkey, &oldvalue)) {
+ /* new values override old ones */
+ g_hash_table_remove(internal_theme->abstracts, oldkey);
+ g_free(oldkey);
+ g_free(oldvalue);
+ }
+
+ g_hash_table_insert(internal_theme->abstracts,
+ g_strdup(key), g_strdup(value));
+}
+
static THEME_REC *theme_find(const char *name)
{
GSList *tmp;
oldtheme = theme;
theme = theme_create(fname, name);
theme->last_modify = statbuf.st_mtime;
- if (!theme_read(theme, theme->path, NULL)) {
+ if (!theme_read(theme, theme->path)) {
/* error reading .theme file */
theme_destroy(theme);
theme = NULL;
if (oldtheme != NULL && theme != NULL) {
theme_destroy(oldtheme);
+ if (current_theme == oldtheme)
+ current_theme = theme;
window_themes_update();
}
return theme;
}
+static void copy_abstract_hash(char *key, char *value, GHashTable *dest)
+{
+ g_hash_table_insert(dest, g_strdup(key), g_strdup(value));
+}
+
+static void theme_copy_abstracts(THEME_REC *dest, THEME_REC *src)
+{
+ g_hash_table_foreach(src->abstracts, (GHFunc) copy_abstract_hash,
+ dest->abstracts);
+}
+
typedef struct {
THEME_REC *theme;
CONFIG_REC *config;
}
}
-static int theme_read(THEME_REC *theme, const char *path, const char *data)
+static int theme_read(THEME_REC *theme, const char *path)
{
CONFIG_REC *config;
THEME_READ_REC rec;
char *str;
- config = config_open(data == NULL ? path : NULL, -1) ;
+ config = config_open(path, -1) ;
if (config == NULL) {
/* didn't exist or no access? */
str = g_strdup_printf("Error reading theme file %s: %s",
return FALSE;
}
- if (data != NULL)
- config_parse_data(config, data, "internal");
+ if (path == NULL)
+ config_parse_data(config, default_theme, "internal");
else
config_parse(config);
theme->default_color =
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) {
- /* get the default abstracts from default theme. */
- CONFIG_REC *default_config;
-
- default_config = config_open(NULL, -1);
- config_parse_data(default_config, default_theme, "internal");
- theme_read_abstracts(default_config, theme);
- config_close(default_config);
- }
+ if (path != NULL)
+ theme_copy_abstracts(theme, internal_theme);
theme_read_abstracts(config, theme);
rec.theme = theme;
ptr = line;
words = 0;
- do {
- words++;
- ptr = strchr(ptr, ' ');
- } while (ptr != NULL);
+ if (*ptr != '\0') {
+ do {
+ ptr++;
+ words++;
+ ptr = strchr(ptr, ' ');
+ } while (ptr != NULL);
+ }
if (words > 2)
return;
rec = theme_load(name);
if (rec != NULL) {
current_theme = rec;
+ signal_emit("theme changed", 1, rec);
+
if (verbose) {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
TXT_THEME_CHANGED,
change_theme(theme, TRUE);
}
-static void themes_read(void)
+void themes_reload(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");
fname = g_strdup_printf("%s/default.theme", get_irssi_dir());
current_theme = theme_create(fname, "default");
current_theme->default_color = -1;
- theme_read(current_theme, NULL, default_theme);
+ theme_read(current_theme, NULL);
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);
+ }
+}
+
+static THEME_REC *read_internal_theme(void)
+{
+ CONFIG_REC *config;
+ THEME_REC *theme;
+
+ theme = theme_create("internal", "_internal");
+
+ config = config_open(NULL, -1);
+ config_parse_data(config, default_theme, "internal");
+ theme_read_abstracts(config, theme);
+ config_close(config);
+
+ return theme;
}
void themes_init(void)
default_formats = g_hash_table_new((GHashFunc) g_str_hash,
(GCompareFunc) g_str_equal);
+ internal_theme = read_internal_theme();
init_finished = FALSE;
init_errors = NULL;
themes = NULL;
- themes_read();
+ themes_reload();
command_bind("format", NULL, (SIGNAL_FUNC) cmd_format);
command_bind("save", NULL, (SIGNAL_FUNC) cmd_save);
signal_add("complete command format", (SIGNAL_FUNC) sig_complete_format);
signal_add("irssi init finished", (SIGNAL_FUNC) sig_print_errors);
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
- signal_add("setup reread", (SIGNAL_FUNC) themes_read);
+ signal_add("setup reread", (SIGNAL_FUNC) themes_reload);
command_set_options("format", "delete reset");
command_set_options("save", "formats");
{
while (themes != NULL)
theme_destroy(themes->data);
+ theme_destroy(internal_theme);
g_hash_table_destroy(default_formats);
default_formats = NULL;
signal_remove("complete command format", (SIGNAL_FUNC) sig_complete_format);
signal_remove("irssi init finished", (SIGNAL_FUNC) sig_print_errors);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
- signal_remove("setup reread", (SIGNAL_FUNC) themes_read);
+ signal_remove("setup reread", (SIGNAL_FUNC) themes_reload);
}