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 <stdio.h>
19 #include <stdlib.h>
20 
21 #include <X11/Intrinsic.h>
22 #include <X11/StringDefs.h>
23 #include <X11/Xaw/Form.h>
24 #include <X11/Xaw/Label.h>
25 #include <X11/Xaw/List.h>
26 #include <X11/Xaw/Command.h>
27 #include <X11/Xaw/MenuButton.h>
28 #include <X11/Xaw/SimpleMenu.h>
29 #include <X11/Xaw/Scrollbar.h>
30 #include <X11/Xaw/SmeBSB.h>
31 #include <X11/Xaw/AsciiText.h>
32 #include <X11/Xaw/Toggle.h>
33 #include <X11/Xaw/Viewport.h>
34 
35 /* utility */
36 #include "fcintl.h"
37 #include "log.h"
38 #include "mem.h"
39 #include "shared.h"
40 #include "string_vector.h"
41 #include "support.h"
42 
43 /* common */
44 #include "events.h"
45 
46 /* client */
47 #include "options.h"
48 
49 /* client/gui-gtk-2.0 */
50 #include "gui_main.h"
51 #include "gui_stuff.h"
52 
53 #include "optiondlg.h"
54 
55 
56 /* The option dialog data. */
57 struct option_dialog {
58   const struct option_set *poptset;     /* The option set. */
59   Widget shell;                         /* The main widget. */
60 };
61 
62 #define SPECLIST_TAG option_dialog
63 #define SPECLIST_TYPE struct option_dialog
64 #include "speclist.h"
65 #define option_dialogs_iterate(pdialog) \
66   TYPED_LIST_ITERATE(struct option_dialog, option_dialogs, pdialog)
67 #define option_dialogs_iterate_end  LIST_ITERATE_END
68 
69 /* All option dialog are set on this list. */
70 static struct option_dialog_list *option_dialogs = NULL;
71 
72 /* Option dialog main functions. */
73 static struct option_dialog *
74 option_dialog_get(const struct option_set *poptset);
75 static struct option_dialog *
76 option_dialog_new(const char *name, const struct option_set *poptset);
77 static void option_dialog_destroy(struct option_dialog *pdialog);
78 
79 static inline void option_dialog_foreach(struct option_dialog *pdialog,
80                                          void (*option_action)
81                                          (struct option *));
82 
83 static void option_dialog_option_refresh(struct option *poption);
84 static void option_dialog_option_apply(struct option *poption);
85 
86 
87 /****************************************************************************
88  Changes the label of the toggle widget to Yes/No depending on the state of
89  the toggle.
90 ****************************************************************************/
toggle_callback(Widget w,XtPointer client_data,XtPointer call_data)91 void toggle_callback(Widget w, XtPointer client_data, XtPointer call_data)
92 {
93   Boolean b;
94 
95   XtVaGetValues(w, XtNstate, &b, NULL);
96   XtVaSetValues(w, XtNlabel, b ? _("Yes") : _("No"), NULL);
97 }
98 
99 /****************************************************************************
100   Callback to change the entry for a string option that has a fixed list
101   of possible entries.
102 ****************************************************************************/
stropt_change_callback(Widget w,XtPointer client_data,XtPointer call_data)103 static void stropt_change_callback(Widget w, XtPointer client_data,
104                                    XtPointer call_data)
105 {
106   char *val = (char *) client_data;
107 
108   XtVaSetValues(XtParent(XtParent(w)), "label", val, NULL);
109 }
110 
111 /****************************************************************************
112   OK button callback.
113 ****************************************************************************/
option_dialog_ok_callback(Widget w,XtPointer client_data,XtPointer call_data)114 static void option_dialog_ok_callback(Widget w, XtPointer client_data,
115                                       XtPointer call_data)
116 {
117   struct option_dialog *pdialog = (struct option_dialog *) client_data;
118 
119   option_dialog_foreach(pdialog, option_dialog_option_apply);
120   option_dialog_destroy(pdialog);
121 }
122 
123 /****************************************************************************
124   Cancel button callback.
125 ****************************************************************************/
option_dialog_cancel_callback(Widget w,XtPointer client_data,XtPointer call_data)126 static void option_dialog_cancel_callback(Widget w, XtPointer client_data,
127                                           XtPointer call_data)
128 {
129   struct option_dialog *pdialog = (struct option_dialog *) client_data;
130 
131   option_dialog_destroy(pdialog);
132 }
133 
134 /****************************************************************************
135   Returns the option dialog which fit the option set.
136 ****************************************************************************/
137 static struct option_dialog *
option_dialog_get(const struct option_set * poptset)138 option_dialog_get(const struct option_set *poptset)
139 {
140   if (NULL != option_dialogs) {
141     option_dialogs_iterate(pdialog) {
142       if (pdialog->poptset == poptset) {
143         return pdialog;
144       }
145     } option_dialogs_iterate_end;
146   }
147   return NULL;
148 }
149 
150 /****************************************************************************
151   Creates a new option dialog.
152 ****************************************************************************/
153 static struct option_dialog *
option_dialog_new(const char * name,const struct option_set * poptset)154 option_dialog_new(const char *name, const struct option_set *poptset)
155 {
156   struct option_dialog *pdialog;
157   Widget form, viewport, command, scrollform, label, widget;
158   Dimension width, longest_width = 0;
159 
160   /* Create the dialog structure. */
161   pdialog = fc_malloc(sizeof(*pdialog));
162   pdialog->poptset = poptset;
163   pdialog->shell = I_T(XtCreatePopupShell("settableoptionspopup",
164                                           transientShellWidgetClass,
165                                           toplevel, NULL, 0));
166   /* Append to the option dialog list. */
167   if (NULL == option_dialogs) {
168     option_dialogs = option_dialog_list_new();
169   }
170   option_dialog_list_append(option_dialogs, pdialog);
171 
172   form = XtVaCreateManagedWidget("settableoptionsform", formWidgetClass,
173                                  pdialog->shell, NULL);
174   label = I_L(XtVaCreateManagedWidget("settableoptionslabel",
175                                       labelWidgetClass, form, NULL));
176   viewport = XtVaCreateManagedWidget("settableoptionsviewport",
177                                      viewportWidgetClass, form, NULL);
178   scrollform = XtVaCreateManagedWidget("settableoptionsscrollform",
179                                        formWidgetClass, viewport, NULL);
180 
181   /* Add option labels. */
182   widget = NULL;
183   options_iterate(poptset, poption) {
184     if (NULL != widget) {
185       option_set_gui_data(poption, widget);     /* Set previous label. */
186 
187       widget = XtVaCreateManagedWidget("label", labelWidgetClass,
188                                        scrollform, XtNlabel,
189                                        option_description(poption),
190                                        XtNfromVert, widget, NULL);
191     } else {
192       widget = XtVaCreateManagedWidget("label", labelWidgetClass,
193                                        scrollform, XtNlabel,
194                                        option_description(poption), NULL);
195     }
196 
197     /* The addition of a scrollbar screws things up. There must be a
198      * better way to do this. */
199     XtVaGetValues(widget, XtNwidth, &width, NULL);
200     width += 15;
201     XtVaSetValues(widget, XtNwidth, width, NULL);
202 
203     if (width > longest_width) {
204       longest_width = width;
205     }
206   } options_iterate_end;
207 
208   /* Resize label. */
209   XtVaSetValues(label, XtNwidth, longest_width + 15, NULL);
210 
211   /* Add option widgets. */
212   options_iterate(poptset, poption) {
213     widget = option_get_gui_data(poption);      /* Get previous label. */
214     option_set_gui_data(poption, NULL);
215 
216     switch (option_type(poption)) {
217     case OT_BOOLEAN:
218       if (NULL != widget) {
219         widget = XtVaCreateManagedWidget("toggle", toggleWidgetClass,
220                                          scrollform, XtNfromHoriz,
221                                          label, XtNfromVert, widget, NULL);
222       } else {
223         widget = XtVaCreateManagedWidget("toggle", toggleWidgetClass,
224                                          scrollform, XtNfromHoriz, label,
225                                          NULL);
226       }
227       XtAddCallback(widget, XtNcallback, toggle_callback, NULL);
228       option_set_gui_data(poption, widget);
229       break;
230     case OT_STRING:
231       if (option_str_values(poption)) {
232         const struct strvec *vals = option_str_values(poption);
233         Widget popupmenu;
234 
235         if (NULL != widget) {
236           widget = XtVaCreateManagedWidget(option_name(poption),
237                                            menuButtonWidgetClass, scrollform,
238                                            XtNfromHoriz, label, XtNfromVert,
239                                            widget, NULL);
240         } else {
241           widget = XtVaCreateManagedWidget(option_name(poption),
242                                            menuButtonWidgetClass, scrollform,
243                                            XtNfromHoriz, label, NULL);
244         }
245 
246         popupmenu = XtVaCreatePopupShell("menu", simpleMenuWidgetClass,
247                                          widget, NULL);
248 
249         strvec_iterate(vals, val) {
250           Widget entry = XtVaCreateManagedWidget(val, smeBSBObjectClass,
251                                                  popupmenu, NULL);
252           XtAddCallback(entry, XtNcallback, stropt_change_callback,
253                         (XtPointer) val);
254         } strvec_iterate_end;
255 
256         if (0 == strvec_size(vals)) {
257           /* We could disable this if there was just one possible choice,
258            * too, but for values that are uninitialized (empty) this
259            * would be confusing. */
260           XtSetSensitive(widget, FALSE);
261         }
262 
263         /* There should be another way to set width of menu button */
264         XtVaSetValues(widget, XtNwidth, 120 ,NULL);
265         option_set_gui_data(poption, widget);
266         break;
267       }
268       /* Else fallthrough. */
269     case OT_INTEGER:
270       if (NULL != widget) {
271         widget = XtVaCreateManagedWidget("input", asciiTextWidgetClass,
272                                          scrollform, XtNfromHoriz, label,
273                                          XtNfromVert, widget, NULL);
274       } else {
275         widget = XtVaCreateManagedWidget("input", asciiTextWidgetClass,
276                                          scrollform, XtNfromHoriz, label,
277                                          NULL);
278       }
279       option_set_gui_data(poption, widget);
280       break;
281     case OT_ENUM:
282     case OT_BITWISE:
283     case OT_FONT:
284     case OT_COLOR:
285     case OT_VIDEO_MODE:
286       log_error("Option type %s (%d) not supported yet.",
287                 option_type_name(option_type(poption)),
288                 option_type(poption));
289       break;
290     }
291   } options_iterate_end;
292 
293   /* Add buttons. */
294   command = I_L(XtVaCreateManagedWidget("settableoptionsokcommand",
295                                         commandWidgetClass, form, NULL));
296   XtAddCallback(command, XtNcallback, option_dialog_ok_callback, pdialog);
297 
298   command = I_L(XtVaCreateManagedWidget("settableoptionscancelcommand",
299                                         commandWidgetClass, form, NULL));
300   XtAddCallback(command, XtNcallback,
301                 option_dialog_cancel_callback, pdialog);
302 
303   XtRealizeWidget(pdialog->shell);
304   xaw_horiz_center(label);
305   XtSetSensitive(main_form, FALSE);
306   xaw_set_relative_position(toplevel, pdialog->shell, 25, 25);
307   XtPopup(pdialog->shell, XtGrabNone);
308 
309   return pdialog;
310 }
311 
312 /****************************************************************************
313   Destroys an option dialog.
314 ****************************************************************************/
option_dialog_destroy(struct option_dialog * pdialog)315 static void option_dialog_destroy(struct option_dialog *pdialog)
316 {
317   if (NULL != option_dialogs) {
318     option_dialog_list_remove(option_dialogs, pdialog);
319   }
320 
321   options_iterate(pdialog->poptset, poption) {
322     option_set_gui_data(poption, NULL);
323   } options_iterate_end;
324 
325   XtDestroyWidget(pdialog->shell);
326   free(pdialog);
327   XtSetSensitive(main_form, TRUE);
328 }
329 
330 /****************************************************************************
331   Do an action for all options of the option dialog.
332 ****************************************************************************/
option_dialog_foreach(struct option_dialog * pdialog,void (* option_action)(struct option *))333 static inline void option_dialog_foreach(struct option_dialog *pdialog,
334                                          void (*option_action)
335                                          (struct option *))
336 {
337   fc_assert_ret(NULL != pdialog);
338 
339   options_iterate(pdialog->poptset, poption) {
340     option_action(poption);
341   } options_iterate_end;
342 }
343 
344 /****************************************************************************
345   Update an option in the option dialog.
346 ****************************************************************************/
option_dialog_option_refresh(struct option * poption)347 static void option_dialog_option_refresh(struct option *poption)
348 {
349   Widget widget = (Widget) option_get_gui_data(poption);
350 
351   switch (option_type(poption)) {
352   case OT_BOOLEAN:
353     XtVaSetValues(widget, XtNstate, option_bool_get(poption),
354                   XtNlabel, option_bool_get(poption) ? _("Yes") : _("No"),
355                   NULL);
356   break;
357   case OT_INTEGER:
358     {
359       char valstr[64];
360 
361       fc_snprintf(valstr, sizeof(valstr), "%d", option_int_get(poption));
362       XtVaSetValues(widget, XtNstring, valstr, NULL);
363     }
364     break;
365   case OT_STRING:
366     XtVaSetValues(widget, option_str_values(poption) ? "label"
367                   : XtNstring, option_str_get(poption), NULL);
368     break;
369   case OT_ENUM:
370   case OT_BITWISE:
371   case OT_FONT:
372   case OT_COLOR:
373   case OT_VIDEO_MODE:
374     log_error("Option type %s (%d) not supported yet.",
375               option_type_name(option_type(poption)),
376               option_type(poption));
377     break;
378   }
379 
380   XtSetSensitive(widget, option_is_changeable(poption));
381 }
382 
383 /****************************************************************************
384   Apply the option change.
385 ****************************************************************************/
option_dialog_option_apply(struct option * poption)386 static void option_dialog_option_apply(struct option *poption)
387 {
388   if (option_is_changeable(poption)) {
389     Widget widget = (Widget) option_get_gui_data(poption);
390 
391     switch (option_type(poption)) {
392     case OT_BOOLEAN:
393       {
394         Boolean b;
395 
396         XtVaGetValues(widget, XtNstate, &b, NULL);
397         option_bool_set(poption, b);
398       }
399       break;
400     case OT_INTEGER:
401       {
402         XtPointer dp;
403         int val;
404 
405         XtVaGetValues(widget, XtNstring, &dp, NULL);
406         if (str_to_int(dp, &val)) {
407           option_int_set(poption, val);
408         }
409       }
410       break;
411     case OT_STRING:
412       {
413         XtPointer dp;
414 
415         XtVaGetValues(widget, option_str_values(poption) ? "label"
416                       : XtNstring, &dp, NULL);
417         option_str_set(poption, dp);
418       }
419       break;
420     case OT_ENUM:
421     case OT_BITWISE:
422     case OT_FONT:
423     case OT_COLOR:
424     case OT_VIDEO_MODE:
425       log_error("Option type %s (%d) not supported yet.",
426                 option_type_name(option_type(poption)),
427                 option_type(poption));
428       break;
429     }
430   }
431 }
432 
433 /****************************************************************************
434   Popup the option dialog for the option set.
435 ****************************************************************************/
option_dialog_popup(const char * name,const struct option_set * poptset)436 void option_dialog_popup(const char *name, const struct option_set *poptset)
437 {
438   struct option_dialog *pdialog = option_dialog_get(poptset);
439 
440   if (NULL != pdialog) {
441     option_dialog_foreach(pdialog, option_dialog_option_refresh);
442   } else {
443     (void) option_dialog_new(name, poptset);
444   }
445 }
446 
447 /****************************************************************************
448   Popdown the option dialog for the option set.
449 ****************************************************************************/
option_dialog_popdown(const struct option_set * poptset)450 void option_dialog_popdown(const struct option_set *poptset)
451 {
452   struct option_dialog *pdialog = option_dialog_get(poptset);
453 
454   if (NULL != pdialog) {
455     option_dialog_destroy(pdialog);
456   }
457 }
458 
459 /****************************************************************************
460   Update the GUI for the option.
461 ****************************************************************************/
option_gui_update(struct option * poption)462 void option_gui_update(struct option *poption)
463 {
464   struct option_dialog *pdialog = option_dialog_get(option_optset(poption));
465 
466   if (NULL != pdialog) {
467     option_dialog_option_refresh(poption);
468   }
469 }
470 
471 /****************************************************************************
472   Add the GUI for the option.
473 ****************************************************************************/
option_gui_add(struct option * poption)474 void option_gui_add(struct option *poption)
475 {
476   /* We cannot currently insert new option widgets. */
477   option_dialog_popdown(option_optset(poption));
478 }
479 
480 /****************************************************************************
481   Remove the GUI for the option.
482 ****************************************************************************/
option_gui_remove(struct option * poption)483 void option_gui_remove(struct option *poption)
484 {
485   /* We cannot currently remove new option widgets. */
486   option_dialog_popdown(option_optset(poption));
487 }
488