X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/33288048f694a84b9c40653f0b72b4ce44a94ab1..a3602333d0e12150d84396b1e5a1841ea3689e96:/disobedience/settings.c diff --git a/disobedience/settings.c b/disobedience/settings.c index a5ea53f..8355712 100644 --- a/disobedience/settings.c +++ b/disobedience/settings.c @@ -1,6 +1,6 @@ /* * This file is part of Disobedience - * Copyright (C) 2007 Richard Kettlewell + * Copyright (C) 2007, 2008 Richard Kettlewell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,91 +37,6 @@ /** @brief HTML displayer */ const char *browser = BROWSER; -void save_settings(void) { - char *dir, *path, *tmp; - FILE *fp = 0; - - byte_xasprintf(&dir, "%s/.disorder", getenv("HOME")); - byte_xasprintf(&path, "%s/disobedience", dir); - byte_xasprintf(&tmp, "%s.tmp", path); - mkdir(dir, 02700); /* make sure directory exists */ - if(!(fp = fopen(tmp, "w"))) { - fpopup_msg(GTK_MESSAGE_ERROR, "error opening %s: %s", - tmp, strerror(errno)); - goto done; - } - /* TODO */ - if(fclose(fp) < 0) { - fpopup_msg(GTK_MESSAGE_ERROR, "error writing to %s: %s", - tmp, strerror(errno)); - fp = 0; - goto done; - } - fp = 0; - if(rename(tmp, path) < 0) - fpopup_msg(GTK_MESSAGE_ERROR, "error renaming %s to %s: %s", - tmp, path, strerror(errno)); -done: - if(fp) - fclose(fp); -} - -static inline unsigned clamp(unsigned n) { - return n > 0xFFFF ? 0xFFFF : n; -} - -void load_settings(void) { - char *path, *line; - FILE *fp; - char **vec; - int nvec; - - byte_xasprintf(&path, "%s/.disorder/disobedience", getenv("HOME")); - if(!(fp = fopen(path, "r"))) { - if(errno != ENOENT) - fpopup_msg(GTK_MESSAGE_ERROR, "error opening %s: %s", - path, strerror(errno)); - } else { - while(!inputline(path, fp, &line, '\n')) { - if(!(vec = split(line, &nvec, SPLIT_COMMENTS|SPLIT_QUOTES, 0, 0)) - || !nvec) - continue; - if(!strcmp(vec[0], "color")) { - if(nvec != 5) { - error(0, "%s: malformed '%s' command", path, vec[0]); - continue; - } - /* TODO */ - } else - /* mention errors but otherwise ignore them */ - error(0, "%s: unknown command '%s'", path, vec[0]); - } - if(ferror(fp)) { - fpopup_msg(GTK_MESSAGE_ERROR, "error reading %s: %s", - path, strerror(errno)); - fclose(fp); - } - } -} - -/** @brief Callback used by set_tool_colors() */ -static void set_tool_colors_callback(GtkWidget *w, - gpointer attribute((unused)) data) { - set_tool_colors(w); -} - -/** @brief Recursively set tool widget colors */ -void set_tool_colors(GtkWidget *w) { - GtkWidget *child; - - gtk_widget_set_style(w, tool_style); - if(GTK_IS_CONTAINER(w)) - gtk_container_foreach(GTK_CONTAINER(w), set_tool_colors_callback, 0); - if(GTK_IS_MENU_ITEM(w) - && (child = gtk_menu_item_get_submenu(GTK_MENU_ITEM(w)))) - set_tool_colors(child); -} - /** @brief Default style for layouts */ GtkStyle *layout_style; @@ -146,6 +61,45 @@ GtkStyle *search_style; /** @brief Style for drag targets */ GtkStyle *drag_style; +/** @brief Table of styles */ +static const struct { + const char *name; + GtkStyle **style; +} styles[] = { + { "layout", &layout_style }, + { "title", &title_style }, + { "even", &even_style }, + { "odd", &odd_style }, + { "active", &active_style }, + { "tool", &tool_style }, + { "search", &search_style }, + { "drag", &drag_style }, +}; + +#define NSTYLES (sizeof styles / sizeof *styles) + +/** @brief Table of state types */ +static const char *const states[] = { + "normal", + "active", + "prelight", + "selected", + "insensitive" +}; + +#define NSTATES (sizeof states / sizeof *states) + +/** @brief Table of colors */ +static const struct { + const char *name; + size_t offset; +} colors[] = { + { "fg", offsetof(GtkStyle, fg) }, + { "bg", offsetof(GtkStyle, bg) }, +}; + +#define NCOLORS (sizeof colors / sizeof *colors) + /** @brief Initialize styles */ void init_styles(void) { layout_style = gtk_style_new(); @@ -197,6 +151,192 @@ void init_styles(void) { /* Tools we leave at GTK+ defaults */ } +void save_settings(void) { + char *dir, *path, *tmp; + FILE *fp = 0; + size_t n, m, c; + + byte_xasprintf(&dir, "%s/.disorder", getenv("HOME")); + byte_xasprintf(&path, "%s/disobedience", dir); + byte_xasprintf(&tmp, "%s.tmp", path); + mkdir(dir, 02700); /* make sure directory exists */ + if(!(fp = fopen(tmp, "w"))) { + fpopup_msg(GTK_MESSAGE_ERROR, "error opening %s: %s", + tmp, strerror(errno)); + goto done; + } + if(fprintf(fp, + "# automatically generated!\n\n") < 0) + goto write_error; + for(n = 0; n < NSTYLES; ++n) + for(c = 0; c < NCOLORS; ++c) + for(m = 0; m < NSTATES; ++m) { + const GdkColor *color = (GdkColor *)((char *)styles[n].style + colors[c].offset) + m; + if(fprintf(fp, "color %8s %12s %s 0x%04x 0x%04x 0x%04x\n", + styles[n].name, states[m], colors[c].name, + color->red, + color->green, + color->blue) < 0) + goto write_error; + } + if(fprintf(fp, "browser %s\n", browser) < 0) + goto write_error; + if(fclose(fp) < 0) { + fp = 0; + write_error: + fpopup_msg(GTK_MESSAGE_ERROR, "error writing to %s: %s", + tmp, strerror(errno)); + goto done; + } + fp = 0; + if(rename(tmp, path) < 0) + fpopup_msg(GTK_MESSAGE_ERROR, "error renaming %s to %s: %s", + tmp, path, strerror(errno)); +done: + if(fp) + fclose(fp); +} + +static inline unsigned clamp(unsigned n) { + return n > 0xFFFF ? 0xFFFF : n; +} + +void load_settings(void) { + char *path, *line; + FILE *fp; + char **vec; + int nvec; + size_t n, m, c; + + byte_xasprintf(&path, "%s/.disorder/disobedience", getenv("HOME")); + if(!(fp = fopen(path, "r"))) { + if(errno != ENOENT) + fpopup_msg(GTK_MESSAGE_ERROR, "error opening %s: %s", + path, strerror(errno)); + } else { + while(!inputline(path, fp, &line, '\n')) { + if(!(vec = split(line, &nvec, SPLIT_COMMENTS|SPLIT_QUOTES, 0, 0)) + || !nvec) + continue; + if(!strcmp(vec[0], "color")) { + GdkColor *color; + if(nvec != 7) { + error(0, "%s: malformed '%s' command", path, vec[0]); + continue; + } + for(n = 0; n < NSTYLES && strcmp(styles[n].name, vec[1]); ++n) + ; + if(n >= NSTYLES) { + error(0, "%s: unknown style '%s'", path, vec[1]); + continue; + } + for(m = 0; m < NSTATES && strcmp(states[m], vec[2]); ++m) + ; + if(m >= NSTATES) { + error(0, "%s: unknown state '%s'", path, vec[2]); + continue; + } + for(c = 0; c < NCOLORS && strcmp(colors[c].name, vec[3]); ++c) + ; + if(c >= NCOLORS) { + error(0, "%s: unknown color '%s'", path, vec[3]); + continue; + } + color = (GdkColor *)((char *)styles[n].style + colors[c].offset) + m; + color->red = strtoul(vec[4], 0, 0); + color->green = strtoul(vec[5], 0, 0); + color->blue = strtoul(vec[6], 0, 0); + } else if(!strcmp(vec[0], "browser")) { + if(nvec != 2) { + error(0, "%s: malformed '%s' command", path, vec[0]); + continue; + } + browser = vec[1]; + } else + /* mention errors but otherwise ignore them */ + error(0, "%s: unknown command '%s'", path, vec[0]); + } + if(ferror(fp)) { + fpopup_msg(GTK_MESSAGE_ERROR, "error reading %s: %s", + path, strerror(errno)); + fclose(fp); + } + } +} + +/** @brief Recursively set tool widget colors + * + * This is currently unused; the idea was to allow for configurability without + * allowing GTK+ to override our use of color, but things seem generally better + * without this particular call. + */ +void set_tool_colors(GtkWidget attribute((unused)) *w) { +} + +/** @brief Pop up a settings editor widget */ +void popup_settings(void) { + static GtkWidget *settings_window; + GtkWidget *table; + unsigned row, col; + + if(settings_window) { + gtk_window_present(GTK_WINDOW(settings_window)); + return; + } + /* Create the window */ + settings_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_style(settings_window, tool_style); + gtk_window_set_title(GTK_WINDOW(settings_window), "Disobedience Settings"); + /* Clear the pointer to the window widget when it is closed */ + g_signal_connect(settings_window, "destroy", + G_CALLBACK(gtk_widget_destroyed), &settings_window); + + /* The color settings live in a big table */ + table = gtk_table_new(2 * NSTYLES + 1/*rows */, NSTATES + 1/*cols*/, + TRUE/*homogeneous*/); + /* Titles */ + for(row = 0; row < 2 * NSTYLES; ++row) { + char *legend; + + byte_xasprintf(&legend, "%s %s", styles[row / 2].name, + row % 2 ? "background" : "foreground"); + gtk_table_attach(GTK_TABLE(table), + gtk_label_new(legend), + 0, 1, + row + 1, row + 2, + GTK_FILL, GTK_FILL, + 1, 1); + } + for(col = 0; col < NSTATES; ++col) { + gtk_table_attach(GTK_TABLE(table), + gtk_label_new(states[col]), + col + 1, col + 2, + 0, 1, + GTK_FILL, GTK_FILL, + 1, 1); + } + /* The actual colors */ + for(row = 0; row < 2 * NSTYLES; ++row) { + for(col = 0; col < NSTATES; ++col) { + GdkColor *const c = &(row % 2 + ? (**styles[row / 2].style).bg + : (**styles[row / 2].style).fg)[col]; + gtk_table_attach(GTK_TABLE(table), + gtk_color_button_new_with_color(c), + col + 1, col + 2, + row + 1, row + 2, + GTK_FILL, GTK_FILL, + 1, 1); + } + } + gtk_container_add(GTK_CONTAINER(settings_window), frame_widget(table, NULL)); + gtk_widget_show_all(settings_window); + /* TODO: save settings + TODO: web browser + TODO: impose settings when they are set + */ +} + /* Local Variables: c-basic-offset:2