+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) {
+ disorder_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) {
+ disorder_error(0, "%s: unknown style '%s'", path, vec[1]);
+ continue;
+ }
+ for(m = 0; m < NSTATES && strcmp(states[m], vec[2]); ++m)
+ ;
+ if(m >= NSTATES) {
+ disorder_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) {
+ disorder_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) {
+ disorder_error(0, "%s: malformed '%s' command", path, vec[0]);
+ continue;
+ }
+ browser = vec[1];
+ } else
+ /* mention errors but otherwise ignore them */
+ disorder_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
+ */
+}
+