+ GtkWidget *widget;
+ const struct global_handler *handler;
+};
+
+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 },
+ { "Prohibited tags", "prohibited-tags", NULL, &global_string },
+ { "Playing", "playing", NULL, &global_boolean },
+ { "Random play", "random-play", NULL, &global_boolean },