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 #include <string.h>
21 #include <math.h> /* sqrt */
22
23 #include <gtk/gtk.h>
24 #include "gtkpixcomm.h"
25
26 /* utility */
27 #include "fcintl.h"
28 #include "mem.h"
29 #include "shared.h"
30 #include "support.h"
31
32 /* common */
33 #include "city.h"
34 #include "game.h"
35 #include "government.h"
36 #include "movement.h"
37 #include "specialist.h"
38 #include "tech.h"
39 #include "unit.h"
40 #include "map.h"
41 #include "research.h"
42 #include "version.h"
43
44 /* client */
45 #include "client_main.h"
46 #include "climisc.h"
47 #include "colors.h"
48 #include "graphics.h"
49 #include "gui_main.h"
50 #include "gui_stuff.h"
51 #include "helpdata.h"
52 #include "options.h"
53 #include "tilespec.h"
54
55 #include "helpdlg.h"
56
57 #define TECH_TREE_DEPTH 20
58
59 /*
60 * Globals.
61 */
62 static GtkWidget *help_dialog_shell;
63 static GtkWidget *help_view_sw;
64
65 static GtkWidget *help_view;
66
67 static GtkWidget *help_frame;
68 static GtkTextBuffer *help_text;
69 static GtkWidget *help_text_sw;
70 static GtkWidget *help_vbox;
71 static GtkWidget *help_tile;
72 static GtkWidget *help_box;
73 static GtkWidget *help_itable;
74 static GtkWidget *help_wtable;
75 static GtkWidget *help_utable;
76 static GtkWidget *help_ttable;
77 static GtkWidget *help_etable;
78 static GtkWidget *help_tree;
79 static GtkTreeStore *tstore;
80
81 static GtkWidget *help_tree_sw;
82 static GtkWidget *help_tree_expand;
83 static GtkWidget *help_tree_collapse;
84 static GtkWidget *help_tree_buttons_hbox;
85 static GtkWidget *help_ilabel[6];
86 static GtkWidget *help_wlabel[6];
87 static GtkWidget *help_ulabel[5][5];
88 static GtkWidget *help_tlabel[2][5];
89 static GtkWidget *help_elabel[6];
90
91 static bool help_advances[A_LAST];
92
93 static GPtrArray *help_history;
94 static int help_history_pos;
95
96
97 static const char *help_ilabel_name[6] =
98 { N_("Cost:"), NULL, N_("Upkeep:"), NULL, N_("Requirement:"), NULL };
99
100 static const char *help_wlabel_name[6] =
101 { N_("Cost:"), NULL, N_("Requirement:"), NULL, N_("Obsolete by:"), NULL };
102
103 static const char *help_ulabel_name[5][5] =
104 {
105 { N_("Cost:"), NULL, NULL, N_("Attack:"), NULL },
106 { N_("Defense:"), NULL, NULL, N_("Move:") , NULL },
107 { N_("FirePower:"), NULL, NULL, N_("Hitpoints:"), NULL },
108 { N_("Basic Upkeep:"), NULL, NULL, N_("Vision:"), NULL },
109 { N_("Requirement:"), NULL, NULL, N_("Obsolete by:"), NULL }
110 };
111
112 static const char *help_tlabel_name[2][5] =
113 {
114 { N_("Move/Defense:"), NULL, NULL, N_("Food/Res/Trade:"), NULL },
115 { N_("Resources:"), NULL, NULL, NULL, NULL }
116 };
117
118 static const char *help_elabel_name[6] =
119 /* TRANS: Label for build cost for extras in help. Will be followed by
120 * something like "3 MP" (where MP = Movement Points) */
121 { N_("Build:"), NULL,
122 /* TRANS: Extra conflicts in help. Will be followed by a list of extras
123 * that can't be built on the same tile as this one. */
124 N_("Conflicts with:"), NULL,
125 /* TRANS: Extra bonus in help. Will be followed by food/production/trade
126 * stats like "0/0/+1", "0/+50%/0" */
127 N_("Bonus (F/P/T):"), NULL };
128
129 #define REQ_LABEL_NONE _("?tech:None")
130 #define REQ_LABEL_NEVER _("(Never)")
131
132 static void create_help_dialog(void);
133 static void help_update_dialog(const struct help_item *pitem);
134 static void create_help_page(enum help_page_type type);
135
136 static void select_help_item_string(const char *item,
137 enum help_page_type htype);
138 static void help_command_update(void);
139 static void help_command_callback(GtkWidget *w, gint response_id);
140
141 /****************************************************************
142 Set topic specific title for help_frame
143 *****************************************************************/
set_title_topic(char * topic)144 static void set_title_topic(char *topic)
145 {
146 if (strcmp(topic, _(HELP_ABOUT_ITEM)) == 0) {
147 gtk_frame_set_label(GTK_FRAME(help_frame), freeciv_name_version());
148 } else {
149 gtk_frame_set_label(GTK_FRAME(help_frame), topic);
150 }
151 return;
152 }
153
154 /****************************************************************
155 Close help dialog
156 *****************************************************************/
popdown_help_dialog(void)157 void popdown_help_dialog(void)
158 {
159 if(help_dialog_shell) {
160 gtk_widget_destroy(help_dialog_shell);
161 }
162 }
163
164 /****************************************************************
165 Popup help dialog for given item of given type.
166 *****************************************************************/
popup_help_dialog_typed(const char * item,enum help_page_type htype)167 void popup_help_dialog_typed(const char *item, enum help_page_type htype)
168 {
169 if(!help_dialog_shell) {
170 create_help_dialog();
171 gtk_set_relative_position(toplevel, help_dialog_shell, 10, 10);
172 }
173 gtk_window_present(GTK_WINDOW(help_dialog_shell));
174
175 select_help_item_string(item, htype);
176 }
177
178
179 /****************************************************************
180 Not sure if this should call Q_(item) as it does, or whether all
181 callers of this function should do so themselves... --dwp
182 *****************************************************************/
popup_help_dialog_string(const char * item)183 void popup_help_dialog_string(const char *item)
184 {
185 popup_help_dialog_typed(Q_(item), HELP_ANY);
186 }
187
188 /**************************************************************************
189 Called by help_update_tech and itself
190 Creates a node in the given tree for the given tech, and creates child
191 nodes for any children it has up to levels deep. These are then expanded
192 if they are less than expanded_levels deep. Avoids generating redundant
193 subtrees, so that if Literacy occurs twice in a tech tree, only the first
194 will have children. Color codes the node based on when it will be
195 discovered: red >2 turns, yellow 1 turn, green 0 turns (discovered).
196 **************************************************************************/
create_tech_tree(int tech,int levels,GtkTreeIter * parent)197 static void create_tech_tree(int tech, int levels, GtkTreeIter *parent)
198 {
199 const struct research *presearch;
200 int bg;
201 int turns_to_tech;
202 bool original;
203 GtkTreeIter l;
204 GValue value = { 0, };
205
206 if (advance_required(tech, AR_ONE) == A_LAST
207 && advance_required(tech, AR_TWO) == A_LAST) {
208 bg = COLOR_REQTREE_UNKNOWN;
209
210 gtk_tree_store_append(tstore, &l, parent);
211 help_advances[tech] = TRUE;
212
213 g_value_init(&value, G_TYPE_STRING);
214 g_value_set_static_string(&value, _("Removed"));
215 gtk_tree_store_set_value(tstore, &l, 0, &value);
216 g_value_unset(&value);
217
218 gtk_tree_store_set(tstore, &l,
219 1, -1,
220 2, tech,
221 3, &get_color(tileset, bg)->color
222 -1);
223 return;
224 }
225
226 presearch = research_get(client_player());
227
228 bg = COLOR_REQTREE_BACKGROUND;
229 switch (research_invention_state(presearch, tech)) {
230 case TECH_UNKNOWN:
231 bg = COLOR_REQTREE_UNKNOWN;
232 break;
233 case TECH_KNOWN:
234 bg = COLOR_REQTREE_KNOWN;
235 break;
236 case TECH_PREREQS_KNOWN:
237 bg = COLOR_REQTREE_PREREQS_KNOWN;
238 break;
239 }
240 turns_to_tech = research_goal_unknown_techs(presearch, tech);
241
242 /* l is the original in the tree. */
243 original = !help_advances[tech];
244
245 gtk_tree_store_append(tstore, &l, parent);
246 help_advances[tech] = TRUE;
247
248 g_value_init(&value, G_TYPE_STRING);
249 g_value_set_static_string(&value,
250 research_advance_name_translation(presearch,
251 tech));
252 gtk_tree_store_set_value(tstore, &l, 0, &value);
253 g_value_unset(&value);
254
255 gtk_tree_store_set(tstore, &l,
256 1, turns_to_tech,
257 2, tech,
258 3, &get_color(tileset, bg)->color,
259 -1);
260
261 if (--levels <= 0)
262 return;
263
264 if (original) {
265 /* only add children to orginals */
266 if (advance_required(tech, AR_ONE) != A_NONE)
267 create_tech_tree(advance_required(tech, AR_ONE), levels, &l);
268 if (advance_required(tech, AR_TWO) != A_NONE)
269 create_tech_tree(advance_required(tech, AR_TWO), levels, &l);
270 }
271 return;
272 }
273
274 /**************************************************************************
275 Selects the help page for the tech in the tree that was double clicked.
276 **************************************************************************/
help_tech_tree_activated_callback(GtkTreeView * view,GtkTreePath * path,GtkTreeViewColumn * col,gpointer data)277 static void help_tech_tree_activated_callback(GtkTreeView *view,
278 GtkTreePath *path,
279 GtkTreeViewColumn *col,
280 gpointer data)
281 {
282 GtkTreeIter it;
283 gint tech;
284
285 gtk_tree_model_get_iter(GTK_TREE_MODEL(tstore), &it, path);
286 gtk_tree_model_get(GTK_TREE_MODEL(tstore), &it, 2, &tech, -1);
287 select_help_item_string(advance_name_translation(advance_by_number(tech)),
288 HELP_TECH);
289 }
290
291 /**************************************************************************
292 Called when "Expand All" button is clicked
293 **************************************************************************/
help_tech_tree_expand_callback(GtkWidget * w,gpointer data)294 static void help_tech_tree_expand_callback(GtkWidget *w, gpointer data)
295 {
296 gtk_tree_view_expand_all(GTK_TREE_VIEW(data));
297 }
298
299 /**************************************************************************
300 Called when "Collapse All" button is clicked
301 **************************************************************************/
help_tech_tree_collapse_callback(GtkWidget * w,gpointer data)302 static void help_tech_tree_collapse_callback(GtkWidget *w, gpointer data)
303 {
304 gtk_tree_view_collapse_all(GTK_TREE_VIEW(data));
305 }
306
307 /**************************************************************************
308 Hyperlink clicked
309 **************************************************************************/
help_hyperlink_callback(GtkWidget * w)310 static void help_hyperlink_callback(GtkWidget *w)
311 {
312 const char *s;
313 enum help_page_type type;
314
315 s=gtk_label_get_text(GTK_LABEL(w));
316 type=GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(w), "page_type"));
317
318 /* FIXME: May be able to skip, or may need to modify, advances[A_NONE]
319 below, depending on which i18n is done elsewhere.
320 */
321 if (strcmp(s, REQ_LABEL_NEVER) != 0
322 && strcmp(s, skip_intl_qualifier_prefix(REQ_LABEL_NONE)) != 0
323 && strcmp(s, advance_name_translation(advance_by_number(A_NONE))) != 0)
324 select_help_item_string(s, type);
325 }
326
327 /**************************************************************************
328 Create new hyperlink button
329 **************************************************************************/
help_hyperlink_new(GtkWidget * label,enum help_page_type type)330 static GtkWidget *help_hyperlink_new(GtkWidget *label, enum help_page_type type)
331 {
332 GtkWidget *button;
333
334 button = gtk_button_new();
335 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
336 gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5);
337 gtk_widget_set_name(label, "help_link");
338 gtk_container_add(GTK_CONTAINER(button), label);
339 gtk_widget_show(button);
340 g_signal_connect_swapped(button, "clicked",
341 G_CALLBACK(help_hyperlink_callback), label);
342 g_object_set_data(G_OBJECT(label), "page_type", GUINT_TO_POINTER(type));
343 return button;
344 }
345
346 /**************************************************************************
347 Create new hyperlink button with text
348 **************************************************************************/
help_slink_new(const gchar * txt,enum help_page_type type)349 static GtkWidget *help_slink_new(const gchar *txt, enum help_page_type type)
350 {
351 GtkWidget *button, *label;
352
353 label = gtk_label_new(txt);
354 gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5);
355 button = help_hyperlink_new(label, type);
356 return button;
357 }
358
359 /**************************************************************************
360 Hide help box
361 **************************************************************************/
help_box_hide(void)362 static void help_box_hide(void)
363 {
364 gtk_widget_hide(help_box);
365
366 gtk_widget_hide(help_tile);
367
368 gtk_widget_hide(help_itable);
369 gtk_widget_hide(help_wtable);
370 gtk_widget_hide(help_utable);
371 gtk_widget_hide(help_ttable);
372 gtk_widget_hide(help_etable);
373
374 gtk_widget_hide(help_tile); /* FIXME: twice? */
375
376 gtk_widget_hide(help_vbox);
377 gtk_widget_hide(help_text_sw);
378
379 gtk_widget_hide(help_tree_sw);
380 gtk_widget_hide(help_tree_buttons_hbox);
381 }
382
383 /**************************************************************************
384 Completely destory help dialog
385 **************************************************************************/
help_destroy_callback(GtkWidget * w,gpointer data)386 static void help_destroy_callback(GtkWidget *w, gpointer data)
387 {
388 g_ptr_array_free(help_history, TRUE);
389 help_dialog_shell = NULL;
390 }
391
392 /**************************************************************************
393 New topic activated from help dialog
394 **************************************************************************/
activated_topic(GtkTreeView * view,gpointer data)395 static void activated_topic(GtkTreeView *view, gpointer data)
396 {
397 GtkTreePath *path;
398 GtkTreeViewColumn *col;
399 GtkTreeModel *model;
400 GtkTreeIter it;
401 struct help_item *pitem;
402
403 model = gtk_tree_view_get_model(view);
404
405 gtk_tree_view_get_cursor(view, &path, &col);
406 gtk_tree_model_get_iter(model, &it, path);
407 gtk_tree_path_free(path);
408
409 if (!path) {
410 return;
411 }
412
413 gtk_tree_model_get(model, &it, 1, &pitem, -1);
414
415 if (help_history_pos >= 0 &&
416 g_ptr_array_index(help_history, help_history_pos) == (gpointer) pitem) {
417 return;
418 }
419
420 help_update_dialog(pitem);
421
422 /* add to history. */
423 if (help_history_pos < help_history->len - 1) {
424 g_ptr_array_set_size(help_history, help_history_pos + 1);
425 }
426 help_history_pos++;
427
428 g_ptr_array_add(help_history, (gpointer)pitem);
429 help_command_update();
430 }
431
432 /**************************************************************************
433 Create help dialog
434 **************************************************************************/
create_help_dialog(void)435 static void create_help_dialog(void)
436 {
437 GtkWidget *hbox;
438 GtkWidget *button;
439 GtkWidget *text;
440 int i, j;
441 GtkCellRenderer *rend;
442 GtkTreeViewColumn *col;
443 GArray *array;
444 GtkTreeStore *store;
445 GtkTreeSelection *selection;
446
447 help_history = g_ptr_array_new();
448 help_history_pos = -1;
449
450 help_dialog_shell = gtk_dialog_new_with_buttons(_("Freeciv Help Browser"),
451 NULL,
452 0,
453 GTK_STOCK_GO_BACK,
454 1,
455 GTK_STOCK_GO_FORWARD,
456 2,
457 GTK_STOCK_CLOSE,
458 GTK_RESPONSE_CLOSE,
459 NULL);
460 setup_dialog(help_dialog_shell, toplevel);
461 gtk_dialog_set_default_response(GTK_DIALOG(help_dialog_shell),
462 GTK_RESPONSE_CLOSE);
463 gtk_widget_set_name(help_dialog_shell, "Freeciv");
464
465 g_signal_connect(help_dialog_shell, "response",
466 G_CALLBACK(help_command_callback), NULL);
467 g_signal_connect(help_dialog_shell, "destroy",
468 G_CALLBACK(help_destroy_callback), NULL);
469
470 hbox = gtk_hbox_new(FALSE, 5);
471 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(help_dialog_shell)->vbox), hbox);
472 gtk_widget_show(hbox);
473
474 /* build tree store. */
475 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
476
477 array = g_array_new(FALSE, FALSE, sizeof(GtkTreeIter));
478 help_items_iterate(pitem) {
479 GtkTreeIter *it, *parent;
480 const char *s;
481 int depth;
482
483 for (s = pitem->topic; *s == ' '; s++) {
484 /* nothing */
485 }
486 depth = s - pitem->topic;
487
488 array = g_array_set_size(array, depth+1);
489
490 if (depth > 0) {
491 parent = &g_array_index(array, GtkTreeIter, depth-1);
492 } else {
493 parent = NULL;
494 }
495
496 it = &g_array_index(array, GtkTreeIter, depth);
497 gtk_tree_store_append(store, it, parent);
498
499 gtk_tree_store_set(store, it, 0, pitem->topic, 1, pitem, -1);
500 } help_items_iterate_end;
501
502
503 /* create tree view. */
504 help_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
505 g_object_unref(store);
506 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(help_view));
507 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(help_view), FALSE);
508
509 g_signal_connect(help_view, "cursor-changed",
510 G_CALLBACK(activated_topic), NULL);
511
512 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(help_view));
513 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
514
515 rend = gtk_cell_renderer_text_new();
516 col = gtk_tree_view_column_new_with_attributes(NULL, rend, "text", 0, NULL);
517 gtk_tree_view_append_column(GTK_TREE_VIEW(help_view), col);
518
519 help_view_sw = gtk_scrolled_window_new(NULL, NULL);
520 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(help_view_sw),
521 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
522 gtk_widget_set_size_request(help_view_sw, 190, -1);
523 gtk_container_add(GTK_CONTAINER(help_view_sw), help_view);
524 gtk_widget_show(help_view);
525 gtk_box_pack_start(GTK_BOX(hbox), help_view_sw, FALSE, FALSE, 0);
526 gtk_widget_show(help_view_sw);
527
528 help_frame = gtk_frame_new("");
529 gtk_box_pack_start(GTK_BOX(hbox), help_frame, TRUE, TRUE, 0);
530 gtk_widget_set_size_request(help_frame, 520, 350);
531 gtk_widget_show(help_frame);
532
533 help_box = gtk_vbox_new(FALSE, 5);
534 gtk_container_add(GTK_CONTAINER(help_frame), help_box);
535
536 help_tile = gtk_pixcomm_new(tileset_full_tile_width(tileset), tileset_full_tile_height(tileset));
537 gtk_box_pack_start(GTK_BOX(help_box), help_tile, FALSE, FALSE, 0);
538
539 help_itable = gtk_table_new(1, 6, FALSE);
540 gtk_box_pack_start(GTK_BOX(help_box), help_itable, FALSE, FALSE, 0);
541
542 for (i=0; i<6; i++) {
543 help_ilabel[i] =
544 gtk_label_new(help_ilabel_name[i] ? _(help_ilabel_name[i]) : "");
545
546 if (i==5) {
547 button = help_hyperlink_new(help_ilabel[i], HELP_TECH);
548 gtk_table_attach_defaults(GTK_TABLE(help_itable), button, i, i+1, 0, 1);
549 } else {
550 gtk_table_attach_defaults(GTK_TABLE(help_itable),
551 help_ilabel[i], i, i+1, 0, 1);
552 gtk_widget_set_name(help_ilabel[i], "help_label");
553 }
554 gtk_widget_show(help_ilabel[i]);
555 }
556
557 help_wtable = gtk_table_new(1, 6, FALSE);
558 gtk_box_pack_start(GTK_BOX(help_box), help_wtable, FALSE, FALSE, 0);
559
560 for (i=0; i<6; i++) {
561 help_wlabel[i] =
562 gtk_label_new(help_wlabel_name[i] ? _(help_wlabel_name[i]) : "");
563
564 if (i==3 || i==5) {
565 button = help_hyperlink_new(help_wlabel[i], HELP_TECH);
566 gtk_table_attach_defaults(GTK_TABLE(help_wtable), button, i, i+1, 0, 1);
567 } else {
568 gtk_table_attach_defaults(GTK_TABLE(help_wtable),
569 help_wlabel[i], i, i+1, 0, 1);
570 gtk_widget_set_name(help_wlabel[i], "help_label");
571 }
572 gtk_widget_show(help_wlabel[i]);
573 }
574
575
576 help_utable = gtk_table_new(5, 5, FALSE);
577 gtk_box_pack_start(GTK_BOX(help_box), help_utable, FALSE, FALSE, 0);
578
579 for (i=0; i<5; i++)
580 for (j=0; j<5; j++) {
581 help_ulabel[j][i] =
582 gtk_label_new(help_ulabel_name[j][i] ? _(help_ulabel_name[j][i]) : "");
583
584 if (j==4 && (i==1 || i==4)) {
585 if (i==1)
586 button = help_hyperlink_new(help_ulabel[j][i], HELP_TECH);
587 else
588 button = help_hyperlink_new(help_ulabel[j][i], HELP_UNIT);
589
590 gtk_table_attach_defaults(GTK_TABLE(help_utable),
591 button, i, i+1, j, j+1);
592 } else {
593 gtk_table_attach_defaults(GTK_TABLE(help_utable),
594 help_ulabel[j][i], i, i+1, j, j+1);
595 gtk_widget_set_name(help_ulabel[j][i], "help_label");
596 }
597 gtk_widget_show(help_ulabel[j][i]);
598 }
599
600
601 help_ttable = gtk_table_new(3, 5, FALSE);
602 gtk_box_pack_start(GTK_BOX(help_box), help_ttable, FALSE, FALSE, 0);
603
604 for (j = 0; j < 2; j++) {
605 for (i = 0; i < 5; i++) {
606 help_tlabel[j][i] =
607 gtk_label_new(help_tlabel_name[j][i] ? _(help_tlabel_name[j][i]) : "");
608 gtk_widget_set_name(help_tlabel[j][i], "help_label");
609
610 /* Ugly (but these numbers are hardcoded in help_update_terrain() too) */
611 if (j == 1 && i == 1) {
612 /* Extra wide cell for terrain specials */
613 gtk_table_attach_defaults(GTK_TABLE(help_ttable),
614 help_tlabel[j][i], i, i+4, j, j+1);
615 gtk_widget_show(help_tlabel[j][i]);
616 break; /* skip rest of row */
617 } else {
618 gtk_table_attach_defaults(GTK_TABLE(help_ttable),
619 help_tlabel[j][i], i, i+1, j, j+1);
620 gtk_widget_show(help_tlabel[j][i]);
621 }
622 }
623 }
624
625 help_etable = gtk_table_new(2, 4, FALSE);
626 gtk_box_pack_start(GTK_BOX(help_box), help_etable, FALSE, FALSE, 0);
627
628 for (i = 0; i < 6; i++) {
629 help_elabel[i] =
630 gtk_label_new(help_elabel_name[i] ? _(help_elabel_name[i]) : "");
631 gtk_table_attach_defaults(GTK_TABLE(help_etable),
632 help_elabel[i], i % 4, (i % 4) + 1, i / 4, i / 4 + 1);
633 gtk_widget_set_name(help_elabel[i], "help_label");
634 gtk_widget_show(help_elabel[i]);
635 }
636
637 help_vbox = gtk_vbox_new(FALSE, 1);
638 gtk_container_set_border_width(GTK_CONTAINER(help_vbox), 5);
639 gtk_box_pack_start(GTK_BOX(help_box), help_vbox, FALSE, FALSE, 0);
640
641 text = gtk_text_view_new();
642 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE);
643 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
644 gtk_container_set_border_width(GTK_CONTAINER(text), 5);
645 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
646 gtk_widget_set_name(text, "help_text");
647 help_text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
648 gtk_widget_show(text);
649
650 help_text_sw = gtk_scrolled_window_new(NULL, NULL);
651 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(help_text_sw),
652 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
653 gtk_container_add(GTK_CONTAINER(help_text_sw), text);
654 gtk_box_pack_start(GTK_BOX(help_box), help_text_sw, TRUE, TRUE, 0);
655
656 /* build tech store. */
657 tstore = gtk_tree_store_new(4,
658 G_TYPE_STRING, /* tech name */
659 G_TYPE_INT, /* turns to tech */
660 G_TYPE_INT, /* tech id */
661 GDK_TYPE_COLOR); /* color */
662 help_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tstore));
663 g_object_unref(tstore);
664 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(help_tree), FALSE);
665
666 g_signal_connect(help_tree, "row_activated",
667 G_CALLBACK(help_tech_tree_activated_callback), NULL);
668
669
670 col = gtk_tree_view_column_new();
671
672 rend = gtk_cell_renderer_text_new();
673 g_object_set(rend, "weight", PANGO_WEIGHT_BOLD, NULL);
674 gtk_tree_view_column_pack_start(col, rend, TRUE);
675 gtk_tree_view_column_set_attributes(col, rend,
676 "text", 0,
677 "background_gdk", 3,
678 NULL);
679 rend = gtk_cell_renderer_text_new();
680 g_object_set(rend, "weight", PANGO_WEIGHT_BOLD, "xalign", 1.0, NULL);
681 gtk_tree_view_column_pack_start(col, rend, FALSE);
682 gtk_tree_view_column_set_attributes(col, rend,
683 "text", 1,
684 "background_gdk", 3,
685 NULL);
686
687 gtk_tree_view_append_column(GTK_TREE_VIEW(help_tree), col);
688
689
690 help_tree_sw = gtk_scrolled_window_new(NULL, NULL);
691 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(help_tree_sw),
692 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
693 gtk_container_add(GTK_CONTAINER(help_tree_sw), help_tree);
694 gtk_widget_show(help_tree);
695 gtk_box_pack_start(GTK_BOX(help_box), help_tree_sw, TRUE, TRUE, 0);
696
697 help_tree_expand =
698 gtk_button_new_with_label(_("Expand All"));
699 help_tree_collapse = gtk_button_new_with_label(_("Collapse All"));
700
701 g_signal_connect(help_tree_expand, "clicked",
702 G_CALLBACK(help_tech_tree_expand_callback), help_tree);
703 g_signal_connect(help_tree_collapse, "clicked",
704 G_CALLBACK(help_tech_tree_collapse_callback), help_tree);
705
706 help_tree_buttons_hbox = gtk_hbutton_box_new();
707 gtk_container_add(GTK_CONTAINER(help_tree_buttons_hbox), help_tree_expand);
708 gtk_container_add(GTK_CONTAINER(help_tree_buttons_hbox), help_tree_collapse);
709 gtk_box_pack_start(GTK_BOX(help_box), help_tree_buttons_hbox,
710 FALSE, FALSE, 0);
711 gtk_widget_show_all(help_tree_buttons_hbox);
712
713 create_help_page(HELP_TEXT);
714 }
715
716 /**************************************************************************
717 Create page for help type
718 **************************************************************************/
create_help_page(enum help_page_type type)719 static void create_help_page(enum help_page_type type)
720 {
721 }
722
723 /**************************************************************************
724 Display updated help about improvement
725 **************************************************************************/
help_update_improvement(const struct help_item * pitem,char * title)726 static void help_update_improvement(const struct help_item *pitem,
727 char *title)
728 {
729 char buf[8192];
730 struct impr_type *imp = improvement_by_translated_name(title);
731
732 create_help_page(HELP_IMPROVEMENT);
733
734 if (imp && !is_great_wonder(imp)) {
735 const char *req = skip_intl_qualifier_prefix(REQ_LABEL_NONE);
736 char req_buf[512];
737
738 sprintf(buf, "%d", impr_build_shield_cost(imp));
739 gtk_label_set_text(GTK_LABEL(help_ilabel[1]), buf);
740 sprintf(buf, "%d", imp->upkeep);
741 gtk_label_set_text(GTK_LABEL(help_ilabel[3]), buf);
742
743 /* FIXME: this should show ranges, negated reqs, and all the
744 * MAX_NUM_REQS reqs.
745 * Currently it's limited to 1 req but this code is partially prepared
746 * to be extended. Remember MAX_NUM_REQS is a compile-time
747 * definition. */
748 requirement_vector_iterate(&imp->reqs, preq) {
749 if (!preq->present) {
750 continue;
751 }
752 req = universal_name_translation(&preq->source, req_buf, sizeof(req_buf));
753 break;
754 } requirement_vector_iterate_end;
755 gtk_label_set_text(GTK_LABEL(help_ilabel[5]), req);
756 /* create_tech_tree(help_improvement_tree, 0, imp->tech_req, 3);*/
757 }
758 else {
759 gtk_label_set_text(GTK_LABEL(help_ilabel[1]), "0");
760 gtk_label_set_text(GTK_LABEL(help_ilabel[3]), "0");
761 gtk_label_set_text(GTK_LABEL(help_ilabel[5]), REQ_LABEL_NEVER);
762 /* create_tech_tree(help_improvement_tree, 0, advance_count(), 3);*/
763 }
764 gtk_widget_show(help_itable);
765
766 helptext_building(buf, sizeof(buf), client.conn.playing, pitem->text, imp);
767 gtk_text_buffer_set_text(help_text, buf, -1);
768 gtk_widget_show(help_text_sw);
769 }
770
771 /**************************************************************************
772 Display updated help about wonder
773 **************************************************************************/
help_update_wonder(const struct help_item * pitem,char * title)774 static void help_update_wonder(const struct help_item *pitem,
775 char *title)
776 {
777 char buf[8192];
778 struct impr_type *imp = improvement_by_translated_name(title);
779
780 create_help_page(HELP_WONDER);
781
782 if (imp && is_great_wonder(imp)) {
783 int i;
784 char req_buf[512];
785
786 sprintf(buf, "%d", impr_build_shield_cost(imp));
787 gtk_label_set_text(GTK_LABEL(help_wlabel[1]), buf);
788
789 /* FIXME: this should show ranges, negated reqs, and all the
790 * MAX_NUM_REQS reqs.
791 * Currently it's limited to 1 req but this code is partially prepared
792 * to be extended. Remember MAX_NUM_REQS is a compile-time
793 * definition. */
794 i = 0;
795 requirement_vector_iterate(&imp->reqs, preq) {
796 if (!preq->present) {
797 continue;
798 }
799 gtk_label_set_text(GTK_LABEL(help_wlabel[3 + i]),
800 universal_name_translation(&preq->source,
801 req_buf, sizeof(req_buf)));
802 i++;
803 break;
804 } requirement_vector_iterate_end;
805 gtk_label_set_text(GTK_LABEL(help_wlabel[5]), REQ_LABEL_NEVER);
806 requirement_vector_iterate(&imp->obsolete_by, pobs) {
807 if (pobs->source.kind == VUT_ADVANCE && pobs->present) {
808 gtk_label_set_text(GTK_LABEL(help_wlabel[5]),
809 advance_name_translation
810 (pobs->source.value.advance));
811 break;
812 }
813 } requirement_vector_iterate_end;
814 /* create_tech_tree(help_improvement_tree, 0, imp->tech_req, 3);*/
815 } else {
816 /* can't find wonder */
817 gtk_label_set_text(GTK_LABEL(help_wlabel[1]), "0");
818 gtk_label_set_text(GTK_LABEL(help_wlabel[3]), REQ_LABEL_NEVER);
819 gtk_label_set_text(GTK_LABEL(help_wlabel[5]), skip_intl_qualifier_prefix(REQ_LABEL_NONE));
820 /* create_tech_tree(help_improvement_tree, 0, advance_count(), 3); */
821 }
822 gtk_widget_show(help_wtable);
823
824 helptext_building(buf, sizeof(buf), client.conn.playing, pitem->text, imp);
825 gtk_text_buffer_set_text(help_text, buf, -1);
826 gtk_widget_show(help_text_sw);
827 }
828
829 /**************************************************************************
830 Display updated help about unit type
831 **************************************************************************/
help_update_unit_type(const struct help_item * pitem,char * title)832 static void help_update_unit_type(const struct help_item *pitem,
833 char *title)
834 {
835 char buf[8192];
836 struct unit_type *utype = unit_type_by_translated_name(title);
837
838 create_help_page(HELP_UNIT);
839
840 if (utype) {
841 sprintf(buf, "%d", utype_build_shield_cost(utype));
842 gtk_label_set_text(GTK_LABEL(help_ulabel[0][1]), buf);
843 sprintf(buf, "%d", utype->attack_strength);
844 gtk_label_set_text(GTK_LABEL(help_ulabel[0][4]), buf);
845 sprintf(buf, "%d", utype->defense_strength);
846 gtk_label_set_text(GTK_LABEL(help_ulabel[1][1]), buf);
847 sprintf(buf, "%s", move_points_text(utype->move_rate, TRUE));
848 gtk_label_set_text(GTK_LABEL(help_ulabel[1][4]), buf);
849 sprintf(buf, "%d", utype->firepower);
850 gtk_label_set_text(GTK_LABEL(help_ulabel[2][1]), buf);
851 sprintf(buf, "%d", utype->hp);
852 gtk_label_set_text(GTK_LABEL(help_ulabel[2][4]), buf);
853 gtk_label_set_text(GTK_LABEL(help_ulabel[3][1]),
854 helptext_unit_upkeep_str(utype));
855 sprintf(buf, "%d", (int)sqrt((double)utype->vision_radius_sq));
856 gtk_label_set_text(GTK_LABEL(help_ulabel[3][4]), buf);
857 if (A_NEVER == utype->require_advance) {
858 gtk_label_set_text(GTK_LABEL(help_ulabel[4][1]), REQ_LABEL_NEVER);
859 } else {
860 gtk_label_set_text(GTK_LABEL(help_ulabel[4][1]),
861 advance_name_translation(utype->require_advance));
862 }
863 /* create_tech_tree(help_improvement_tree, 0, advance_number(utype->require_advance), 3);*/
864 if (U_NOT_OBSOLETED == utype->obsoleted_by) {
865 gtk_label_set_text(GTK_LABEL(help_ulabel[4][4]), skip_intl_qualifier_prefix(REQ_LABEL_NONE));
866 } else {
867 gtk_label_set_text(GTK_LABEL(help_ulabel[4][4]),
868 utype_name_translation(utype->obsoleted_by));
869 }
870
871 helptext_unit(buf, sizeof(buf), client.conn.playing, pitem->text, utype);
872
873 gtk_text_buffer_set_text(help_text, buf, -1);
874 gtk_widget_show(help_text_sw);
875
876 gtk_pixcomm_freeze(GTK_PIXCOMM(help_tile));
877 {
878 struct canvas store;
879
880 store.type = CANVAS_PIXCOMM;
881 store.v.pixcomm = GTK_PIXCOMM(help_tile);
882 create_overlay_unit(&store, utype, direction8_invalid());
883 }
884 gtk_pixcomm_thaw(GTK_PIXCOMM(help_tile));
885 gtk_widget_show(help_tile);
886 }
887 else {
888 gtk_label_set_text(GTK_LABEL(help_ulabel[0][1]), "0");
889 gtk_label_set_text(GTK_LABEL(help_ulabel[0][4]), "0");
890 gtk_label_set_text(GTK_LABEL(help_ulabel[1][1]), "0");
891 gtk_label_set_text(GTK_LABEL(help_ulabel[1][4]), "0");
892 gtk_label_set_text(GTK_LABEL(help_ulabel[2][1]), "0");
893 gtk_label_set_text(GTK_LABEL(help_ulabel[2][4]), "0");
894 gtk_label_set_text(GTK_LABEL(help_ulabel[3][1]), "0");
895 gtk_label_set_text(GTK_LABEL(help_ulabel[3][4]), "0");
896
897 gtk_label_set_text(GTK_LABEL(help_ulabel[4][1]), REQ_LABEL_NEVER);
898 /* create_tech_tree(help_improvement_tree, 0, A_LAST, 3);*/
899 gtk_label_set_text(GTK_LABEL(help_ulabel[4][4]), skip_intl_qualifier_prefix(REQ_LABEL_NONE));
900
901 gtk_text_buffer_set_text(help_text, buf, -1);
902 gtk_widget_show(help_text_sw);
903 }
904 gtk_widget_show(help_utable);
905 }
906
907 /**************************************************************************
908 Cut str to at max len bytes in a utf8 friendly way
909 **************************************************************************/
fc_chomp(char * str,size_t len)910 static char *fc_chomp(char *str, size_t len)
911 {
912 gchar *i;
913
914 if (!str || !*str)
915 return str;
916
917 i = str + len;
918 for (i = g_utf8_find_prev_char(str, i);
919 (i && g_unichar_isspace(g_utf8_get_char(i)));
920 i = g_utf8_find_prev_char(str, i)) {
921 *i = '\0';
922 }
923 return str;
924 }
925
926 /**************************************************************************
927 Display updated help about tech
928 **************************************************************************/
help_update_tech(const struct help_item * pitem,char * title)929 static void help_update_tech(const struct help_item *pitem, char *title)
930 {
931 int i, j;
932 GtkWidget *w, *hbox;
933 char buf[8192];
934 struct advance *padvance = advance_by_translated_name(title);
935
936 create_help_page(HELP_TECH);
937
938 if (padvance && !is_future_tech(i = advance_number(padvance))) {
939 GtkTextBuffer *txt;
940 size_t len;
941
942 gtk_container_foreach(GTK_CONTAINER(help_vbox), (GtkCallback)gtk_widget_destroy, NULL);
943
944 for (j = 0; j < ARRAY_SIZE(help_advances); j++) {
945 help_advances[j] = FALSE;
946 }
947 gtk_tree_store_clear(tstore);
948 create_tech_tree(i, TECH_TREE_DEPTH, NULL);
949 gtk_widget_show(help_tree_sw);
950 gtk_widget_show(help_tree_buttons_hbox);
951
952 helptext_advance(buf, sizeof(buf), client.conn.playing, pitem->text, i);
953 len = strlen(buf);
954 fc_chomp(buf, len);
955
956 if (get_tech_sprite(tileset, i)) {
957 GdkPixbuf *pixbuf = sprite_get_pixbuf(get_tech_sprite(tileset, i));
958
959 w = gtk_image_new_from_pixbuf(pixbuf);
960 gtk_widget_set_name(w, "help_tech_image");
961 gtk_box_pack_start(GTK_BOX(help_vbox), w, FALSE, FALSE, 0);
962 gtk_widget_show(w);
963 }
964
965 w = gtk_text_view_new();
966 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(w), FALSE);
967 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(w), GTK_WRAP_WORD);
968 gtk_widget_set_name(w, "help_text");
969 gtk_container_set_border_width(GTK_CONTAINER(w), 5);
970 gtk_text_view_set_editable(GTK_TEXT_VIEW(w), FALSE);
971 gtk_box_pack_start(GTK_BOX(help_vbox), w, TRUE, TRUE, 0);
972 gtk_widget_show(w);
973
974 txt = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w));
975 if (txt) {
976 gtk_text_buffer_set_text(txt, buf, -1);
977 }
978
979 w = gtk_hseparator_new();
980 gtk_box_pack_start(GTK_BOX(help_vbox), w, TRUE, TRUE, 5);
981 gtk_widget_show(w);
982
983 governments_iterate(pgov) {
984 /* FIXME: need a more general mechanism for this, since this
985 * helptext needs to be shown in all possible req source types. */
986 requirement_vector_iterate(&pgov->reqs, preq) {
987 if (VUT_ADVANCE == preq->source.kind
988 && preq->source.value.advance == padvance) {
989 hbox = gtk_hbox_new(FALSE, 0);
990 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
991 w = gtk_label_new(_("Allows"));
992 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
993 w = help_slink_new(government_name_translation(pgov),
994 HELP_GOVERNMENT);
995 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
996 gtk_widget_show_all(hbox);
997 }
998 } requirement_vector_iterate_end;
999 } governments_iterate_end;
1000
1001 improvement_iterate(pimprove) {
1002 if (valid_improvement(pimprove)) {
1003 requirement_vector_iterate(&pimprove->reqs, preq) {
1004 if (VUT_ADVANCE == preq->source.kind
1005 && preq->source.value.advance == padvance) {
1006 hbox = gtk_hbox_new(FALSE, 0);
1007 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1008 w = gtk_label_new(_("Allows"));
1009 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1010 w = help_slink_new(improvement_name_translation(pimprove),
1011 is_great_wonder(pimprove)
1012 ? HELP_WONDER
1013 : HELP_IMPROVEMENT);
1014 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1015 gtk_widget_show_all(hbox);
1016 }
1017 } requirement_vector_iterate_end;
1018 requirement_vector_iterate(&pimprove->obsolete_by, pobs) {
1019 if (pobs->source.kind == VUT_ADVANCE
1020 && pobs->source.value.advance == padvance
1021 && pobs->present) {
1022 hbox = gtk_hbox_new(FALSE, 0);
1023 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1024 w = gtk_label_new(_("Obsoletes"));
1025 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1026 w = help_slink_new(improvement_name_translation(pimprove),
1027 is_great_wonder(pimprove)
1028 ? HELP_WONDER
1029 : HELP_IMPROVEMENT);
1030 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1031 gtk_widget_show_all(hbox);
1032 }
1033 } requirement_vector_iterate_end;
1034 }
1035 } improvement_iterate_end;
1036
1037 unit_type_iterate(punittype) {
1038 if (padvance != punittype->require_advance) {
1039 continue;
1040 }
1041 hbox = gtk_hbox_new(FALSE, 0);
1042 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1043 w = gtk_label_new(_("Allows"));
1044 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1045 w = help_slink_new(utype_name_translation(punittype), HELP_UNIT);
1046 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1047 gtk_widget_show_all(hbox);
1048 } unit_type_iterate_end;
1049
1050 advance_iterate(A_NONE, ptest) {
1051 if (padvance == advance_requires(ptest, AR_ONE)) {
1052 if (advance_by_number(A_NONE) == advance_requires(ptest, AR_TWO)) {
1053 hbox = gtk_hbox_new(FALSE, 0);
1054 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1055 w = gtk_label_new(_("Allows"));
1056 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1057 w = help_slink_new(advance_name_translation(ptest), HELP_TECH);
1058 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1059 gtk_widget_show_all(hbox);
1060 } else {
1061 hbox = gtk_hbox_new(FALSE, 0);
1062 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1063 w = gtk_label_new(_("Allows"));
1064 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1065 w = help_slink_new(advance_name_translation(ptest), HELP_TECH);
1066 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1067 w = gtk_label_new(_("with"));
1068 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1069 w = help_slink_new(advance_name_translation(advance_requires(ptest, AR_TWO)),
1070 HELP_TECH);
1071 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1072 w = gtk_label_new(Q_("?techhelp:"));
1073 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1074 gtk_widget_show_all(hbox);
1075 }
1076 }
1077 if (padvance == advance_requires(ptest, AR_TWO)) {
1078 hbox = gtk_hbox_new(FALSE, 0);
1079 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1080 w = gtk_label_new(_("Allows"));
1081 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1082 w = help_slink_new(advance_name_translation(ptest), HELP_TECH);
1083 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1084 w = gtk_label_new(_("with"));
1085 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1086 w = help_slink_new(advance_name_translation(advance_requires(ptest, AR_ONE)),
1087 HELP_TECH);
1088 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1089 w = gtk_label_new(Q_("?techhelp:"));
1090 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1091 gtk_widget_show_all(hbox);
1092 }
1093 } advance_iterate_end;
1094 gtk_widget_show(help_vbox);
1095 }
1096 }
1097
1098 /**********************************************************************//**
1099 Add a line for an activity linking to help for result
1100 **************************************************************************/
add_act_help_for_terrain(const char * act_label,const char * result_link_label,enum help_page_type result_link_type,const char * descr_label)1101 static void add_act_help_for_terrain(const char *act_label,
1102 const char *result_link_label,
1103 enum help_page_type result_link_type,
1104 const char *descr_label)
1105 {
1106 GtkWidget *w;
1107 GtkWidget *hbox;
1108
1109 hbox = gtk_hbox_new(FALSE, 0);
1110 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1111 w = gtk_label_new(act_label);
1112 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1113 w = help_slink_new(result_link_label, result_link_type);
1114 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1115 w = gtk_label_new(descr_label);
1116 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
1117
1118 gtk_widget_show_all(hbox);
1119 }
1120
1121 /**************************************************************************
1122 Create widgets about all extras of one cause buildable to the terrain.
1123 **************************************************************************/
help_extras_of_act_for_terrain(struct terrain * pterr,enum unit_activity act,char * label)1124 static void help_extras_of_act_for_terrain(struct terrain *pterr,
1125 enum unit_activity act,
1126 char *label)
1127 {
1128 enum extra_cause cause = activity_to_extra_cause(act);
1129
1130 extra_type_by_cause_iterate(cause, pextra) {
1131 if (pextra->buildable
1132 && requirement_fulfilled_by_terrain(pterr, &(pextra->reqs))) {
1133 add_act_help_for_terrain(label,
1134 extra_name_translation(pextra), HELP_EXTRA,
1135 helptext_extra_for_terrain_str(pextra, pterr,
1136 act));
1137 }
1138 } extra_type_by_cause_iterate_end;
1139 }
1140
1141 /**************************************************************************
1142 Display updated help about terrain
1143 **************************************************************************/
help_update_terrain(const struct help_item * pitem,char * title)1144 static void help_update_terrain(const struct help_item *pitem,
1145 char *title)
1146 {
1147 char buf[8192];
1148 struct terrain *pterrain = terrain_by_translated_name(title);
1149
1150 create_help_page(HELP_TERRAIN);
1151
1152 if (pterrain) {
1153 struct universal for_terr = { .kind = VUT_TERRAIN, .value = { .terrain = pterrain }};
1154
1155 {
1156 /* 25 => "1.25"; 50 => "1.5"; 100 => "2.0" */
1157 int defbonus = pterrain->defense_bonus + 100;
1158 int frac = defbonus % 100;
1159 if ((frac % 10) == 0) {
1160 frac /= 10;
1161 }
1162 sprintf(buf, "%d/%d.%d",
1163 pterrain->movement_cost, defbonus / 100, frac);
1164 }
1165 gtk_label_set_text(GTK_LABEL(help_tlabel[0][1]), buf);
1166
1167 sprintf(buf, "%d/%d/%d",
1168 pterrain->output[O_FOOD],
1169 pterrain->output[O_SHIELD],
1170 pterrain->output[O_TRADE]);
1171 gtk_label_set_text(GTK_LABEL(help_tlabel[0][4]), buf);
1172
1173 buf[0] = '\0';
1174 if (*(pterrain->resources)) {
1175 struct resource **r;
1176
1177 for (r = pterrain->resources; *r; r++) {
1178 /* TRANS: " Whales (2/1/2)," */
1179 sprintf (buf + strlen (buf), " %s (%d/%d/%d),",
1180 resource_name_translation(*r),
1181 pterrain->output[O_FOOD] + (*r)->output[O_FOOD],
1182 pterrain->output[O_SHIELD] + (*r)->output[O_SHIELD],
1183 pterrain->output[O_TRADE] + (*r)->output[O_TRADE]);
1184 }
1185 buf[strlen (buf) - 1] = '.';
1186 } else {
1187 /* TRANS: "Resources: (none)" */
1188 sprintf (buf + strlen (buf), _("(none)"));
1189 }
1190 gtk_label_set_text(GTK_LABEL(help_tlabel[1][1]), buf);
1191
1192 gtk_container_foreach(GTK_CONTAINER(help_vbox), (GtkCallback)gtk_widget_destroy, NULL);
1193
1194 if (pterrain->irrigation_result != pterrain
1195 && pterrain->irrigation_result != T_NONE
1196 && pterrain->irrigation_time != 0
1197 && effect_cumulative_max(EFT_IRRIG_TF_POSSIBLE, &for_terr) > 0) {
1198 fc_snprintf(buf, sizeof(buf),
1199 PL_("%d turn", "%d turns", pterrain->irrigation_time),
1200 pterrain->irrigation_time);
1201 add_act_help_for_terrain(_("Irrig. Rslt/Time"),
1202 terrain_name_translation(pterrain->irrigation_result),
1203 HELP_TERRAIN, buf);
1204 }
1205
1206 if (pterrain->mining_result != pterrain
1207 && pterrain->mining_result != T_NONE
1208 && pterrain->mining_time != 0
1209 && effect_cumulative_max(EFT_MINING_TF_POSSIBLE, &for_terr) > 0) {
1210 fc_snprintf(buf, sizeof(buf),
1211 PL_("%d turn", "%d turns", pterrain->mining_time),
1212 pterrain->mining_time);
1213 add_act_help_for_terrain(_("Mine Rslt/Time"),
1214 terrain_name_translation(pterrain->mining_result),
1215 HELP_TERRAIN, buf);
1216 }
1217
1218 if (pterrain->transform_result != T_NONE
1219 && pterrain->transform_time != 0
1220 && effect_cumulative_max(EFT_TRANSFORM_POSSIBLE, &for_terr) > 0) {
1221 fc_snprintf(buf, sizeof(buf),
1222 PL_("%d turn", "%d turns", pterrain->transform_time),
1223 pterrain->transform_time);
1224 add_act_help_for_terrain(_("Trans. Rslt/Time"),
1225 terrain_name_translation(pterrain->transform_result),
1226 HELP_TERRAIN, buf);
1227 }
1228
1229 if (pterrain->irrigation_result == pterrain
1230 && pterrain->irrigation_time != 0
1231 && effect_cumulative_max(EFT_IRRIG_POSSIBLE, &for_terr) > 0) {
1232 help_extras_of_act_for_terrain(pterrain, ACTIVITY_IRRIGATE, _("Build as irrigation"));
1233 }
1234 if (pterrain->mining_result == pterrain
1235 && pterrain->mining_time != 0
1236 && effect_cumulative_max(EFT_MINING_POSSIBLE, &for_terr) > 0) {
1237 help_extras_of_act_for_terrain(pterrain, ACTIVITY_MINE, _("Build as mine"));
1238 }
1239 if (pterrain->road_time != 0) {
1240 help_extras_of_act_for_terrain(pterrain, ACTIVITY_GEN_ROAD, _("Build as road"));
1241 }
1242 if (pterrain->base_time != 0) {
1243 help_extras_of_act_for_terrain(pterrain, ACTIVITY_BASE, _("Build as base"));
1244 }
1245 gtk_widget_show(help_vbox);
1246 }
1247
1248 helptext_terrain(buf, sizeof(buf), client.conn.playing, pitem->text, pterrain);
1249
1250 gtk_text_buffer_set_text(help_text, buf, -1);
1251 gtk_widget_show(help_text_sw);
1252
1253 gtk_widget_show(help_ttable);
1254 }
1255
1256 /**************************************************************************
1257 Help page for extras.
1258 **************************************************************************/
help_update_extra(const struct help_item * pitem,char * title)1259 static void help_update_extra(const struct help_item *pitem, char *title)
1260 {
1261 char buf[8192];
1262 struct extra_type *pextra = extra_type_by_translated_name(title);
1263
1264 create_help_page(HELP_EXTRA);
1265
1266 buf[0] = '\0';
1267 if (pextra == NULL) {
1268 strcat(buf, pitem->text);
1269 } else {
1270 struct road_type *proad = extra_road_get(pextra);
1271
1272 /* Cost to build */
1273 if (pextra->buildable) {
1274 if (pextra->build_time != 0) {
1275 /* TRANS: "MP" = movement points */
1276 sprintf(buf, _("%d MP"), pextra->build_time);
1277 } else {
1278 /* TRANS: Build time depends on terrain. */
1279 sprintf(buf, _("Terrain specific"));
1280 }
1281 } else {
1282 sprintf(buf, "-");
1283 }
1284 gtk_label_set_text(GTK_LABEL(help_elabel[1]), buf);
1285 /* Conflicting extras */
1286 buf[0] = '\0';
1287 extra_type_iterate(pextra2) {
1288 if (!can_extras_coexist(pextra, pextra2)) {
1289 if (buf[0] != '\0') {
1290 strcat(buf, "/");
1291 }
1292 strcat(buf, extra_name_translation(pextra2));
1293 }
1294 } extra_type_iterate_end;
1295 /* TRANS: "Conflicts with: (none)" (extras) */
1296 gtk_label_set_text(GTK_LABEL(help_elabel[3]), buf[0] ? buf : _("(none)"));
1297
1298 /* Bonus */
1299 if (proad != NULL) {
1300 const char *bonus = NULL;
1301
1302 output_type_iterate(o) {
1303 if (proad->tile_incr[o] > 0) {
1304 /* TRANS: Road bonus depends on terrain. */
1305 bonus = _("Terrain specific");
1306 break;
1307 }
1308 } output_type_iterate_end;
1309 if (!bonus) {
1310 bonus = helptext_road_bonus_str(NULL, proad);
1311 }
1312 if (!bonus) {
1313 /* TRANS: No output bonus from a road */
1314 bonus = Q_("?bonus:None");
1315 }
1316 gtk_label_set_text(GTK_LABEL(help_elabel[5]), bonus);
1317 } else {
1318 gtk_label_set_text(GTK_LABEL(help_elabel[5]), Q_("?bonus:None"));
1319 }
1320
1321 helptext_extra(buf, sizeof(buf), client.conn.playing, pitem->text, pextra);
1322 }
1323 gtk_widget_show(help_etable);
1324
1325 gtk_text_buffer_set_text(help_text, buf, -1);
1326 gtk_widget_show(help_text_sw);
1327 }
1328
1329 /**************************************************************************
1330 This is currently just a text page, with special text:
1331 **************************************************************************/
help_update_specialist(const struct help_item * pitem,char * title)1332 static void help_update_specialist(const struct help_item *pitem,
1333 char *title)
1334 {
1335 char buf[8192];
1336 struct specialist *pspec = specialist_by_translated_name(title);
1337
1338 if (!pspec) {
1339 strcat(buf, pitem->text);
1340 } else {
1341 helptext_specialist(buf, sizeof(buf), client.conn.playing, pitem->text,
1342 pspec);
1343 }
1344 create_help_page(HELP_TEXT);
1345 gtk_text_buffer_set_text(help_text, buf, -1);
1346 gtk_widget_show(help_text_sw);
1347 }
1348
1349 /**************************************************************************
1350 This is currently just a text page, with special text:
1351 **************************************************************************/
help_update_government(const struct help_item * pitem,char * title)1352 static void help_update_government(const struct help_item *pitem,
1353 char *title)
1354 {
1355 char buf[8192];
1356 struct government *gov = government_by_translated_name(title);
1357
1358 if (!gov) {
1359 strcat(buf, pitem->text);
1360 } else {
1361 helptext_government(buf, sizeof(buf), client.conn.playing, pitem->text, gov);
1362 }
1363 create_help_page(HELP_TEXT);
1364 gtk_text_buffer_set_text(help_text, buf, -1);
1365 gtk_widget_show(help_text_sw);
1366 }
1367
1368 /**************************************************************************
1369 This is currently just a text page, with special text
1370 **************************************************************************/
help_update_nation(const struct help_item * pitem,char * title,struct nation_type * pnation)1371 static void help_update_nation(const struct help_item *pitem, char *title,
1372 struct nation_type *pnation)
1373 {
1374 char buf[4096];
1375
1376 if (!pnation) {
1377 strcat(buf, pitem->text);
1378 } else {
1379 helptext_nation(buf, sizeof(buf), pnation, pitem->text);
1380 }
1381 create_help_page(HELP_TEXT);
1382 gtk_text_buffer_set_text(help_text, buf, -1);
1383 gtk_widget_show(help_text_sw);
1384 }
1385
1386 /**************************************************************************
1387 Display updated help dialog
1388 **************************************************************************/
help_update_dialog(const struct help_item * pitem)1389 static void help_update_dialog(const struct help_item *pitem)
1390 {
1391 char *top;
1392
1393 /* figure out what kind of item is required for pitem ingo */
1394
1395 for (top = pitem->topic; *top == ' '; top++) {
1396 /* nothing */
1397 }
1398
1399 help_box_hide();
1400 gtk_text_buffer_set_text(help_text, "", -1);
1401
1402 switch(pitem->type) {
1403 case HELP_IMPROVEMENT:
1404 help_update_improvement(pitem, top);
1405 break;
1406 case HELP_WONDER:
1407 help_update_wonder(pitem, top);
1408 break;
1409 case HELP_UNIT:
1410 help_update_unit_type(pitem, top);
1411 break;
1412 case HELP_TECH:
1413 help_update_tech(pitem, top);
1414 break;
1415 case HELP_TERRAIN:
1416 help_update_terrain(pitem, top);
1417 break;
1418 case HELP_EXTRA:
1419 help_update_extra(pitem, top);
1420 break;
1421 case HELP_SPECIALIST:
1422 help_update_specialist(pitem, top);
1423 break;
1424 case HELP_GOVERNMENT:
1425 help_update_government(pitem, top);
1426 break;
1427 case HELP_NATIONS:
1428 help_update_nation(pitem, top, nation_by_translated_plural(top));
1429 break;
1430 case HELP_TEXT:
1431 default:
1432 /* it was a pure text item */
1433 create_help_page(HELP_TEXT);
1434
1435 gtk_text_buffer_set_text(help_text, pitem->text, -1);
1436 gtk_widget_show(help_text_sw);
1437 break;
1438 }
1439 set_title_topic(pitem->topic);
1440
1441 gtk_widget_show(help_box);
1442 }
1443
1444
1445 /**************************************************************************
1446 Add item at path to selection and scroll to its cell
1447 **************************************************************************/
help_item_zoom(GtkTreePath * path)1448 static void help_item_zoom(GtkTreePath *path)
1449 {
1450 GtkTreeModel *model;
1451 GtkTreeIter it, child, item;
1452 GtkTreeSelection *selection;
1453
1454 model = gtk_tree_view_get_model(GTK_TREE_VIEW(help_view));
1455 gtk_tree_model_get_iter(model, &item, path);
1456
1457 for (child=item; gtk_tree_model_iter_parent(model, &it, &child); child=it) {
1458 GtkTreePath *it_path;
1459
1460 it_path = gtk_tree_model_get_path(model, &it);
1461 gtk_tree_view_expand_row(GTK_TREE_VIEW(help_view), it_path, TRUE);
1462 gtk_tree_path_free(it_path);
1463 }
1464
1465 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(help_view));
1466 gtk_tree_selection_select_iter(selection, &item);
1467 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(help_view), path, NULL,
1468 TRUE, 0.0, 0.0);
1469 }
1470
1471 /****************************************************************
1472 Return path to help item.
1473 *****************************************************************/
help_item_path(const struct help_item * pitem)1474 static GtkTreePath *help_item_path(const struct help_item *pitem)
1475 {
1476 GtkTreePath *path;
1477 bool next;
1478
1479 path = gtk_tree_path_new_first();
1480 next = FALSE;
1481 help_items_iterate(pitem2) {
1482 const char *s;
1483 int depth;
1484
1485 for (s = pitem2->topic; *s == ' '; s++) {
1486 /* nothing */
1487 }
1488 depth = s - pitem2->topic + 1;
1489
1490 while (depth < gtk_tree_path_get_depth(path)) {
1491 gtk_tree_path_up(path);
1492 gtk_tree_path_next(path);
1493 next = FALSE;
1494 }
1495 while (depth > gtk_tree_path_get_depth(path)) {
1496 gtk_tree_path_down(path);
1497 next = FALSE;
1498 }
1499
1500 if (next) {
1501 gtk_tree_path_next(path);
1502 }
1503
1504 if (pitem == pitem2)
1505 break;
1506
1507 next = TRUE;
1508 } help_items_iterate_end;
1509
1510 return path;
1511 }
1512
1513 /****************************************************************
1514 Add item to selection
1515 *****************************************************************/
select_help_item_string(const char * item,enum help_page_type htype)1516 static void select_help_item_string(const char *item, enum help_page_type htype)
1517 {
1518 const struct help_item *pitem;
1519 int idx;
1520 GtkTreePath *path;
1521 GtkTreeViewColumn *col;
1522
1523 if (!(pitem = get_help_item_spec(item, htype, &idx))) {
1524 return;
1525 }
1526
1527 path = help_item_path(pitem);
1528 help_item_zoom(path);
1529
1530 col = gtk_tree_view_get_column(GTK_TREE_VIEW(help_view), 0);
1531 gtk_tree_view_set_cursor(GTK_TREE_VIEW(help_view), path, col, FALSE);
1532 gtk_tree_path_free(path);
1533 }
1534
1535 /**************************************************************************
1536 Set sensitivity of help dialog response buttons.
1537 **************************************************************************/
help_command_update(void)1538 static void help_command_update(void)
1539 {
1540 GtkDialog *dialog = GTK_DIALOG(help_dialog_shell);
1541
1542 if (help_history_pos < 0) {
1543 gtk_dialog_set_response_sensitive(dialog, 1, FALSE);
1544 gtk_dialog_set_response_sensitive(dialog, 2, FALSE);
1545 } else {
1546 gtk_dialog_set_response_sensitive(dialog, 1, TRUE);
1547 gtk_dialog_set_response_sensitive(dialog, 2, TRUE);
1548
1549 if (help_history_pos == 0) {
1550 gtk_dialog_set_response_sensitive(dialog, 1, FALSE);
1551 }
1552 if (help_history_pos >= help_history->len - 1) {
1553 gtk_dialog_set_response_sensitive(dialog, 2, FALSE);
1554 }
1555 }
1556 }
1557
1558 /**************************************************************************
1559 User gave response to help dialog
1560 **************************************************************************/
help_command_callback(GtkWidget * w,gint response_id)1561 static void help_command_callback(GtkWidget *w, gint response_id)
1562 {
1563 GtkTreePath *path;
1564 const struct help_item *pitem;
1565
1566 if (response_id == 1) {
1567 if (help_history_pos > 0) {
1568 help_history_pos--;
1569
1570 pitem = g_ptr_array_index(help_history, help_history_pos);
1571 path = help_item_path(pitem);
1572 help_item_zoom(path);
1573 help_update_dialog(pitem);
1574 help_command_update();
1575 }
1576 } else if (response_id == 2) {
1577 if (help_history_pos < help_history->len - 1) {
1578 help_history_pos++;
1579
1580 pitem = g_ptr_array_index(help_history, help_history_pos);
1581 path = help_item_path(pitem);
1582 help_item_zoom(path);
1583 help_update_dialog(pitem);
1584 help_command_update();
1585 }
1586 } else {
1587 gtk_widget_destroy(help_dialog_shell);
1588 }
1589 }
1590