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,
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;
}
static const char *bgcolorformats = "n01234567";
#define IS_FGCOLOR_FORMAT(c) \
- ((c) != '\0' && strchr(fgcolorformats, (c)) != NULL)
+ ((c) != '\0' && strchr(fgcolorformats, c) != NULL)
#define IS_BGCOLOR_FORMAT(c) \
- ((c) != '\0' && strchr(bgcolorformats, (c)) != NULL)
+ ((c) != '\0' && strchr(bgcolorformats, c) != NULL)
/* append "variable" part in $variable, ie. not the contents of the variable */
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));
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 {
(*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;
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;
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 $<digit> .. this breaks things if next
character is digit or '-' */
char digit, *tmp;
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;
str = g_string_new(NULL);
- last_fg = last_bg = 'n';
+ last_fg = last_bg = '\0';
while (*format != '\0') {
if (*format == '$') {
/* $variable, skrip it entirely */
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,
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);
}
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) {
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;
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;
}
}
- 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;
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)
words = 0;
do {
+ ptr++;
+
words++;
ptr = strchr(ptr, ' ');
} while (ptr != NULL);
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,
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)
signal_add("setup reread", (SIGNAL_FUNC) themes_read);
command_set_options("format", "delete reset");
+ command_set_options("save", "formats");
}
void themes_deinit(void)