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