chiark / gitweb /
Merge branch 'master' of git.distorted.org.uk:~mdw/publish/public-git/disorder
[disorder] / disobedience / globals.c
index 7f853fd06fc5bc1fc397bfd687c44f6f6f3ed66d..a6b4ca4bd5bb1c52dd700f8de2b6efb487e5b02f 100644 (file)
 #include "disobedience.h"
 
 static GtkWidget *globals_window;
-static void globals_close(GtkButton attribute((unused)) *button,
-                        gpointer attribute((unused)) userdata);
 
-static struct globals_row {
+struct globals_row;
+
+/** @brief Handler for the presentation form of a global preference */
+struct global_handler {
+  /** @brief Initialize */
+  void (*init)(struct globals_row *row);
+
+  /** @brief Convert presentation form to string */
+  const char *(*get)(struct globals_row *row);
+
+  /** @brief Convert string to presentation form */
+  void (*set)(struct globals_row *row, const char *value);
+};
+
+/** @brief Definition of a global preference */
+struct globals_row {
   const char *label;
   const char *pref;
-  GtkWidget *entry;
-} globals_rows[] = {
-  { "Required tags", "required-tags", NULL },
-  { "Prohibited tags", "prohibited-tags", NULL },
-  { "Plating", "playing", NULL },
-  { "Random play", "random-play", NULL },
+  GtkWidget *widget;
+  const struct global_handler *handler;
+  int initialized;
+};
+
+static void globals_close(GtkButton attribute((unused)) *button,
+                        gpointer attribute((unused)) userdata);
+static void globals_row_changed(struct globals_row *row);
+
+/** @brief Called when the user changes the contents of a string entry */
+static void global_string_entry_changed(GtkEditable attribute((unused)) *editable,
+                                       gpointer user_data) {
+  struct globals_row *row = user_data;
+  globals_row_changed(row);
+}
+
+/** @brief Initialize a global presented as a string */
+static void global_string_init(struct globals_row *row) {
+  row->widget = gtk_entry_new();
+  g_signal_connect(row->widget, "changed",
+                  G_CALLBACK(global_string_entry_changed), row);
+}
+
+static const char *global_string_get(struct globals_row *row) {
+  return gtk_entry_get_text(GTK_ENTRY(row->widget));
+}
+
+static void global_string_set(struct globals_row *row, const char *value) {
+  /* Identify unset and empty lists */
+  if(!value)
+    value = "";
+  /* Skip trivial updates (we'll see one as a consequence of each
+   * update we make...) */
+  if(strcmp(gtk_entry_get_text(GTK_ENTRY(row->widget)), value))
+    gtk_entry_set_text(GTK_ENTRY(row->widget), value);
+}
+
+/** @brief String global preference */
+static const struct global_handler global_string = {
+  global_string_init,
+  global_string_get,
+  global_string_set,
+};
+
+/** @brief Called when the user changes the contents of a string entry */
+static void global_boolean_toggled(GtkToggleButton attribute((unused)) *button,
+                                  gpointer user_data) {
+  struct globals_row *row = user_data;
+  globals_row_changed(row);
+}
+
+static void global_boolean_init(struct globals_row *row) {
+  row->widget = gtk_check_button_new();
+  g_signal_connect(row->widget, "toggled",
+                  G_CALLBACK(global_boolean_toggled), row);
+}
+
+static const char *global_boolean_get(struct globals_row *row) {
+  return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(row->widget)) ? "yes" : "no";
+}
+
+static void global_boolean_set(struct globals_row *row, const char *value) {
+  gboolean new_state = !(value && strcmp(value, "yes"));
+  if(new_state != gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(row->widget)))
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(row->widget), new_state);
+}
+
+/** @brief Boolean global preference */
+static const struct global_handler global_boolean = {
+  global_boolean_init,
+  global_boolean_get,
+  global_boolean_set,
+};
+
+/** @brief Table of global preferences */
+static struct globals_row globals_rows[] = {
+  { "Required tags", "required-tags", NULL, &global_string, 0 },
+  { "Prohibited tags", "prohibited-tags", NULL, &global_string, 0 },
+  { "Playing", "playing", NULL, &global_boolean, 0 },
+  { "Random play", "random-play", NULL, &global_boolean, 0 },
 };
 #define NGLOBALS (sizeof globals_rows / sizeof *globals_rows)
 
@@ -60,13 +147,8 @@ static void globals_get_completed(void *v, const char *err,
     popup_protocol_error(0, err);
   else if(globals_window) {
     struct globals_row *row = v;
-    /* Identify unset and empty lists */
-    if(!value)
-      value = "";
-    /* Skip trivial updates (we'll see one as a consequence of each
-     * update we make...) */
-    if(strcmp(gtk_entry_get_text(GTK_ENTRY(row->entry)), value))
-      gtk_entry_set_text(GTK_ENTRY(row->entry), value);
+    row->handler->set(row, value);
+    row->initialized = 1;
   }
 }
 
@@ -75,14 +157,14 @@ static void globals_get(struct globals_row *row) {
   disorder_eclient_get_global(client, globals_get_completed, row->pref, row);
 }
 
-/** @brief Called when the user changes the contents of some entry */
-static void globals_entry_changed(GtkEditable *editable, gpointer user_data) {
-  struct globals_row *row = user_data;
-  const char *new_value = gtk_entry_get_text(GTK_ENTRY(editable));
-  if(*new_value)
-    disorder_eclient_set_global(client, NULL, row->pref, new_value, row);
-  else
-    disorder_eclient_unset_global(client, NULL, row->pref, row);
+static void globals_row_changed(struct globals_row *row) {
+  if(row->initialized) {
+    const char *new_value = row->handler->get(row);
+    if(new_value)
+      disorder_eclient_set_global(client, NULL, row->pref, new_value, row);
+    else
+      disorder_eclient_unset_global(client, NULL, row->pref, row);
+  }
 }
 
 /** @brief Display the globals window */
@@ -105,6 +187,7 @@ void popup_globals(void) {
   gtk_widget_set_style(table, tool_style);\
 
   for(size_t n = 0; n < NGLOBALS; ++n) {
+    globals_rows[n].initialized = 0;
     label = gtk_label_new(globals_rows[n].label);
     gtk_widget_set_style(label, tool_style);
     gtk_misc_set_alignment(GTK_MISC(label), 1/*right*/, 0/*bottom*/);
@@ -113,15 +196,13 @@ void popup_globals(void) {
                     n, n+1,              /* top/bottom_attach */
                     GTK_FILL, 0,         /* x/yoptions */
                     1, 1);               /* x/ypadding */
-    globals_rows[n].entry = gtk_entry_new();
-    gtk_widget_set_style(globals_rows[n].entry, tool_style);
-    gtk_table_attach(GTK_TABLE(table), globals_rows[n].entry,
+    globals_rows[n].handler->init(&globals_rows[n]);
+    gtk_widget_set_style(globals_rows[n].widget, tool_style);
+    gtk_table_attach(GTK_TABLE(table), globals_rows[n].widget,
                     1, 2,                /* left/right_attach */
                     n, n+1,              /* top/bottom_attach */
                     GTK_FILL, 0,         /* x/yoptions */
                     1, 1);               /* x/ypadding */
-    g_signal_connect(globals_rows[n].entry, "changed",
-                    G_CALLBACK(globals_entry_changed), &globals_rows[n]);
     globals_get(&globals_rows[n]);
   }
   hbox = create_buttons_box(globals_buttons,
@@ -136,9 +217,9 @@ void popup_globals(void) {
 }
 
 /** @brief Called when any global pref changes */
-static void globals_pref_changed(const char *event,
+static void globals_pref_changed(const char attribute((unused)) *event,
                                 void *eventdata,
-                                void *callbackdata) {
+                                void attribute((unused)) *callbackdata) {
   const char *pref = eventdata;
   if(!globals_window)
     return;                    /* not paying attention */