toptdialog.c - vaccinewars - be a doctor and try to vaccinate the world
HTML git clone git://src.adamsgaard.dk/vaccinewars
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
toptdialog.c (35785B)
---
1 /************************************************************************
2 * optdialog.c Configuration file editing dialog *
3 * Copyright (C) 2002-2004 Ben Webb *
4 * Email: benwebb@users.sf.net *
5 * WWW: https://dopewars.sourceforge.io/ *
6 * *
7 * This program is free software; you can redistribute it and/or *
8 * modify it under the terms of the GNU General Public License *
9 * as published by the Free Software Foundation; either version 2 *
10 * of the License, or (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the Free Software *
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, *
20 * MA 02111-1307, USA. *
21 ************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h> /* For strcmp */
28 #include <stdlib.h> /* For atoi */
29
30 #include "configfile.h" /* For UpdateConfigFile etc. */
31 #include "dopewars.h" /* For struct GLOBALS etc. */
32 #include "gtk_client.h" /* For mainwindow etc. */
33 #include "nls.h" /* For _ function */
34 #include "sound.h" /* For SoundPlay */
35 #include "gtkport/gtkport.h" /* For gtk_ functions */
36
37 struct ConfigWidget {
38 GtkWidget *widget;
39 gint globind;
40 int maxindex;
41 gchar **data;
42 };
43
44 static GSList *configlist = NULL, *clists = NULL;
45
46 struct ConfigMembers {
47 gchar *label, *name;
48 };
49
50 static void FreeConfigWidgets(void)
51 {
52 while (configlist) {
53 int i;
54 struct ConfigWidget *cwid = (struct ConfigWidget *)configlist->data;
55
56 for (i = 0; i < cwid->maxindex; i++) {
57 g_free(cwid->data[i]);
58 }
59 g_free(cwid->data);
60 configlist = g_slist_remove(configlist, cwid);
61 }
62 }
63
64 static void UpdateAllLists(void)
65 {
66 GSList *listpt;
67
68 for (listpt = clists; listpt; listpt = g_slist_next(listpt)) {
69 GtkTreeView *tv = GTK_TREE_VIEW(listpt->data);
70 GtkTreeSelection *treesel = gtk_tree_view_get_selection(tv);
71 /* Force unselection, which should trigger *_sel_changed function to
72 copy widget data into configuration */
73 gtk_tree_selection_unselect_all(treesel);
74 }
75 }
76
77 static void SaveConfigWidget(struct GLOBALS *gvar, struct ConfigWidget *cwid,
78 int structind)
79 {
80 gboolean changed = FALSE;
81
82 if (gvar->BoolVal) {
83 gboolean *boolpt, newset;
84
85 boolpt = GetGlobalBoolean(cwid->globind, structind);
86 if (cwid->maxindex) {
87 newset = (cwid->data[structind - 1] != NULL);
88 } else {
89 newset = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cwid->widget));
90 }
91 changed = (*boolpt != newset);
92 *boolpt = newset;
93 } else {
94 gchar *text;
95
96 if (cwid->maxindex) {
97 text = cwid->data[structind - 1];
98 } else {
99 text = gtk_editable_get_chars(GTK_EDITABLE(cwid->widget), 0, -1);
100 }
101 if (gvar->IntVal) {
102 int *intpt, newset;
103
104 intpt = GetGlobalInt(cwid->globind, structind);
105 newset = atoi(text);
106 if (newset < gvar->MinVal) {
107 newset = gvar->MinVal;
108 }
109 if (gvar->MaxVal > gvar->MinVal && newset > gvar->MaxVal) {
110 newset = gvar->MaxVal;
111 }
112 changed = (*intpt != newset);
113 *intpt = newset;
114 } else if (gvar->PriceVal) {
115 price_t *pricept, newset;
116
117 pricept = GetGlobalPrice(cwid->globind, structind);
118 newset = strtoprice(text);
119 changed = (*pricept != newset);
120 *pricept = newset;
121 } else if (gvar->StringVal) {
122 gchar **strpt;
123
124 strpt = GetGlobalString(cwid->globind, structind);
125 changed = (strcmp(*strpt, text) != 0);
126 AssignName(strpt, text);
127 }
128 if (!cwid->maxindex) {
129 g_free(text);
130 }
131 }
132 gvar->Modified |= changed;
133 }
134
135 static void ResizeList(struct GLOBALS *gvar, int newmax)
136 {
137 int i;
138
139 for (i = 0; i < NUMGLOB; i++) {
140 if (Globals[i].StructListPt == gvar->StructListPt
141 && Globals[i].ResizeFunc) {
142 Globals[i].Modified = TRUE;
143 (Globals[i].ResizeFunc) (newmax);
144 return;
145 }
146 }
147 g_assert(0);
148 }
149
150 static void SaveConfigWidgets(void)
151 {
152 GSList *listpt;
153
154 UpdateAllLists();
155
156 for (listpt = configlist; listpt; listpt = g_slist_next(listpt)) {
157 struct ConfigWidget *cwid = (struct ConfigWidget *)listpt->data;
158 struct GLOBALS *gvar;
159
160 gvar = &Globals[cwid->globind];
161 if (gvar->NameStruct && gvar->NameStruct[0]) {
162 int i;
163
164 if (cwid->maxindex != *gvar->MaxIndex) {
165 ResizeList(gvar, cwid->maxindex);
166 }
167
168 for (i = 1; i <= *gvar->MaxIndex; i++) {
169 SaveConfigWidget(gvar, cwid, i);
170 }
171 } else {
172 SaveConfigWidget(gvar, cwid, 0);
173 }
174 }
175 }
176
177 static void AddConfigWidget(GtkWidget *widget, int ind)
178 {
179 struct ConfigWidget *cwid = g_new(struct ConfigWidget, 1);
180 struct GLOBALS *gvar;
181
182 cwid->widget = widget;
183 cwid->globind = ind;
184 cwid->data = NULL;
185 cwid->maxindex = 0;
186
187 gvar = &Globals[ind];
188 if (gvar->MaxIndex) {
189 int i;
190
191 cwid->maxindex = *gvar->MaxIndex;
192 cwid->data = g_new(gchar *, *gvar->MaxIndex);
193 for (i = 1; i <= *gvar->MaxIndex; i++) {
194 if (gvar->StringVal) {
195 cwid->data[i - 1] = g_strdup(*GetGlobalString(ind, i));
196 } else if (gvar->IntVal) {
197 cwid->data[i - 1] =
198 g_strdup_printf("%d", *GetGlobalInt(ind, i));
199 } else if (gvar->PriceVal) {
200 cwid->data[i - 1] = pricetostr(*GetGlobalPrice(ind, i));
201 } else if (gvar->BoolVal) {
202 if (*GetGlobalBoolean(ind, i)) {
203 cwid->data[i - 1] = g_strdup("1");
204 } else {
205 cwid->data[i - 1] = NULL;
206 }
207 }
208 }
209 }
210 configlist = g_slist_append(configlist, cwid);
211 }
212
213 static GtkWidget *NewConfigCheck(gchar *name, gchar *label)
214 {
215 GtkWidget *check;
216 int ind;
217 struct GLOBALS *gvar;
218
219 ind = GetGlobalIndex(name, NULL);
220 g_assert(ind >= 0);
221 gvar = &Globals[ind];
222 g_assert(gvar->BoolVal);
223
224 check = gtk_check_button_new_with_label(label);
225 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), *gvar->BoolVal);
226
227 AddConfigWidget(check, ind);
228
229 return check;
230 }
231
232 static GtkWidget *NewConfigEntry(gchar *name)
233 {
234 GtkWidget *entry;
235 gchar *tmpstr;
236 int ind;
237 struct GLOBALS *gvar;
238
239 ind = GetGlobalIndex(name, NULL);
240 g_assert(ind >= 0);
241
242 entry = gtk_entry_new();
243 gvar = &Globals[ind];
244
245 if (gvar->StringVal) {
246 gtk_entry_set_text(GTK_ENTRY(entry), *gvar->StringVal);
247 } else if (gvar->IntVal) {
248 if (gvar->MaxVal > gvar->MinVal) {
249 GtkAdjustment *spin_adj;
250
251 gtk_widget_destroy(entry);
252 spin_adj = (GtkAdjustment *)gtk_adjustment_new(*gvar->IntVal,
253 gvar->MinVal,
254 gvar->MaxVal,
255 1.0, 10.0, 0.0);
256 entry = gtk_spin_button_new(spin_adj, 1.0, 0);
257 } else {
258 tmpstr = g_strdup_printf("%d", *gvar->IntVal);
259 gtk_entry_set_text(GTK_ENTRY(entry), tmpstr);
260 g_free(tmpstr);
261 }
262 } else if (gvar->PriceVal) {
263 tmpstr = pricetostr(*gvar->PriceVal);
264 gtk_entry_set_text(GTK_ENTRY(entry), tmpstr);
265 g_free(tmpstr);
266 }
267
268 AddConfigWidget(entry, ind);
269
270 return entry;
271 }
272
273 static void AddStructConfig(GtkWidget *grid, int row, gchar *structname,
274 struct ConfigMembers *member)
275 {
276 int ind;
277 struct GLOBALS *gvar;
278
279 ind = GetGlobalIndex(structname, member->name);
280 g_assert(ind >= 0);
281
282 gvar = &Globals[ind];
283 if (gvar->BoolVal) {
284 GtkWidget *check;
285
286 check = gtk_check_button_new_with_label(_(member->label));
287 dp_gtk_grid_attach(GTK_GRID(grid), check, 0, row, 2, 1, TRUE);
288 AddConfigWidget(check, ind);
289 } else {
290 GtkWidget *label, *entry;
291
292 label = gtk_label_new(_(member->label));
293 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1, FALSE);
294 if (gvar->IntVal && gvar->MaxVal > gvar->MinVal) {
295 GtkAdjustment *spin_adj = (GtkAdjustment *)
296 gtk_adjustment_new(gvar->MinVal, gvar->MinVal, gvar->MaxVal,
297 1.0, 10.0, 0.0);
298 entry = gtk_spin_button_new(spin_adj, 1.0, 0);
299 } else {
300 entry = gtk_entry_new();
301 }
302 dp_gtk_grid_attach(GTK_GRID(grid), entry, 1, row, 1, 1, TRUE);
303 AddConfigWidget(entry, ind);
304 }
305 }
306
307 static void swap_rows(GtkTreeView *tv, gint selrow, gint swaprow,
308 gchar *structname)
309 {
310 GSList *listpt;
311 GtkTreeIter seliter, swapiter;
312 GtkTreeModel *model = gtk_tree_view_get_model(tv);
313 GtkTreeSelection *treesel = gtk_tree_view_get_selection(tv);
314
315 g_assert(gtk_tree_model_iter_nth_child(model, &seliter, NULL, selrow));
316 g_assert(gtk_tree_model_iter_nth_child(model, &swapiter, NULL, swaprow));
317
318 gtk_tree_selection_unselect_iter(treesel, &seliter);
319 gtk_list_store_swap(GTK_LIST_STORE(model), &seliter, &swapiter);
320
321 for (listpt = configlist; listpt; listpt = g_slist_next(listpt)) {
322 struct ConfigWidget *cwid = (struct ConfigWidget *)listpt->data;
323 struct GLOBALS *gvar;
324
325 gvar = &Globals[cwid->globind];
326
327 if (gvar->NameStruct && strcmp(structname, gvar->NameStruct) == 0) {
328 gchar *tmp = cwid->data[selrow];
329
330 cwid->data[selrow] = cwid->data[swaprow];
331 cwid->data[swaprow] = tmp;
332 }
333 }
334
335 gtk_tree_selection_select_iter(treesel, &seliter);
336 }
337
338 /* Return the index of the currently selected row, or -1 if none is selected.
339 This works only for lists (not trees) with GTK_SELECTION_SINGLE mode */
340 static int get_tree_selection_row_index(GtkTreeSelection *treesel,
341 GtkTreeModel **model)
342 {
343 int row = -1;
344 GList *selrows = gtk_tree_selection_get_selected_rows(treesel, model);
345 if (selrows) {
346 GtkTreePath *path = selrows->data;
347 gint depth;
348 gint *inds = gtk_tree_path_get_indices_with_depth(path, &depth);
349 g_assert(selrows->next == NULL);
350 g_assert(depth == 1);
351 row = inds[0];
352 }
353 g_list_free_full(selrows, (GDestroyNotify)gtk_tree_path_free);
354 return row;
355 }
356
357 static void list_delete(GtkWidget *widget, gchar *structname)
358 {
359 GtkTreeView *tv;
360 GtkTreeSelection *treesel;
361 GtkTreeModel *model;
362 int minlistlength, nrows, row;
363
364 tv = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget), "treeview"));
365 g_assert(tv);
366 treesel = gtk_tree_view_get_selection(tv);
367 row = get_tree_selection_row_index(treesel, &model);
368
369 minlistlength = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model),
370 "minlistlength"));
371 nrows = gtk_tree_model_iter_n_children(model, NULL);
372
373 if (nrows > minlistlength && row >= 0) {
374 GtkTreeIter iter;
375 GSList *listpt;
376 gboolean valid;
377 /* Prevent selection changed from reading deleted entry */
378 g_object_set_data(G_OBJECT(model), "oldsel", GINT_TO_POINTER(-1));
379 g_assert(gtk_tree_model_iter_nth_child(model, &iter, NULL, row));
380 gtk_tree_selection_unselect_iter(treesel, &iter);
381 valid = gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
382
383 for (listpt = configlist; listpt; listpt = g_slist_next(listpt)) {
384 struct ConfigWidget *cwid = (struct ConfigWidget *)listpt->data;
385 struct GLOBALS *gvar;
386
387 gvar = &Globals[cwid->globind];
388 if (gvar->NameStruct && strcmp(structname, gvar->NameStruct) == 0) {
389 int i;
390
391 cwid->maxindex--;
392 g_free(cwid->data[row]);
393 for (i = row; i < cwid->maxindex; i++) {
394 cwid->data[i] = cwid->data[i + 1];
395 }
396 cwid->data = g_realloc(cwid->data, sizeof(gchar *) * cwid->maxindex);
397 }
398 }
399 if (valid) {
400 gtk_tree_selection_select_iter(treesel, &iter);
401 }
402 }
403 }
404
405 static void list_new(GtkWidget *widget, gchar *structname)
406 {
407 GtkTreeView *tv;
408 GtkListStore *store;
409 GtkTreeSelection *treesel;
410 GtkTreeIter iter;
411 int row;
412 GSList *listpt;
413 gchar *newname;
414
415 tv = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget), "treeview"));
416 g_assert(tv);
417 treesel = gtk_tree_view_get_selection(tv);
418 store = GTK_LIST_STORE(gtk_tree_view_get_model(tv));
419
420 newname = g_strdup_printf(_("New %s"), structname);
421 gtk_list_store_append(store, &iter);
422 gtk_list_store_set(store, &iter, 0, newname, -1);
423 row = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL) - 1;
424
425 for (listpt = configlist; listpt; listpt = g_slist_next(listpt)) {
426 struct ConfigWidget *cwid = (struct ConfigWidget *)listpt->data;
427 struct GLOBALS *gvar;
428
429 gvar = &Globals[cwid->globind];
430 if (gvar->NameStruct && strcmp(structname, gvar->NameStruct) == 0) {
431 cwid->maxindex++;
432 g_assert(cwid->maxindex == row + 1);
433 cwid->data = g_realloc(cwid->data, sizeof(gchar *) * cwid->maxindex);
434 if (strcmp(gvar->Name, "Name") == 0) {
435 cwid->data[row] = g_strdup(newname);
436 } else {
437 cwid->data[row] = g_strdup("");
438 }
439 }
440 }
441 g_free(newname);
442
443 gtk_tree_selection_select_iter(treesel, &iter);
444 }
445
446 static void list_up(GtkWidget *widget, gchar *structname)
447 {
448 GtkTreeView *tv;
449 GtkTreeSelection *treesel;
450 int row;
451
452 tv = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget), "treeview"));
453 g_assert(tv);
454 treesel = gtk_tree_view_get_selection(tv);
455 row = get_tree_selection_row_index(treesel, NULL);
456
457 if (row > 0) {
458 swap_rows(tv, row, row - 1, structname);
459 }
460 }
461
462 static void list_down(GtkWidget *widget, gchar *structname)
463 {
464 GtkTreeView *tv;
465 GtkTreeSelection *treesel;
466 GtkTreeModel *model;
467 int row, nrows;
468
469 tv = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget), "treeview"));
470 g_assert(tv);
471 treesel = gtk_tree_view_get_selection(tv);
472 row = get_tree_selection_row_index(treesel, &model);
473 nrows = gtk_tree_model_iter_n_children(model, NULL);
474
475 if (row < nrows - 1 && row >= 0) {
476 swap_rows(tv, row, row + 1, structname);
477 }
478 }
479
480 static void list_sel_changed(GtkTreeSelection *treesel, gpointer data)
481 {
482 GtkTreeModel *model;
483 GtkWidget *delbut, *upbut, *downbut;
484 int minlistlength, nrows, row, oldsel;
485 GSList *listpt;
486 gchar *structname = data;
487
488 row = get_tree_selection_row_index(treesel, &model);
489 nrows = gtk_tree_model_iter_n_children(model, NULL);
490
491 delbut = GTK_WIDGET(g_object_get_data(G_OBJECT(model), "delete"));
492 upbut = GTK_WIDGET(g_object_get_data(G_OBJECT(model), "up"));
493 downbut = GTK_WIDGET(g_object_get_data(G_OBJECT(model), "down"));
494 oldsel = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model), "oldsel"));
495 g_assert(delbut && upbut && downbut);
496 minlistlength = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model),
497 "minlistlength"));
498
499 gtk_widget_set_sensitive(delbut, nrows > minlistlength);
500 gtk_widget_set_sensitive(upbut, row > 0);
501 gtk_widget_set_sensitive(downbut, row < nrows - 1);
502
503 /* Store any edited data from old-selected row */
504 if (oldsel >= 0) {
505 for (listpt = configlist; listpt; listpt = g_slist_next(listpt)) {
506 struct ConfigWidget *conf = (struct ConfigWidget *)listpt->data;
507 struct GLOBALS *gvar;
508
509 gvar = &Globals[conf->globind];
510
511 if (gvar->NameStruct && strcmp(structname, gvar->NameStruct) == 0) {
512 g_free(conf->data[oldsel]);
513 conf->data[oldsel] = NULL;
514
515 if (gvar->BoolVal) {
516 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(conf->widget))) {
517 conf->data[oldsel] = g_strdup("1");
518 }
519 } else {
520 conf->data[oldsel] = gtk_editable_get_chars(
521 GTK_EDITABLE(conf->widget), 0, -1);
522 gtk_entry_set_text(GTK_ENTRY(conf->widget), "");
523 }
524 if (strcmp(gvar->Name, "Name") == 0) {
525 GtkTreeIter ositer;
526 g_assert(gtk_tree_model_iter_nth_child(model, &ositer, NULL, oldsel));
527 gtk_list_store_set(GTK_LIST_STORE(model), &ositer, 0,
528 conf->data[oldsel], -1);
529 }
530 }
531 }
532 }
533 g_object_set_data(G_OBJECT(model), "oldsel", GINT_TO_POINTER(row));
534
535 /* Update widgets with selected row */
536 if (row >= 0) {
537 for (listpt = configlist; listpt; listpt = g_slist_next(listpt)) {
538 struct ConfigWidget *conf = (struct ConfigWidget *)listpt->data;
539 struct GLOBALS *gvar;
540
541 gvar = &Globals[conf->globind];
542
543 if (gvar->NameStruct && strcmp(structname, gvar->NameStruct) == 0) {
544 if (gvar->BoolVal) {
545 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(conf->widget),
546 conf->data[row] != NULL);
547 } else {
548 gtk_entry_set_text(GTK_ENTRY(conf->widget), conf->data[row]);
549 }
550 }
551 }
552 }
553 }
554
555 static void sound_sel_changed(GtkTreeSelection *treesel, gpointer data)
556 {
557 GtkTreeModel *model;
558 GtkTreeIter iter;
559 GtkWidget *entry;
560 int row, oldsel, globind;
561
562 row = get_tree_selection_row_index(treesel, &model);
563 oldsel = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model), "oldsel"));
564 entry = GTK_WIDGET(g_object_get_data(G_OBJECT(model), "entry"));
565
566 /* Store any edited data from old-selected row */
567 if (oldsel >= 0) {
568 gchar *text, **oldtext;
569 g_assert(gtk_tree_model_iter_nth_child(model, &iter, NULL, oldsel));
570 gtk_tree_model_get(model, &iter, 2, &globind, -1);
571 g_assert(globind >=0 && globind < NUMGLOB);
572
573 text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
574 oldtext = GetGlobalString(globind, 0);
575 g_assert(text && oldtext);
576 if (strcmp(text, *oldtext) != 0) {
577 AssignName(GetGlobalString(globind, 0), text);
578 Globals[globind].Modified = TRUE;
579 }
580 gtk_entry_set_text(GTK_ENTRY(entry), "");
581 g_free(text);
582 }
583
584 g_object_set_data(G_OBJECT(model), "oldsel", GINT_TO_POINTER(row));
585 /* Update new selection */
586 if (row >= 0) {
587 gchar **text;
588 g_assert(gtk_tree_model_iter_nth_child(model, &iter, NULL, row));
589 gtk_tree_model_get(model, &iter, 2, &globind, -1);
590 g_assert(globind >=0 && globind < NUMGLOB);
591 text = GetGlobalString(globind, 0);
592 gtk_entry_set_text(GTK_ENTRY(entry), *text);
593 }
594 }
595
596 static void BrowseSound(GtkWidget *entry)
597 {
598 gchar *oldtext, *newtext;
599 GtkWidget *dialog = gtk_widget_get_ancestor(entry, GTK_TYPE_WINDOW);
600
601 oldtext = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
602
603 newtext = GtkGetFile(dialog, oldtext, _("Select sound file"));
604 g_free(oldtext);
605 if (newtext) {
606 gtk_entry_set_text(GTK_ENTRY(entry), newtext);
607 g_free(newtext);
608 }
609 }
610
611 static void TestPlaySound(GtkWidget *entry)
612 {
613 gchar *text;
614 gboolean sound_enabled;
615
616 text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
617 sound_enabled = IsSoundEnabled();
618 SoundEnable(TRUE);
619 SoundPlay(text);
620 SoundEnable(sound_enabled);
621 g_free(text);
622 }
623
624 static void OKCallback(GtkWidget *widget, GtkWidget *dialog)
625 {
626 GtkToggleButton *unicode_check;
627
628 SaveConfigWidgets();
629 unicode_check = GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(dialog),
630 "unicode_check"));
631 UpdateConfigFile(NULL, gtk_toggle_button_get_active(unicode_check));
632 gtk_widget_destroy(dialog);
633 }
634
635 static gchar *GetHelpPage(const gchar *pagename)
636 {
637 gchar *root, *file;
638
639 root = GetDocRoot();
640 file = g_strdup_printf("%shelp/%s.html", root, pagename);
641 g_free(root);
642 return file;
643 }
644
645 static void HelpCallback(GtkWidget *widget, GtkWidget *notebook)
646 {
647 const static gchar *pagehelp[] = {
648 "general", "locations", "drugs", "guns", "cops", "server", "sounds"
649 };
650 gint page = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
651 gchar *help;
652
653 help = GetHelpPage(pagehelp[page]);
654 DisplayHTML(widget, OurWebBrowser, help);
655 g_free(help);
656 }
657
658 static void FinishOptDialog(GtkWidget *widget, gpointer data)
659 {
660 FreeConfigWidgets();
661
662 g_slist_free(clists);
663 clists = NULL;
664 }
665
666 static GtkWidget *CreateList(gchar *structname, struct ConfigMembers *members)
667 {
668 GtkWidget *hbox, *vbox, *hbbox, *tv, *scrollwin, *button, *grid;
669 GtkTreeSelection *treesel;
670 GtkListStore *store;
671 GtkCellRenderer *renderer;
672 GtkTreeIter iter;
673
674 int ind, minlistlength = 0;
675 gint i, nummembers;
676 struct GLOBALS *gvar;
677 struct ConfigMembers namemember = { N_("Name"), "Name" };
678
679 ind = GetGlobalIndex(structname, "Name");
680 g_assert(ind >= 0);
681 gvar = &Globals[ind];
682 g_assert(gvar->StringVal && gvar->MaxIndex);
683
684 for (i = 0; i < NUMGLOB; i++) {
685 if (Globals[i].StructListPt == gvar->StructListPt
686 && Globals[i].ResizeFunc) {
687 minlistlength = Globals[i].MinVal;
688 }
689 }
690
691 nummembers = 0;
692 while (members && members[nummembers].label) {
693 nummembers++;
694 }
695
696 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
697
698 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
699
700 tv = gtk_scrolled_tree_view_new(&scrollwin);
701 store = gtk_list_store_new(1, G_TYPE_STRING);
702 gtk_tree_view_set_model(GTK_TREE_VIEW(tv), GTK_TREE_MODEL(store));
703 renderer = gtk_cell_renderer_text_new();
704 gtk_tree_view_insert_column_with_attributes(
705 GTK_TREE_VIEW(tv), -1, structname, renderer, "text", 0, NULL);
706 g_object_unref(store);
707 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(tv), FALSE);
708
709 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv));
710 g_signal_connect(G_OBJECT(treesel), "changed", G_CALLBACK(list_sel_changed),
711 structname);
712 gtk_tree_selection_set_mode(treesel, GTK_SELECTION_SINGLE);
713 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store),
714 GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
715
716 clists = g_slist_append(clists, tv);
717
718 for (i = 1; i <= *gvar->MaxIndex; i++) {
719 gtk_list_store_append(store, &iter);
720 gtk_list_store_set(store, &iter, 0, *GetGlobalString(ind, i), -1);
721 }
722 gtk_box_pack_start(GTK_BOX(vbox), scrollwin, TRUE, TRUE, 0);
723
724 hbbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
725 gtk_box_set_homogeneous(GTK_BOX(hbbox), TRUE);
726 g_object_set_data(G_OBJECT(store), "oldsel", GINT_TO_POINTER(-1));
727
728 button = gtk_button_new_with_label(_("New"));
729 g_object_set_data(G_OBJECT(button), "treeview", tv);
730 g_signal_connect(G_OBJECT(button), "clicked",
731 G_CALLBACK(list_new), structname);
732 gtk_box_pack_start(GTK_BOX(hbbox), button, TRUE, TRUE, 0);
733
734 button = gtk_button_new_with_label(_("Delete"));
735 gtk_widget_set_sensitive(button, FALSE);
736 g_object_set_data(G_OBJECT(button), "treeview", tv);
737 g_object_set_data(G_OBJECT(store), "delete", button);
738 g_object_set_data(G_OBJECT(store), "minlistlength",
739 GINT_TO_POINTER(minlistlength));
740 g_signal_connect(G_OBJECT(button), "clicked",
741 G_CALLBACK(list_delete), structname);
742 gtk_box_pack_start(GTK_BOX(hbbox), button, TRUE, TRUE, 0);
743
744 button = gtk_button_new_with_label(_("Up"));
745 gtk_widget_set_sensitive(button, FALSE);
746 g_object_set_data(G_OBJECT(button), "treeview", tv);
747 g_object_set_data(G_OBJECT(store), "up", button);
748 g_signal_connect(G_OBJECT(button), "clicked",
749 G_CALLBACK(list_up), structname);
750 gtk_box_pack_start(GTK_BOX(hbbox), button, TRUE, TRUE, 0);
751
752 button = gtk_button_new_with_label(_("Down"));
753 gtk_widget_set_sensitive(button, FALSE);
754 g_object_set_data(G_OBJECT(button), "treeview", tv);
755 g_object_set_data(G_OBJECT(store), "down", button);
756 g_signal_connect(G_OBJECT(button), "clicked",
757 G_CALLBACK(list_down), structname);
758 gtk_box_pack_start(GTK_BOX(hbbox), button, TRUE, TRUE, 0);
759
760 gtk_box_pack_start(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
761 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
762
763 grid = dp_gtk_grid_new(nummembers + 1, 2, FALSE);
764 gtk_grid_set_row_spacing(GTK_GRID(grid), 5);
765 gtk_grid_set_column_spacing(GTK_GRID(grid), 5);
766
767 AddStructConfig(grid, 0, structname, &namemember);
768 for (i = 0; i < nummembers; i++) {
769 AddStructConfig(grid, i + 1, structname, &members[i]);
770 }
771
772 gtk_box_pack_start(GTK_BOX(hbox), grid, TRUE, TRUE, 0);
773
774 return hbox;
775 }
776
777 static void FillSoundsList(GtkTreeView *tv)
778 {
779 GtkListStore *store;
780 GtkTreeIter iter;
781 gint i;
782
783 /* Don't update the widget until we're done */
784 store = GTK_LIST_STORE(gtk_tree_view_get_model(tv));
785 g_object_ref(store);
786 gtk_tree_view_set_model(tv, NULL);
787
788 gtk_list_store_clear(store);
789 for (i = 0; i < NUMGLOB; i++) {
790 if (strlen(Globals[i].Name) > 7
791 && strncmp(Globals[i].Name, "Sounds.", 7) == 0) {
792 gtk_list_store_append(store, &iter);
793 gtk_list_store_set(store, &iter, 0, &Globals[i].Name[7],
794 1, _(Globals[i].Help), 2, i, -1);
795 }
796 }
797
798 gtk_tree_view_set_model(tv, GTK_TREE_MODEL(store));
799 }
800
801 void OptDialog(GtkWidget *widget, gpointer data)
802 {
803 GtkWidget *dialog, *notebook, *label, *check, *entry, *grid;
804 GtkWidget *hbox, *vbox, *vbox2, *hsep, *button, *hbbox, *tv;
805 GtkWidget *scrollwin;
806 GtkAccelGroup *accel_group;
807 gchar *sound_titles[2];
808 GtkCellRenderer *renderer;
809 GtkListStore *store;
810 GtkTreeSelection *treesel;
811
812 struct ConfigMembers locmembers[] = {
813 { N_("Police presence"), "PolicePresence" },
814 { N_("Minimum no. of drugs"), "MinDrug" },
815 { N_("Maximum no. of drugs"), "MaxDrug" },
816 { NULL, NULL }
817 };
818 struct ConfigMembers drugmembers[] = {
819 { N_("Minimum normal price"), "MinPrice" },
820 { N_("Maximum normal price"), "MaxPrice" },
821 { N_("Can be specially cheap"), "Cheap" },
822 { N_("Cheap string"), "CheapStr" },
823 { N_("Can be specially expensive"), "Expensive" },
824 { NULL, NULL }
825 };
826 struct ConfigMembers gunmembers[] = {
827 { N_("Price"), "Price" },
828 { N_("Inventory space"), "Space" },
829 { N_("Damage"), "Damage" },
830 { NULL, NULL }
831 };
832 struct ConfigMembers copmembers[] = {
833 { N_("Name of one deputy"), "DeputyName" },
834 { N_("Name of several deputies"), "DeputiesName" },
835 { N_("Minimum no. of deputies"), "MinDeputies" },
836 { N_("Maximum no. of deputies"), "MaxDeputies" },
837 { N_("Cop armor"), "Armor" },
838 { N_("Deputy armor"), "DeputyArmor" },
839 { NULL, NULL }
840 };
841
842 dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
843 accel_group = gtk_accel_group_new();
844 gtk_window_add_accel_group(GTK_WINDOW(dialog), accel_group);
845
846 gtk_window_set_title(GTK_WINDOW(dialog), _("Options"));
847 my_set_dialog_position(GTK_WINDOW(dialog));
848 gtk_container_set_border_width(GTK_CONTAINER(dialog), 7);
849 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
850
851 gtk_window_set_transient_for(GTK_WINDOW(dialog),
852 GTK_WINDOW(MainWindow));
853
854 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 7);
855
856 notebook = gtk_notebook_new();
857
858 grid = dp_gtk_grid_new(8, 3, FALSE);
859 gtk_grid_set_row_spacing(GTK_GRID(grid), 5);
860 gtk_grid_set_column_spacing(GTK_GRID(grid), 5);
861
862 check = NewConfigCheck("Sanitized", _("Remove drug references"));
863 dp_gtk_grid_attach(GTK_GRID(grid), check, 0, 0, 1, 1, FALSE);
864
865 check = gtk_check_button_new_with_label(_("Unicode config file"));
866 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), IsConfigFileUTF8());
867 dp_gtk_grid_attach(GTK_GRID(grid), check, 1, 0, 2, 1, TRUE);
868 g_object_set_data(G_OBJECT(dialog), "unicode_check", check);
869
870 label = gtk_label_new(_("Game length (turns)"));
871 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1, FALSE);
872 entry = NewConfigEntry("NumTurns");
873 dp_gtk_grid_attach(GTK_GRID(grid), entry, 1, 1, 2, 1, TRUE);
874
875 label = gtk_label_new(_("Starting cash"));
876 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 2, 1, 1, FALSE);
877 entry = NewConfigEntry("StartCash");
878 dp_gtk_grid_attach(GTK_GRID(grid), entry, 1, 2, 2, 1, TRUE);
879
880 label = gtk_label_new(_("Starting debt"));
881 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 3, 1, 1, FALSE);
882 entry = NewConfigEntry("StartDebt");
883 dp_gtk_grid_attach(GTK_GRID(grid), entry, 1, 3, 2, 1, TRUE);
884
885 label = gtk_label_new(_("Currency symbol"));
886 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 4, 1, 1, FALSE);
887 entry = NewConfigEntry("Currency.Symbol");
888 dp_gtk_grid_attach(GTK_GRID(grid), entry, 1, 4, 1, 1, TRUE);
889 check = NewConfigCheck("Currency.Prefix", _("Symbol prefixes prices"));
890 dp_gtk_grid_attach(GTK_GRID(grid), check, 2, 4, 1, 1, TRUE);
891
892 label = gtk_label_new(_("Name of one bitch"));
893 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 5, 1, 1, FALSE);
894 entry = NewConfigEntry("Names.Bitch");
895 dp_gtk_grid_attach(GTK_GRID(grid), entry, 1, 5, 2, 1, TRUE);
896
897 label = gtk_label_new(_("Name of several bitches"));
898 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 6, 1, 1, FALSE);
899 entry = NewConfigEntry("Names.Bitches");
900 dp_gtk_grid_attach(GTK_GRID(grid), entry, 1, 6, 2, 1, TRUE);
901
902 #ifndef CYGWIN
903 label = gtk_label_new(_("Web browser"));
904 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 7, 1, 1, FALSE);
905 entry = NewConfigEntry("WebBrowser");
906 dp_gtk_grid_attach(GTK_GRID(grid), entry, 1, 7, 2, 1, TRUE);
907 #endif
908
909 gtk_container_set_border_width(GTK_CONTAINER(grid), 7);
910 label = gtk_label_new(_("General"));
911 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), grid, label);
912
913 hbox = CreateList("Location", locmembers);
914 gtk_container_set_border_width(GTK_CONTAINER(hbox), 7);
915
916 label = gtk_label_new(_("Locations"));
917 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, label);
918
919 vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
920 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 7);
921
922 hbox = CreateList("Drug", drugmembers);
923 gtk_box_pack_start(GTK_BOX(vbox2), hbox, TRUE, TRUE, 0);
924
925 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
926 gtk_box_pack_start(GTK_BOX(vbox2), hsep, FALSE, FALSE, 0);
927
928 grid = dp_gtk_grid_new(2, 2, FALSE);
929 gtk_grid_set_row_spacing(GTK_GRID(grid), 5);
930 gtk_grid_set_column_spacing(GTK_GRID(grid), 5);
931 label = gtk_label_new(_("Expensive string 1"));
932 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1, FALSE);
933 entry = NewConfigEntry("Drugs.ExpensiveStr1");
934 dp_gtk_grid_attach(GTK_GRID(grid), entry, 1, 0, 1, 1, TRUE);
935
936 label = gtk_label_new(_("Expensive string 2"));
937 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1, FALSE);
938 entry = NewConfigEntry("Drugs.ExpensiveStr2");
939 dp_gtk_grid_attach(GTK_GRID(grid), entry, 1, 1, 1, 1, TRUE);
940 gtk_box_pack_start(GTK_BOX(vbox2), grid, FALSE, FALSE, 0);
941
942 label = gtk_label_new(_("Drugs"));
943 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, label);
944
945 hbox = CreateList("Gun", gunmembers);
946 gtk_container_set_border_width(GTK_CONTAINER(hbox), 7);
947 label = gtk_label_new(_("Guns"));
948 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, label);
949
950 hbox = CreateList("Cop", copmembers);
951 gtk_container_set_border_width(GTK_CONTAINER(hbox), 7);
952 label = gtk_label_new(_("Cops"));
953 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, label);
954
955 #ifdef NETWORKING
956 grid = dp_gtk_grid_new(6, 4, FALSE);
957 gtk_grid_set_row_spacing(GTK_GRID(grid), 5);
958 gtk_grid_set_column_spacing(GTK_GRID(grid), 5);
959
960 check = NewConfigCheck("MetaServer.Active",
961 _("Server reports to metaserver"));
962 dp_gtk_grid_attach(GTK_GRID(grid), check, 0, 0, 2, 1, TRUE);
963
964 #ifdef CYGWIN
965 check = NewConfigCheck("MinToSysTray", _("Minimize to System Tray"));
966 dp_gtk_grid_attach(GTK_GRID(grid), check, 2, 0, 2, 1, TRUE);
967 #endif
968
969 label = gtk_label_new(_("Metaserver URL"));
970 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1, FALSE);
971 entry = NewConfigEntry("MetaServer.URL");
972 dp_gtk_grid_attach(GTK_GRID(grid), entry, 1, 1, 3, 1, TRUE);
973
974 label = gtk_label_new(_("Comment"));
975 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 4, 1, 1, FALSE);
976 entry = NewConfigEntry("MetaServer.Comment");
977 dp_gtk_grid_attach(GTK_GRID(grid), entry, 1, 4, 3, 1, TRUE);
978
979 label = gtk_label_new(_("MOTD (welcome message)"));
980 dp_gtk_grid_attach(GTK_GRID(grid), label, 0, 5, 1, 1, FALSE);
981 entry = NewConfigEntry("ServerMOTD");
982 dp_gtk_grid_attach(GTK_GRID(grid), entry, 1, 5, 3, 1, TRUE);
983
984 gtk_container_set_border_width(GTK_CONTAINER(grid), 7);
985 label = gtk_label_new(_("Server"));
986 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), grid, label);
987 #endif
988
989 vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
990 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 7);
991
992 sound_titles[0] = _("Sound name");
993 sound_titles[1] = _("Description");
994 tv = gtk_scrolled_tree_view_new(&scrollwin);
995 store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
996 g_object_set_data(G_OBJECT(store), "oldsel", GINT_TO_POINTER(-1));
997 gtk_tree_view_set_model(GTK_TREE_VIEW(tv), GTK_TREE_MODEL(store));
998 renderer = gtk_cell_renderer_text_new();
999 gtk_tree_view_insert_column_with_attributes(
1000 GTK_TREE_VIEW(tv), -1, sound_titles[0], renderer,
1001 "text", 0, NULL);
1002 gtk_tree_view_insert_column_with_attributes(
1003 GTK_TREE_VIEW(tv), -1, sound_titles[1], renderer,
1004 "text", 1, NULL);
1005 g_object_unref(store);
1006 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(tv), FALSE);
1007 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv));
1008 gtk_tree_selection_set_mode(treesel, GTK_SELECTION_SINGLE);
1009
1010 FillSoundsList(GTK_TREE_VIEW(tv));
1011 g_signal_connect(G_OBJECT(treesel), "changed",
1012 G_CALLBACK(sound_sel_changed), NULL);
1013
1014 clists = g_slist_append(clists, tv);
1015
1016 gtk_box_pack_start(GTK_BOX(vbox2), scrollwin, TRUE, TRUE, 0);
1017
1018 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
1019 label = gtk_label_new(_("Sound file"));
1020 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1021
1022 entry = gtk_entry_new();
1023 g_object_set_data(G_OBJECT(store), "entry", entry);
1024 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
1025
1026 button = gtk_button_new_with_label(_("Browse..."));
1027 g_signal_connect_swapped(G_OBJECT(button), "clicked",
1028 G_CALLBACK(BrowseSound), G_OBJECT(entry));
1029 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1030
1031 button = gtk_button_new_with_label(_("Play"));
1032 g_signal_connect_swapped(G_OBJECT(button), "clicked",
1033 G_CALLBACK(TestPlaySound), G_OBJECT(entry));
1034 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1035
1036 gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
1037
1038 label = gtk_label_new(_("Sounds"));
1039 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, label);
1040
1041 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 0);
1042
1043 gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
1044
1045 hsep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1046 gtk_box_pack_start(GTK_BOX(vbox), hsep, FALSE, FALSE, 0);
1047
1048 hbbox = my_hbbox_new();
1049
1050 button = gtk_button_new_with_mnemonic(_("_OK"));
1051 g_signal_connect(G_OBJECT(button), "clicked",
1052 G_CALLBACK(OKCallback), (gpointer)dialog);
1053 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button);
1054
1055 button = gtk_button_new_with_mnemonic(_("_Help"));
1056 g_signal_connect(G_OBJECT(button), "clicked",
1057 G_CALLBACK(HelpCallback), (gpointer)notebook);
1058 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button);
1059
1060 button = gtk_button_new_with_mnemonic(_("_Cancel"));
1061 g_signal_connect_swapped(G_OBJECT(button), "clicked",
1062 G_CALLBACK(gtk_widget_destroy),
1063 G_OBJECT(dialog));
1064 g_signal_connect(G_OBJECT(dialog), "destroy",
1065 G_CALLBACK(FinishOptDialog), NULL);
1066 my_gtk_box_pack_start_defaults(GTK_BOX(hbbox), button);
1067
1068 gtk_box_pack_start(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
1069
1070 gtk_container_add(GTK_CONTAINER(dialog), vbox);
1071
1072 gtk_widget_show_all(dialog);
1073 }