1 /***********************************************************************
2  Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 ***********************************************************************/
13 
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17 
18 #include <stdlib.h>
19 
20 #include <gtk/gtk.h>
21 
22 /* utility */
23 #include "log.h"
24 #include "mem.h"
25 #include "string_vector.h"
26 
27 /* client */
28 #include "options.h"
29 
30 /* client/gui-gtk-3.22 */
31 #include "colors.h"
32 #include "dialogs.h"
33 #include "gui_main.h"
34 #include "gui_stuff.h"
35 #include "pages.h"
36 
37 #include "optiondlg.h"
38 
39 
40 /* The option dialog data. */
41 struct option_dialog {
42   const struct option_set *poptset;     /* The option set. */
43   GtkWidget *shell;                     /* The main widget. */
44   GtkWidget *notebook;                  /* The notebook. */
45   GtkWidget **vboxes;                   /* Category boxes. */
46   int *box_children;                    /* The number of children for
47                                          * each category. */
48 };
49 
50 #define SPECLIST_TAG option_dialog
51 #define SPECLIST_TYPE struct option_dialog
52 #include "speclist.h"
53 #define option_dialogs_iterate(pdialog) \
54   TYPED_LIST_ITERATE(struct option_dialog, option_dialogs, pdialog)
55 #define option_dialogs_iterate_end  LIST_ITERATE_END
56 
57 /* All option dialog are set on this list. */
58 static struct option_dialog_list *option_dialogs = NULL;
59 
60 enum {
61   RESPONSE_CANCEL,
62   RESPONSE_OK,
63   RESPONSE_APPLY,
64   RESPONSE_RESET,
65   RESPONSE_REFRESH,
66   RESPONSE_SAVE
67 };
68 
69 
70 /* Option dialog main functions. */
71 static struct option_dialog *
72 option_dialog_get(const struct option_set *poptset);
73 static struct option_dialog *
74 option_dialog_new(const char *name, const struct option_set *poptset);
75 static void option_dialog_destroy(struct option_dialog *pdialog);
76 
77 static void option_dialog_reorder_notebook(struct option_dialog *pdialog);
78 static inline void option_dialog_foreach(struct option_dialog *pdialog,
79                                          void (*option_action)
80                                          (struct option *));
81 
82 /* Option dialog option-specific functions. */
83 static void option_dialog_option_add(struct option_dialog *pdialog,
84                                      struct option *poption,
85                                      bool reorder_notebook);
86 static void option_dialog_option_remove(struct option_dialog *pdialog,
87                                         struct option *poption);
88 
89 static void option_dialog_option_refresh(struct option *poption);
90 static void option_dialog_option_reset(struct option *poption);
91 static void option_dialog_option_apply(struct option *poption);
92 
93 
94 /****************************************************************************
95   Option dialog widget response callback.
96 ****************************************************************************/
option_dialog_reponse_callback(GtkDialog * dialog,gint response_id,gpointer data)97 static void option_dialog_reponse_callback(GtkDialog *dialog,
98                                            gint response_id, gpointer data)
99 {
100   struct option_dialog *pdialog = (struct option_dialog *) data;
101 
102   switch (response_id) {
103   case RESPONSE_CANCEL:
104     gtk_widget_destroy(GTK_WIDGET(dialog));
105     break;
106   case RESPONSE_OK:
107     option_dialog_foreach(pdialog, option_dialog_option_apply);
108     gtk_widget_destroy(GTK_WIDGET(dialog));
109     break;
110   case RESPONSE_APPLY:
111     option_dialog_foreach(pdialog, option_dialog_option_apply);
112     break;
113   case RESPONSE_RESET:
114     option_dialog_foreach(pdialog, option_dialog_option_reset);
115     break;
116   case RESPONSE_REFRESH:
117     option_dialog_foreach(pdialog, option_dialog_option_refresh);
118     break;
119   case RESPONSE_SAVE:
120     desired_settable_options_update();
121     options_save(NULL);
122     break;
123   }
124 }
125 
126 /****************************************************************************
127   Option dialog widget destroyed callback.
128 ****************************************************************************/
option_dialog_destroy_callback(GtkWidget * object,gpointer data)129 static void option_dialog_destroy_callback(GtkWidget *object, gpointer data)
130 {
131   struct option_dialog *pdialog = (struct option_dialog *) data;
132 
133   if (NULL != pdialog->shell) {
134     /* Mark as destroyed, see also option_dialog_destroy(). */
135     pdialog->shell = NULL;
136     option_dialog_destroy(pdialog);
137   }
138 }
139 
140 /*************************************************************************
141   Option refresh requested from menu.
142 *************************************************************************/
option_refresh_callback(GtkMenuItem * menuitem,gpointer data)143 static void option_refresh_callback(GtkMenuItem *menuitem, gpointer data)
144 {
145   struct option *poption = (struct option *) data;
146   struct option_dialog *pdialog = option_dialog_get(option_optset(poption));
147 
148   if (NULL != pdialog) {
149     option_dialog_option_refresh(poption);
150   }
151 }
152 
153 /*************************************************************************
154   Option reset requested from menu.
155 *************************************************************************/
option_reset_callback(GtkMenuItem * menuitem,gpointer data)156 static void option_reset_callback(GtkMenuItem *menuitem, gpointer data)
157 {
158   struct option *poption = (struct option *) data;
159   struct option_dialog *pdialog = option_dialog_get(option_optset(poption));
160 
161   if (NULL != pdialog) {
162     option_dialog_option_reset(poption);
163   }
164 }
165 
166 /*************************************************************************
167   Option apply requested from menu.
168 *************************************************************************/
option_apply_callback(GtkMenuItem * menuitem,gpointer data)169 static void option_apply_callback(GtkMenuItem *menuitem, gpointer data)
170 {
171   struct option *poption = (struct option *) data;
172   struct option_dialog *pdialog = option_dialog_get(option_optset(poption));
173 
174   if (NULL != pdialog) {
175     option_dialog_option_apply(poption);
176   }
177 }
178 
179 /*************************************************************************
180   Called when a button is pressed on a option.
181 *************************************************************************/
option_button_press_callback(GtkWidget * widget,GdkEventButton * event,gpointer data)182 static gboolean option_button_press_callback(GtkWidget *widget,
183                                              GdkEventButton *event,
184                                              gpointer data)
185 {
186   struct option *poption = (struct option *) data;
187   GtkWidget *menu, *item;
188 
189   if (3 != event->button || !option_is_changeable(poption)) {
190     /* Only right button please! */
191     return FALSE;
192   }
193 
194   menu = gtk_menu_new();
195 
196   item = gtk_menu_item_new_with_label(_("Refresh this option"));
197   gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
198   g_signal_connect(item, "activate",
199                    G_CALLBACK(option_refresh_callback), poption);
200 
201   item = gtk_menu_item_new_with_label(_("Reset this option"));
202   gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
203   g_signal_connect(item, "activate",
204                    G_CALLBACK(option_reset_callback), poption);
205 
206   item = gtk_menu_item_new_with_label(_("Apply the changes for this option"));
207   gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
208   g_signal_connect(item, "activate",
209                    G_CALLBACK(option_apply_callback), poption);
210 
211   gtk_widget_show_all(menu);
212   gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
213 
214   return TRUE;
215 }
216 
217 /****************************************************************************
218   Returns the option dialog which fit the option set.
219 ****************************************************************************/
220 static struct option_dialog *
option_dialog_get(const struct option_set * poptset)221 option_dialog_get(const struct option_set *poptset)
222 {
223   if (NULL != option_dialogs) {
224     option_dialogs_iterate(pdialog) {
225       if (pdialog->poptset == poptset) {
226         return pdialog;
227       }
228     } option_dialogs_iterate_end;
229   }
230   return NULL;
231 }
232 
233 /****************************************************************************
234   GDestroyNotify callback.
235 ****************************************************************************/
option_color_destroy_notify(gpointer data)236 static void option_color_destroy_notify(gpointer data)
237 {
238   GdkRGBA *color = (GdkRGBA *) data;
239 
240   if (NULL != color) {
241     gdk_rgba_free(color);
242   }
243 }
244 
245 /****************************************************************************
246   Set the color of a button.
247 ****************************************************************************/
option_color_set_button_color(GtkButton * button,const GdkRGBA * new_color)248 static void option_color_set_button_color(GtkButton *button,
249                                           const GdkRGBA *new_color)
250 {
251   GdkRGBA *current_color = g_object_get_data(G_OBJECT(button), "color");
252   GtkWidget *child;
253 
254   if (NULL == new_color) {
255     if (NULL != current_color) {
256       g_object_set_data(G_OBJECT(button), "color", NULL);
257       if ((child = gtk_bin_get_child(GTK_BIN(button)))) {
258         gtk_widget_destroy(child);
259       }
260     }
261   } else {
262     GdkPixbuf *pixbuf;
263 
264     /* Apply the new color. */
265     if (NULL != current_color) {
266       /* We already have a GdkRGBA pointer. */
267       *current_color = *new_color;
268     } else {
269       /* We need to make a GdkRGBA pointer. */
270       current_color = gdk_rgba_copy(new_color);
271       g_object_set_data_full(G_OBJECT(button), "color", current_color,
272                              option_color_destroy_notify);
273     }
274     if ((child = gtk_bin_get_child(GTK_BIN(button)))) {
275       gtk_widget_destroy(child);
276     }
277 
278     /* Update the button. */
279     {
280       cairo_surface_t *surface = cairo_image_surface_create(
281           CAIRO_FORMAT_RGB24, 16, 16);
282       cairo_t *cr = cairo_create(surface);
283       gdk_cairo_set_source_rgba(cr, current_color);
284       cairo_paint(cr);
285       cairo_destroy(cr);
286       pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, 16, 16);
287       cairo_surface_destroy(surface);
288     }
289     child = gtk_image_new_from_pixbuf(pixbuf);
290     gtk_container_add(GTK_CONTAINER(button), child);
291     gtk_widget_show(child);
292     g_object_unref(G_OBJECT(pixbuf));
293   }
294 }
295 
296 /****************************************************************************
297   "response" signal callback.
298 ****************************************************************************/
color_selector_response_callback(GtkDialog * dialog,gint res,gpointer data)299 static void color_selector_response_callback(GtkDialog *dialog,
300                                              gint res, gpointer data)
301 {
302   if (res == GTK_RESPONSE_REJECT) {
303     /* Clears the current color. */
304     option_color_set_button_color(GTK_BUTTON(data), NULL);
305   } else if (res == GTK_RESPONSE_OK) {
306     /* Apply the new color. */
307     GtkColorChooser *chooser =
308       GTK_COLOR_CHOOSER(g_object_get_data(G_OBJECT(dialog), "chooser"));
309     GdkRGBA new_color;
310 
311     gtk_color_chooser_get_rgba(chooser, &new_color);
312     option_color_set_button_color(GTK_BUTTON(data), &new_color);
313   }
314 
315   gtk_widget_destroy(GTK_WIDGET(dialog));
316 }
317 
318 /****************************************************************************
319   Called when the user press a color button.
320 ****************************************************************************/
option_color_select_callback(GtkButton * button,gpointer data)321 static void option_color_select_callback(GtkButton *button, gpointer data)
322 {
323   GtkWidget *dialog, *chooser;
324   GdkRGBA *current_color = g_object_get_data(G_OBJECT(button), "color");
325 
326   dialog = gtk_dialog_new_with_buttons(_("Select a color"), NULL,
327                                        GTK_DIALOG_MODAL,
328                                        _("_Cancel"), GTK_RESPONSE_CANCEL,
329                                        _("C_lear"), GTK_RESPONSE_REJECT,
330                                        _("_OK"), GTK_RESPONSE_OK, NULL);
331   setup_dialog(dialog, toplevel);
332   g_signal_connect(dialog, "response",
333                    G_CALLBACK(color_selector_response_callback), button);
334 
335   chooser = gtk_color_chooser_widget_new();
336   g_object_set_data(G_OBJECT(dialog), "chooser", chooser);
337   gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), chooser,
338                      FALSE, FALSE, 0);
339   if (current_color) {
340     gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(chooser), current_color);
341   }
342 
343   gtk_widget_show_all(dialog);
344 }
345 
346 
347 /****************************************************************************
348   Creates a new option dialog.
349 ****************************************************************************/
350 static struct option_dialog *
option_dialog_new(const char * name,const struct option_set * poptset)351 option_dialog_new(const char *name, const struct option_set *poptset)
352 {
353   struct option_dialog *pdialog;
354   const int CATEGORY_NUM = optset_category_number(poptset);
355 
356   /* Create the dialog structure. */
357   pdialog = fc_malloc(sizeof(*pdialog));
358   pdialog->poptset = poptset;
359   pdialog->shell = gtk_dialog_new_with_buttons(name, NULL, 0,
360                                                _("_Cancel"), RESPONSE_CANCEL,
361                                                _("_Save"), RESPONSE_SAVE,
362                                                _("_Refresh"), RESPONSE_REFRESH,
363                                                _("Reset"), RESPONSE_RESET,
364                                                _("_Apply"), RESPONSE_APPLY,
365                                                _("_OK"), RESPONSE_OK, NULL);
366   pdialog->notebook = gtk_notebook_new();
367   pdialog->vboxes = fc_calloc(CATEGORY_NUM, sizeof(*pdialog->vboxes));
368   pdialog->box_children = fc_calloc(CATEGORY_NUM,
369                                     sizeof(*pdialog->box_children));
370 
371   /* Append to the option dialog list. */
372   if (NULL == option_dialogs) {
373     option_dialogs = option_dialog_list_new();
374   }
375   option_dialog_list_append(option_dialogs, pdialog);
376 
377   /* Shell */
378   setup_dialog(pdialog->shell, toplevel);
379   gtk_window_set_position(GTK_WINDOW(pdialog->shell), GTK_WIN_POS_MOUSE);
380   gtk_window_set_default_size(GTK_WINDOW(pdialog->shell), -1, 480);
381   g_signal_connect(pdialog->shell, "response",
382                    G_CALLBACK(option_dialog_reponse_callback), pdialog);
383   g_signal_connect(pdialog->shell, "destroy",
384                    G_CALLBACK(option_dialog_destroy_callback), pdialog);
385 
386   gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(pdialog->shell))),
387                      pdialog->notebook, TRUE, TRUE, 0);
388 
389   /* Add the options. */
390   options_iterate(poptset, poption) {
391     option_dialog_option_add(pdialog, poption, FALSE);
392   } options_iterate_end;
393 
394   option_dialog_reorder_notebook(pdialog);
395 
396   /* Show the widgets. */
397   gtk_widget_show_all(pdialog->shell);
398 
399   return pdialog;
400 }
401 
402 /****************************************************************************
403   Destroys an option dialog.
404 ****************************************************************************/
option_dialog_destroy(struct option_dialog * pdialog)405 static void option_dialog_destroy(struct option_dialog *pdialog)
406 {
407   GtkWidget *shell = pdialog->shell;
408 
409   if (NULL != option_dialogs) {
410     option_dialog_list_remove(option_dialogs, pdialog);
411   }
412 
413   options_iterate(pdialog->poptset, poption) {
414     option_set_gui_data(poption, NULL);
415   } options_iterate_end;
416 
417   if (NULL != shell) {
418     /* Maybe already destroyed, see also option_dialog_destroy_callback(). */
419     pdialog->shell = NULL;
420     gtk_widget_destroy(shell);
421   }
422 
423   free(pdialog->vboxes);
424   free(pdialog->box_children);
425   free(pdialog);
426 }
427 
428 /****************************************************************************
429   Utility for sorting the pages of a option dialog.
430 ****************************************************************************/
option_dialog_pages_sort_func(const void * w1,const void * w2)431 static int option_dialog_pages_sort_func(const void *w1, const void *w2)
432 {
433   GObject *obj1 = G_OBJECT(*(GtkWidget **) w1);
434   GObject *obj2 = G_OBJECT(*(GtkWidget **) w2);
435 
436   return (GPOINTER_TO_INT(g_object_get_data(obj1, "category"))
437           - GPOINTER_TO_INT(g_object_get_data(obj2, "category")));
438 }
439 
440 /****************************************************************************
441   Reoder the pages of the notebook of the option dialog.
442 ****************************************************************************/
option_dialog_reorder_notebook(struct option_dialog * pdialog)443 static void option_dialog_reorder_notebook(struct option_dialog *pdialog)
444 {
445   GtkNotebook *notebook = GTK_NOTEBOOK(pdialog->notebook);
446   const int pages_num = gtk_notebook_get_n_pages(notebook);
447 
448   if (0 < pages_num) {
449     GtkWidget *pages[pages_num];
450     int i;
451 
452     for (i = 0; i < pages_num; i++) {
453       pages[i] = gtk_notebook_get_nth_page(notebook, i);
454     }
455     qsort(pages, pages_num, sizeof(*pages), option_dialog_pages_sort_func);
456     for (i = 0; i < pages_num; i++) {
457       gtk_notebook_reorder_child(notebook, pages[i], i);
458     }
459   }
460 }
461 
462 /****************************************************************************
463   Do an action for all options of the option dialog.
464 ****************************************************************************/
option_dialog_foreach(struct option_dialog * pdialog,void (* option_action)(struct option *))465 static inline void option_dialog_foreach(struct option_dialog *pdialog,
466                                          void (*option_action)
467                                          (struct option *))
468 {
469   fc_assert_ret(NULL != pdialog);
470 
471   options_iterate(pdialog->poptset, poption) {
472     option_action(poption);
473   } options_iterate_end;
474 }
475 
476 /****************************************************************************
477   Add an option to the option dialog.
478 ****************************************************************************/
option_dialog_option_add(struct option_dialog * pdialog,struct option * poption,bool reorder_notebook)479 static void option_dialog_option_add(struct option_dialog *pdialog,
480                                      struct option *poption,
481                                      bool reorder_notebook)
482 {
483   const int category = option_category(poption);
484   GtkWidget *main_hbox, *label, *ebox, *w = NULL;
485 
486   fc_assert(NULL == option_get_gui_data(poption));
487 
488   /* Add category if needed. */
489   if (NULL == pdialog->vboxes[category]) {
490     GtkWidget *sw;
491 
492     sw = gtk_scrolled_window_new(NULL, NULL);
493     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
494                                    GTK_POLICY_NEVER,
495                                    GTK_POLICY_AUTOMATIC);
496     g_object_set_data(G_OBJECT(sw), "category", GINT_TO_POINTER(category));
497     gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), sw,
498                              gtk_label_new_with_mnemonic
499                              (option_category_name(poption)));
500 
501     if (reorder_notebook) {
502       option_dialog_reorder_notebook(pdialog);
503     }
504 
505     pdialog->vboxes[category] = gtk_grid_new();
506     gtk_orientable_set_orientation(GTK_ORIENTABLE(pdialog->vboxes[category]),
507                                    GTK_ORIENTATION_VERTICAL);
508     g_object_set(pdialog->vboxes[category], "margin", 8, NULL);
509     gtk_widget_set_hexpand(pdialog->vboxes[category], TRUE);
510     gtk_container_add(GTK_CONTAINER(sw), pdialog->vboxes[category]);
511 
512     gtk_widget_show_all(sw);
513   }
514   pdialog->box_children[category]++;
515 
516   ebox = gtk_event_box_new();
517   gtk_widget_set_tooltip_text(ebox, option_help_text(poption));
518   gtk_container_add(GTK_CONTAINER(pdialog->vboxes[category]), ebox);
519   g_signal_connect(ebox, "button_press_event",
520                    G_CALLBACK(option_button_press_callback), poption);
521 
522   main_hbox = gtk_grid_new();
523   label = gtk_label_new(option_description(poption));
524   g_object_set(label, "margin", 2, NULL);
525   gtk_container_add(GTK_CONTAINER(main_hbox), label);
526   gtk_container_add(GTK_CONTAINER(ebox), main_hbox);
527 
528   switch (option_type(poption)) {
529   case OT_BOOLEAN:
530     w = gtk_check_button_new();
531     break;
532 
533   case OT_INTEGER:
534     {
535       int min = option_int_min(poption), max = option_int_max(poption);
536 
537       w = gtk_spin_button_new_with_range(min, max, MAX((max - min) / 50, 1));
538     }
539     break;
540 
541   case OT_STRING:
542     {
543       const struct strvec *values = option_str_values(poption);
544 
545       if (NULL != values) {
546         w = gtk_combo_box_text_new_with_entry();
547         strvec_iterate(values, value) {
548           gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(w), value);
549         } strvec_iterate_end;
550       } else {
551         w = gtk_entry_new();
552       }
553     }
554     break;
555 
556   case OT_ENUM:
557     {
558       int i;
559       const char *str;
560       GtkListStore *model;
561       GtkCellRenderer *renderer;
562       GtkTreeIter iter;
563 
564       /* 0: enum index, 1: translated enum name. */
565       model = gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING);
566       w = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
567       g_object_unref(model);
568 
569       renderer = gtk_cell_renderer_text_new();
570       gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w), renderer, FALSE);
571       gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), renderer,
572                                      "text", 1, NULL);
573       for (i = 0; (str = option_enum_int_to_str(poption, i)); i++) {
574         gtk_list_store_append(model, &iter);
575         gtk_list_store_set(model, &iter, 0, i, 1, _(str), -1);
576       }
577     }
578     break;
579 
580   case OT_BITWISE:
581     {
582       GList *list = NULL;
583       GtkWidget *grid, *check;
584       const struct strvec *values = option_bitwise_values(poption);
585       int i;
586 
587       w = gtk_frame_new(NULL);
588       grid = gtk_grid_new();
589       gtk_grid_set_column_spacing(GTK_GRID(grid), 4);
590       gtk_grid_set_row_homogeneous(GTK_GRID(grid), TRUE);
591       gtk_container_add(GTK_CONTAINER(w), grid);
592       for (i = 0; i < strvec_size(values); i++) {
593         check = gtk_check_button_new();
594         gtk_grid_attach(GTK_GRID(grid), check, 0, i, 1, 1);
595         label = gtk_label_new(_(strvec_get(values, i)));
596         gtk_grid_attach(GTK_GRID(grid), label, 1, i, 1, 1);
597         list = g_list_append(list, check);
598       }
599       g_object_set_data_full(G_OBJECT(w), "check_buttons", list,
600                              (GDestroyNotify) g_list_free);
601     }
602     break;
603 
604   case OT_FONT:
605     w = gtk_font_button_new();
606     g_object_set(G_OBJECT(w), "use-font", TRUE, NULL);
607     break;
608 
609   case OT_COLOR:
610     {
611       GtkWidget *button;
612 
613       w = gtk_grid_new();
614       gtk_grid_set_column_spacing(GTK_GRID(w), 4);
615       gtk_grid_set_row_homogeneous(GTK_GRID(w), TRUE);
616 
617       /* Foreground color selector button. */
618       button = gtk_button_new();
619       gtk_container_add(GTK_CONTAINER(w), button);
620       gtk_widget_set_tooltip_text(GTK_WIDGET(button),
621                                   _("Select the text color"));
622       g_object_set_data(G_OBJECT(w), "fg_button", button);
623       g_signal_connect(button, "clicked",
624                        G_CALLBACK(option_color_select_callback), NULL);
625 
626       /* Background color selector button. */
627       button = gtk_button_new();
628       gtk_container_add(GTK_CONTAINER(w), button);
629       gtk_widget_set_tooltip_text(GTK_WIDGET(button),
630                                   _("Select the background color"));
631       g_object_set_data(G_OBJECT(w), "bg_button", button);
632       g_signal_connect(button, "clicked",
633                        G_CALLBACK(option_color_select_callback), NULL);
634     }
635     break;
636 
637   case OT_VIDEO_MODE:
638     log_error("Option type %s (%d) not supported yet.",
639               option_type_name(option_type(poption)),
640               option_type(poption));
641     break;
642   }
643 
644   option_set_gui_data(poption, w);
645   if (NULL == w) {
646     log_error("Failed to create a widget for option %d \"%s\".",
647               option_number(poption), option_name(poption));
648   } else {
649     g_object_set_data(G_OBJECT(w), "main_widget", ebox);
650     gtk_widget_set_hexpand(w, TRUE);
651     gtk_widget_set_halign(w, GTK_ALIGN_END);
652     gtk_container_add(GTK_CONTAINER(main_hbox), w);
653   }
654 
655   gtk_widget_show_all(ebox);
656 
657   /* Set as current value. */
658   option_dialog_option_refresh(poption);
659 }
660 
661 /****************************************************************************
662   Remove an option from the option dialog.
663 ****************************************************************************/
option_dialog_option_remove(struct option_dialog * pdialog,struct option * poption)664 static void option_dialog_option_remove(struct option_dialog *pdialog,
665                                         struct option *poption)
666 {
667   GObject *object = G_OBJECT(option_get_gui_data(poption));
668 
669   if (NULL != object) {
670     const int category = option_category(poption);
671 
672     option_set_gui_data(poption, NULL);
673     gtk_widget_destroy(GTK_WIDGET(g_object_get_data(object, "main_widget")));
674 
675     /* Remove category if needed. */
676     if (0 == --pdialog->box_children[category]) {
677       gtk_notebook_remove_page(GTK_NOTEBOOK(pdialog->notebook), category);
678       pdialog->vboxes[category] = NULL;
679     }
680   }
681 }
682 
683 /****************************************************************************
684   Set the boolean value of the option.
685 ****************************************************************************/
option_dialog_option_bool_set(struct option * poption,bool value)686 static inline void option_dialog_option_bool_set(struct option *poption,
687                                                  bool value)
688 {
689   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
690                                (option_get_gui_data(poption)),
691                                value);
692 }
693 
694 /****************************************************************************
695   Set the integer value of the option.
696 ****************************************************************************/
option_dialog_option_int_set(struct option * poption,int value)697 static inline void option_dialog_option_int_set(struct option *poption,
698                                                 int value)
699 {
700   gtk_spin_button_set_value(GTK_SPIN_BUTTON(option_get_gui_data(poption)),
701                             value);
702 }
703 
704 /****************************************************************************
705   Set the string value of the option.
706 ****************************************************************************/
option_dialog_option_str_set(struct option * poption,const char * string)707 static inline void option_dialog_option_str_set(struct option *poption,
708                                                 const char *string)
709 {
710   if (NULL != option_str_values(poption)) {
711     gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN
712                        (option_get_gui_data(poption)))), string);
713   } else {
714     gtk_entry_set_text(GTK_ENTRY(option_get_gui_data(poption)), string);
715   }
716 }
717 
718 /****************************************************************************
719   Set the enum value of the option.
720 ****************************************************************************/
option_dialog_option_enum_set(struct option * poption,int value)721 static inline void option_dialog_option_enum_set(struct option *poption,
722                                                  int value)
723 {
724   GtkComboBox *combo = GTK_COMBO_BOX(option_get_gui_data(poption));
725   GtkTreeModel *model = gtk_combo_box_get_model(combo);
726   GtkTreeIter iter;
727   int i;
728 
729   if (gtk_tree_model_get_iter_first(model, &iter)) {
730     do {
731       gtk_tree_model_get(model, &iter, 0, &i, -1);
732       if (i == value) {
733         gtk_combo_box_set_active_iter(combo, &iter);
734         return;
735       }
736     } while (gtk_tree_model_iter_next(model, &iter));
737   }
738 
739   log_error("Didn't find the value %d for option \"%s\" (nb %d).",
740             value, option_name(poption), option_number(poption));
741 }
742 
743 /****************************************************************************
744   Set the enum value of the option.
745 ****************************************************************************/
option_dialog_option_bitwise_set(struct option * poption,unsigned value)746 static inline void option_dialog_option_bitwise_set(struct option *poption,
747                                                     unsigned value)
748 {
749   GObject *data = option_get_gui_data(poption);
750   GList *iter = g_object_get_data(data, "check_buttons");
751   int bit;
752 
753   for (bit = 0; NULL != iter; iter = g_list_next(iter), bit++) {
754     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(iter->data),
755                                  value & (1 << bit));
756   }
757 }
758 
759 /****************************************************************************
760   Set the font value of the option.
761 ****************************************************************************/
option_dialog_option_font_set(struct option * poption,const char * font)762 static inline void option_dialog_option_font_set(struct option *poption,
763                                                  const char *font)
764 {
765   gtk_font_chooser_set_font(GTK_FONT_CHOOSER
766                             (option_get_gui_data(poption)), font);
767 }
768 
769 /****************************************************************************
770   Set the font value of the option.
771 ****************************************************************************/
option_dialog_option_color_set(struct option * poption,struct ft_color color)772 static inline void option_dialog_option_color_set(struct option *poption,
773                                                   struct ft_color color)
774 {
775   GtkWidget *w = option_get_gui_data(poption);
776   GdkRGBA gdk_color;
777 
778   /* Update the foreground button. */
779   if (NULL != color.foreground
780       && '\0' != color.foreground[0]
781       && gdk_rgba_parse(&gdk_color, color.foreground)) {
782     option_color_set_button_color(g_object_get_data(G_OBJECT(w),
783                                                     "fg_button"),
784                                   &gdk_color);
785   } else {
786     option_color_set_button_color(g_object_get_data(G_OBJECT(w),
787                                                     "fg_button"), NULL);
788   }
789 
790   /* Update the background button. */
791   if (NULL != color.background
792       && '\0' != color.background[0]
793       && gdk_rgba_parse(&gdk_color, color.background)) {
794     option_color_set_button_color(g_object_get_data(G_OBJECT(w),
795                                                     "bg_button"),
796                                   &gdk_color);
797   } else {
798     option_color_set_button_color(g_object_get_data(G_OBJECT(w),
799                                                     "bg_button"), NULL);
800   }
801 }
802 
803 /****************************************************************************
804   Update an option in the option dialog.
805 ****************************************************************************/
option_dialog_option_refresh(struct option * poption)806 static void option_dialog_option_refresh(struct option *poption)
807 {
808   switch (option_type(poption)) {
809   case OT_BOOLEAN:
810     option_dialog_option_bool_set(poption, option_bool_get(poption));
811     break;
812   case OT_INTEGER:
813     option_dialog_option_int_set(poption, option_int_get(poption));
814     break;
815   case OT_STRING:
816     option_dialog_option_str_set(poption, option_str_get(poption));
817     break;
818   case OT_ENUM:
819     option_dialog_option_enum_set(poption, option_enum_get_int(poption));
820     break;
821   case OT_BITWISE:
822     option_dialog_option_bitwise_set(poption, option_bitwise_get(poption));
823     break;
824   case OT_FONT:
825     option_dialog_option_font_set(poption, option_font_get(poption));
826     break;
827   case OT_COLOR:
828     option_dialog_option_color_set(poption, option_color_get(poption));
829     break;
830   case OT_VIDEO_MODE:
831     log_error("Option type %s (%d) not supported yet.",
832               option_type_name(option_type(poption)),
833               option_type(poption));
834     break;
835   }
836 
837   gtk_widget_set_sensitive(option_get_gui_data(poption),
838                            option_is_changeable(poption));
839 }
840 
841 /****************************************************************************
842   Reset the option.
843 ****************************************************************************/
option_dialog_option_reset(struct option * poption)844 static void option_dialog_option_reset(struct option *poption)
845 {
846   switch (option_type(poption)) {
847   case OT_BOOLEAN:
848     option_dialog_option_bool_set(poption, option_bool_def(poption));
849     break;
850   case OT_INTEGER:
851     option_dialog_option_int_set(poption, option_int_def(poption));
852     break;
853   case OT_STRING:
854     option_dialog_option_str_set(poption, option_str_def(poption));
855     break;
856   case OT_ENUM:
857     option_dialog_option_enum_set(poption, option_enum_def_int(poption));
858     break;
859   case OT_BITWISE:
860     option_dialog_option_bitwise_set(poption, option_bitwise_def(poption));
861     break;
862   case OT_FONT:
863     option_dialog_option_font_set(poption, option_font_def(poption));
864     break;
865   case OT_COLOR:
866     option_dialog_option_color_set(poption, option_color_def(poption));
867     break;
868   case OT_VIDEO_MODE:
869     log_error("Option type %s (%d) not supported yet.",
870               option_type_name(option_type(poption)),
871               option_type(poption));
872     break;
873   }
874 }
875 
876 /****************************************************************************
877   Apply the option change.
878 ****************************************************************************/
option_dialog_option_apply(struct option * poption)879 static void option_dialog_option_apply(struct option *poption)
880 {
881   GtkWidget *w = GTK_WIDGET(option_get_gui_data(poption));
882 
883   switch (option_type(poption)) {
884   case OT_BOOLEAN:
885     (void) option_bool_set(poption, gtk_toggle_button_get_active
886                            (GTK_TOGGLE_BUTTON(w)));
887     break;
888 
889   case OT_INTEGER:
890     (void) option_int_set(poption, gtk_spin_button_get_value_as_int
891                           (GTK_SPIN_BUTTON(w)));
892     break;
893 
894   case OT_STRING:
895     if (NULL != option_str_values(poption)) {
896       (void) option_str_set(poption, gtk_entry_get_text
897                             (GTK_ENTRY(gtk_bin_get_child(GTK_BIN(w)))));
898     } else {
899       (void) option_str_set(poption, gtk_entry_get_text(GTK_ENTRY(w)));
900     }
901     break;
902 
903   case OT_ENUM:
904     {
905       GtkTreeIter iter;
906       int value;
907 
908       if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(w), &iter)) {
909         break;
910       }
911 
912       gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(w)),
913                          &iter, 0, &value, -1);
914       (void) option_enum_set_int(poption, value);
915     }
916     break;
917 
918   case OT_BITWISE:
919     {
920       GList *iter = g_object_get_data(G_OBJECT(w), "check_buttons");
921       unsigned value = 0;
922       int bit;
923 
924       for (bit = 0; NULL != iter; iter = g_list_next(iter), bit++) {
925         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iter->data))) {
926           value |= 1 << bit;
927         }
928       }
929       (void) option_bitwise_set(poption, value);
930     }
931     break;
932 
933   case OT_FONT:
934     (void) option_font_set(poption, gtk_font_chooser_get_font
935                            (GTK_FONT_CHOOSER(w)));
936     break;
937 
938   case OT_COLOR:
939     {
940       gchar *fg_color_text = NULL, *bg_color_text = NULL;
941       GObject *button;
942       GdkRGBA *color;
943 
944       /* Get foreground color. */
945       button = g_object_get_data(G_OBJECT(w), "fg_button");
946       color = g_object_get_data(button, "color");
947       if (color) fg_color_text = gdk_rgba_to_string(color);
948 
949       /* Get background color. */
950       button = g_object_get_data(G_OBJECT(w), "bg_button");
951       color = g_object_get_data(button, "color");
952       if (color) bg_color_text = gdk_rgba_to_string(color);
953 
954       (void) option_color_set(poption,
955                               ft_color_construct(fg_color_text, bg_color_text));
956       g_free(fg_color_text);
957       g_free(bg_color_text);
958     }
959     break;
960 
961   case OT_VIDEO_MODE:
962     log_error("Option type %s (%d) not supported yet.",
963               option_type_name(option_type(poption)),
964               option_type(poption));
965     break;
966   }
967 }
968 
969 /****************************************************************************
970   Popup the option dialog for the option set.
971 ****************************************************************************/
option_dialog_popup(const char * name,const struct option_set * poptset)972 void option_dialog_popup(const char *name, const struct option_set *poptset)
973 {
974   struct option_dialog *pdialog = option_dialog_get(poptset);
975 
976   if (NULL != pdialog) {
977     option_dialog_foreach(pdialog, option_dialog_option_refresh);
978   } else {
979     (void) option_dialog_new(name, poptset);
980   }
981 }
982 
983 /****************************************************************************
984   Popdown the option dialog for the option set.
985 ****************************************************************************/
option_dialog_popdown(const struct option_set * poptset)986 void option_dialog_popdown(const struct option_set *poptset)
987 {
988   struct option_dialog *pdialog = option_dialog_get(poptset);
989 
990   if (NULL != pdialog) {
991     option_dialog_destroy(pdialog);
992   }
993 }
994 
995 /****************************************************************************
996   Pass on updated option values to controls outside the main option
997   dialogs.
998 ****************************************************************************/
option_gui_update_extra(struct option * poption)999 static void option_gui_update_extra(struct option *poption)
1000 {
1001   if (option_optset(poption) == server_optset) {
1002     if (strcmp(option_name(poption), "aifill") == 0) {
1003       ai_fill_changed_by_server(option_int_get(poption));
1004     } else if (strcmp(option_name(poption), "nationset") == 0) {
1005       nationset_sync_to_server(option_str_get(poption));
1006     }
1007   }
1008 }
1009 
1010 /****************************************************************************
1011   Update the GUI for the option.
1012 ****************************************************************************/
option_gui_update(struct option * poption)1013 void option_gui_update(struct option *poption)
1014 {
1015   struct option_dialog *pdialog = option_dialog_get(option_optset(poption));
1016 
1017   if (NULL != pdialog) {
1018     option_dialog_option_refresh(poption);
1019   }
1020 
1021   option_gui_update_extra(poption);
1022 }
1023 
1024 /****************************************************************************
1025   Add the GUI for the option.
1026 ****************************************************************************/
option_gui_add(struct option * poption)1027 void option_gui_add(struct option *poption)
1028 {
1029   struct option_dialog *pdialog = option_dialog_get(option_optset(poption));
1030 
1031   if (NULL != pdialog) {
1032     option_dialog_option_add(pdialog, poption, TRUE);
1033   }
1034 
1035   option_gui_update_extra(poption);
1036 }
1037 
1038 /****************************************************************************
1039   Remove the GUI for the option.
1040 ****************************************************************************/
option_gui_remove(struct option * poption)1041 void option_gui_remove(struct option *poption)
1042 {
1043   struct option_dialog *pdialog = option_dialog_get(option_optset(poption));
1044 
1045   if (NULL != pdialog) {
1046     option_dialog_option_remove(pdialog, poption);
1047   }
1048 }
1049