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