Added SILC Thread Queue API
[crypto.git] / apps / irssi / src / fe-common / core / themes.c
index c80c9e2d1190aaadc5b97cc46bc71bd48759b6e0..b4407bda9e6b521c3a1e17453793341eecbaae5a 100644 (file)
@@ -39,8 +39,9 @@ GHashTable *default_formats;
 
 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)
 {
@@ -50,6 +51,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 +85,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 +100,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,
@@ -146,7 +158,9 @@ static void theme_format_append_variable(GString *str, const char **format)
        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));
@@ -219,7 +233,7 @@ static void theme_format_append_next(THEME_REC *theme, GString *str,
        }
 
        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 {
@@ -240,10 +254,11 @@ 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 */
-       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;
@@ -264,8 +279,12 @@ static int data_is_empty(const char **data)
 
        /* 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++;
@@ -283,12 +302,51 @@ static int data_is_empty(const char **data)
         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;
@@ -327,9 +385,8 @@ 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 && i_isdigit(data[len-1]) && data[len-2] == '$') {
@@ -349,7 +406,21 @@ static char *theme_format_expand_abstract(THEME_REC *theme,
                                   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;
@@ -540,7 +611,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,
@@ -705,6 +776,22 @@ void theme_unregister_module(const char *module)
        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;
@@ -770,7 +857,7 @@ THEME_REC *theme_load(const char *setname)
         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;
@@ -778,6 +865,8 @@ THEME_REC *theme_load(const char *setname)
 
        if (oldtheme != NULL && theme != NULL) {
                theme_destroy(oldtheme);
+               if (current_theme == oldtheme)
+                       current_theme = theme;
                window_themes_update();
        }
 
@@ -786,6 +875,17 @@ THEME_REC *theme_load(const char *setname)
        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;
@@ -812,13 +912,13 @@ static void read_error(const char *str)
        }
 }
 
-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",
@@ -828,8 +928,8 @@ static int theme_read(THEME_REC *theme, const char *path, const char *data)
                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);
 
@@ -842,21 +942,16 @@ static int theme_read(THEME_REC *theme, const char *path, const char *data)
 
        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;
@@ -1154,10 +1249,13 @@ static void sig_complete_format(GList **list, WINDOW_REC *window,
         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;
@@ -1173,6 +1271,8 @@ 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(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
                                    TXT_THEME_CHANGED,
@@ -1197,12 +1297,22 @@ static void read_settings(void)
                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");
@@ -1210,12 +1320,32 @@ static void themes_read(void)
                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)
@@ -1224,19 +1354,20 @@ 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");
@@ -1246,6 +1377,7 @@ void themes_deinit(void)
 {
        while (themes != NULL)
                theme_destroy(themes->data);
+       theme_destroy(internal_theme);
 
        g_hash_table_destroy(default_formats);
        default_formats = NULL;
@@ -1255,5 +1387,5 @@ void themes_deinit(void)
        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);
 }