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.22 */
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 _("_Back"),
458 1,
459 _("_Forward"),
460 2,
461 _("_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 Set sprite to show for current terrain.
758 **************************************************************************/
set_help_tile_from_terrain(struct terrain * pterr)759 static void set_help_tile_from_terrain(struct terrain *pterr)
760 {
761 struct canvas canvas = FC_STATIC_CANVAS_INIT;
762 cairo_t *cr;
763 int i;
764
765 canvas.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
766 tileset_tile_width(tileset),
767 tileset_tile_height(tileset));
768
769 cr = cairo_create(canvas.surface);
770 cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
771 cairo_paint(cr);
772 cairo_destroy(cr);
773
774 for (i = 0; i < 3; i++) {
775 struct drawn_sprite sprs[80];
776 int count = fill_basic_terrain_layer_sprite_array(tileset, sprs,
777 i, pterr);
778
779 put_drawn_sprites(&canvas, 1.0, 0, 0, count, sprs, FALSE);
780 }
781
782 gtk_image_set_from_surface(GTK_IMAGE(help_tile), canvas.surface);
783 gtk_widget_show(help_tile);
784 cairo_surface_destroy(canvas.surface);
785 }
786
787 /**************************************************************************
788 Display updated help about improvement
789 **************************************************************************/
help_update_improvement(const struct help_item * pitem,char * title)790 static void help_update_improvement(const struct help_item *pitem,
791 char *title)
792 {
793 char buf[8192];
794 struct impr_type *imp = improvement_by_translated_name(title);
795
796 create_help_page(HELP_IMPROVEMENT);
797
798 if (imp && !is_great_wonder(imp)) {
799 const char *req = skip_intl_qualifier_prefix(REQ_LABEL_NONE);
800 char req_buf[512];
801
802 sprintf(buf, "%d", impr_build_shield_cost(imp));
803 gtk_label_set_text(GTK_LABEL(help_ilabel[1]), buf);
804 sprintf(buf, "%d", imp->upkeep);
805 gtk_label_set_text(GTK_LABEL(help_ilabel[3]), buf);
806
807 /* FIXME: this should show ranges, negated reqs, and all the
808 * MAX_NUM_REQS reqs.
809 * Currently it's limited to 1 req but this code is partially prepared
810 * to be extended. Remember MAX_NUM_REQS is a compile-time
811 * definition. */
812 requirement_vector_iterate(&imp->reqs, preq) {
813 if (!preq->present) {
814 continue;
815 }
816 req = universal_name_translation(&preq->source, req_buf, sizeof(req_buf));
817 break;
818 } requirement_vector_iterate_end;
819 gtk_label_set_text(GTK_LABEL(help_ilabel[5]), req);
820 /* create_tech_tree(help_improvement_tree, 0, imp->tech_req, 3);*/
821 } else {
822 gtk_label_set_text(GTK_LABEL(help_ilabel[1]), "0");
823 gtk_label_set_text(GTK_LABEL(help_ilabel[3]), "0");
824 gtk_label_set_text(GTK_LABEL(help_ilabel[5]), REQ_LABEL_NEVER);
825 /* create_tech_tree(help_improvement_tree, 0, advance_count(), 3);*/
826 }
827
828 set_help_tile_from_sprite(get_building_sprite(tileset, imp));
829
830 gtk_widget_show(help_itable);
831
832 helptext_building(buf, sizeof(buf), client.conn.playing, pitem->text, imp);
833 gtk_text_buffer_set_text(help_text, buf, -1);
834 gtk_widget_show(help_text_sw);
835 }
836
837 /**************************************************************************
838 Display updated help about wonder
839 **************************************************************************/
help_update_wonder(const struct help_item * pitem,char * title)840 static void help_update_wonder(const struct help_item *pitem,
841 char *title)
842 {
843 char buf[8192];
844 struct impr_type *imp = improvement_by_translated_name(title);
845
846 create_help_page(HELP_WONDER);
847
848 if (imp && is_great_wonder(imp)) {
849 int i;
850 char req_buf[512];
851
852 sprintf(buf, "%d", impr_build_shield_cost(imp));
853 gtk_label_set_text(GTK_LABEL(help_wlabel[1]), buf);
854
855 /* FIXME: this should show ranges, negated reqs, and all the
856 * MAX_NUM_REQS reqs.
857 * Currently it's limited to 1 req but this code is partially prepared
858 * to be extended. Remember MAX_NUM_REQS is a compile-time
859 * definition. */
860 i = 0;
861 requirement_vector_iterate(&imp->reqs, preq) {
862 if (!preq->present) {
863 continue;
864 }
865 gtk_label_set_text(GTK_LABEL(help_wlabel[3 + i]),
866 universal_name_translation(&preq->source,
867 req_buf, sizeof(req_buf)));
868 i++;
869 break;
870 } requirement_vector_iterate_end;
871 gtk_label_set_text(GTK_LABEL(help_wlabel[5]), REQ_LABEL_NEVER);
872 requirement_vector_iterate(&imp->obsolete_by, pobs) {
873 if (pobs->source.kind == VUT_ADVANCE && pobs->present) {
874 gtk_label_set_text(GTK_LABEL(help_wlabel[5]),
875 advance_name_translation
876 (pobs->source.value.advance));
877 break;
878 }
879 } requirement_vector_iterate_end;
880 /* create_tech_tree(help_improvement_tree, 0, imp->tech_req, 3);*/
881 } else {
882 /* can't find wonder */
883 gtk_label_set_text(GTK_LABEL(help_wlabel[1]), "0");
884 gtk_label_set_text(GTK_LABEL(help_wlabel[3]), REQ_LABEL_NEVER);
885 gtk_label_set_text(GTK_LABEL(help_wlabel[5]), skip_intl_qualifier_prefix(REQ_LABEL_NONE));
886 /* create_tech_tree(help_improvement_tree, 0, advance_count(), 3); */
887 }
888
889 set_help_tile_from_sprite(get_building_sprite(tileset, imp));
890
891 gtk_widget_show(help_wtable);
892
893 helptext_building(buf, sizeof(buf), client.conn.playing, pitem->text, imp);
894 gtk_text_buffer_set_text(help_text, buf, -1);
895 gtk_widget_show(help_text_sw);
896 }
897
898 /**************************************************************************
899 Display updated help about unit type
900 **************************************************************************/
help_update_unit_type(const struct help_item * pitem,char * title)901 static void help_update_unit_type(const struct help_item *pitem,
902 char *title)
903 {
904 char buf[8192];
905 struct unit_type *utype = unit_type_by_translated_name(title);
906
907 create_help_page(HELP_UNIT);
908
909 if (utype) {
910 sprintf(buf, "%d", utype_build_shield_cost(utype));
911 gtk_label_set_text(GTK_LABEL(help_ulabel[0][1]), buf);
912 sprintf(buf, "%d", utype->attack_strength);
913 gtk_label_set_text(GTK_LABEL(help_ulabel[0][4]), buf);
914 sprintf(buf, "%d", utype->defense_strength);
915 gtk_label_set_text(GTK_LABEL(help_ulabel[1][1]), buf);
916 sprintf(buf, "%s", move_points_text(utype->move_rate, TRUE));
917 gtk_label_set_text(GTK_LABEL(help_ulabel[1][4]), buf);
918 sprintf(buf, "%d", utype->firepower);
919 gtk_label_set_text(GTK_LABEL(help_ulabel[2][1]), buf);
920 sprintf(buf, "%d", utype->hp);
921 gtk_label_set_text(GTK_LABEL(help_ulabel[2][4]), buf);
922 gtk_label_set_text(GTK_LABEL(help_ulabel[3][1]),
923 helptext_unit_upkeep_str(utype));
924 sprintf(buf, "%d", (int)sqrt((double)utype->vision_radius_sq));
925 gtk_label_set_text(GTK_LABEL(help_ulabel[3][4]), buf);
926 if (A_NEVER == utype->require_advance) {
927 gtk_label_set_text(GTK_LABEL(help_ulabel[4][1]), REQ_LABEL_NEVER);
928 } else {
929 gtk_label_set_text(GTK_LABEL(help_ulabel[4][1]),
930 advance_name_translation(utype->require_advance));
931 }
932 /* create_tech_tree(help_improvement_tree, 0, advance_number(utype->require_advance), 3);*/
933 if (U_NOT_OBSOLETED == utype->obsoleted_by) {
934 gtk_label_set_text(GTK_LABEL(help_ulabel[4][4]), skip_intl_qualifier_prefix(REQ_LABEL_NONE));
935 } else {
936 gtk_label_set_text(GTK_LABEL(help_ulabel[4][4]),
937 utype_name_translation(utype->obsoleted_by));
938 }
939
940 helptext_unit(buf, sizeof(buf), client.conn.playing, pitem->text, utype);
941
942 gtk_text_buffer_set_text(help_text, buf, -1);
943 gtk_widget_show(help_text_sw);
944
945 set_help_tile_from_sprite(get_unittype_sprite(tileset, utype, direction8_invalid(),
946 TRUE));
947 } else {
948 gtk_label_set_text(GTK_LABEL(help_ulabel[0][1]), "0");
949 gtk_label_set_text(GTK_LABEL(help_ulabel[0][4]), "0");
950 gtk_label_set_text(GTK_LABEL(help_ulabel[1][1]), "0");
951 gtk_label_set_text(GTK_LABEL(help_ulabel[1][4]), "0");
952 gtk_label_set_text(GTK_LABEL(help_ulabel[2][1]), "0");
953 gtk_label_set_text(GTK_LABEL(help_ulabel[2][4]), "0");
954 gtk_label_set_text(GTK_LABEL(help_ulabel[3][1]), "0");
955 gtk_label_set_text(GTK_LABEL(help_ulabel[3][4]), "0");
956
957 gtk_label_set_text(GTK_LABEL(help_ulabel[4][1]), REQ_LABEL_NEVER);
958 /* create_tech_tree(help_improvement_tree, 0, A_LAST, 3);*/
959 gtk_label_set_text(GTK_LABEL(help_ulabel[4][4]), skip_intl_qualifier_prefix(REQ_LABEL_NONE));
960
961 gtk_text_buffer_set_text(help_text, buf, -1);
962 gtk_widget_show(help_text_sw);
963 }
964 gtk_widget_show(help_utable);
965 }
966
967 /**************************************************************************
968 Cut str to at max len bytes in a utf8 friendly way
969 **************************************************************************/
fc_chomp(char * str,size_t len)970 static char *fc_chomp(char *str, size_t len)
971 {
972 gchar *i;
973
974 if (!str || !*str)
975 return str;
976
977 i = str + len;
978 for (i = g_utf8_find_prev_char(str, i);
979 (i && g_unichar_isspace(g_utf8_get_char(i)));
980 i = g_utf8_find_prev_char(str, i)) {
981 *i = '\0';
982 }
983 return str;
984 }
985
986 /**************************************************************************
987 Display updated help about tech
988 **************************************************************************/
help_update_tech(const struct help_item * pitem,char * title)989 static void help_update_tech(const struct help_item *pitem, char *title)
990 {
991 int i, j;
992 GtkWidget *w, *hbox;
993 char buf[8192];
994 struct advance *padvance = advance_by_translated_name(title);
995
996 create_help_page(HELP_TECH);
997
998 if (padvance && !is_future_tech(i = advance_number(padvance))) {
999 GtkTextBuffer *txt;
1000 size_t len;
1001
1002 gtk_container_foreach(GTK_CONTAINER(help_vbox), (GtkCallback)gtk_widget_destroy, NULL);
1003
1004 for (j = 0; j < ARRAY_SIZE(help_advances); j++) {
1005 help_advances[j] = FALSE;
1006 }
1007 gtk_tree_store_clear(tstore);
1008 create_tech_tree(i, TECH_TREE_DEPTH, NULL);
1009 gtk_widget_show(help_tree_sw);
1010 gtk_widget_show(help_tree_buttons_hbox);
1011
1012 helptext_advance(buf, sizeof(buf), client.conn.playing, pitem->text, i);
1013 len = strlen(buf);
1014 fc_chomp(buf, len);
1015
1016 set_help_tile_from_sprite(get_tech_sprite(tileset, i));
1017
1018 w = gtk_text_view_new();
1019 gtk_widget_set_hexpand(w, TRUE);
1020 gtk_widget_set_vexpand(w, TRUE);
1021 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(w), FALSE);
1022 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(w), GTK_WRAP_WORD);
1023 gtk_widget_set_name(w, "help_text");
1024 gtk_container_set_border_width(GTK_CONTAINER(w), 5);
1025 gtk_text_view_set_editable(GTK_TEXT_VIEW(w), FALSE);
1026 gtk_container_add(GTK_CONTAINER(help_vbox), w);
1027 gtk_widget_show(w);
1028
1029 txt = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w));
1030 if (txt) {
1031 gtk_text_buffer_set_text(txt, buf, -1);
1032 }
1033
1034 w = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1035 g_object_set(w, "margin", 5, NULL);
1036 gtk_widget_set_hexpand(w, TRUE);
1037 gtk_widget_set_vexpand(w, TRUE);
1038 gtk_container_add(GTK_CONTAINER(help_vbox), w);
1039 gtk_widget_show(w);
1040
1041 governments_iterate(pgov) {
1042 /* FIXME: need a more general mechanism for this, since this
1043 * helptext needs to be shown in all possible req source types. */
1044 requirement_vector_iterate(&pgov->reqs, preq) {
1045 if (VUT_ADVANCE == preq->source.kind
1046 && preq->source.value.advance == padvance) {
1047 hbox = gtk_grid_new();
1048 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1049 w = gtk_label_new(_("Allows"));
1050 gtk_container_add(GTK_CONTAINER(hbox), w);
1051 w = help_slink_new(government_name_translation(pgov),
1052 HELP_GOVERNMENT);
1053 gtk_container_add(GTK_CONTAINER(hbox), w);
1054 gtk_widget_show_all(hbox);
1055 }
1056 } requirement_vector_iterate_end;
1057 } governments_iterate_end;
1058
1059 improvement_iterate(pimprove) {
1060 if (valid_improvement(pimprove)) {
1061 requirement_vector_iterate(&pimprove->reqs, preq) {
1062 if (VUT_ADVANCE == preq->source.kind
1063 && preq->source.value.advance == padvance) {
1064 hbox = gtk_grid_new();
1065 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1066 w = gtk_label_new(_("Allows"));
1067 gtk_container_add(GTK_CONTAINER(hbox), w);
1068 w = help_slink_new(improvement_name_translation(pimprove),
1069 is_great_wonder(pimprove)
1070 ? HELP_WONDER
1071 : HELP_IMPROVEMENT);
1072 gtk_container_add(GTK_CONTAINER(hbox), w);
1073 gtk_widget_show_all(hbox);
1074 }
1075 } requirement_vector_iterate_end;
1076 requirement_vector_iterate(&pimprove->obsolete_by, pobs) {
1077 if (pobs->source.kind == VUT_ADVANCE
1078 && pobs->source.value.advance == padvance
1079 && pobs->present) {
1080 hbox = gtk_grid_new();
1081 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1082 w = gtk_label_new(_("Obsoletes"));
1083 gtk_container_add(GTK_CONTAINER(hbox), w);
1084 w = help_slink_new(improvement_name_translation(pimprove),
1085 is_great_wonder(pimprove)
1086 ? HELP_WONDER
1087 : HELP_IMPROVEMENT);
1088 gtk_container_add(GTK_CONTAINER(hbox), w);
1089 gtk_widget_show_all(hbox);
1090 }
1091 } requirement_vector_iterate_end;
1092 }
1093 } improvement_iterate_end;
1094
1095 unit_type_iterate(punittype) {
1096 if (padvance != punittype->require_advance) {
1097 continue;
1098 }
1099 hbox = gtk_grid_new();
1100 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1101 w = gtk_label_new(_("Allows"));
1102 gtk_container_add(GTK_CONTAINER(hbox), w);
1103 w = help_slink_new(utype_name_translation(punittype), HELP_UNIT);
1104 gtk_container_add(GTK_CONTAINER(hbox), w);
1105 gtk_widget_show_all(hbox);
1106 } unit_type_iterate_end;
1107
1108 advance_iterate(A_NONE, ptest) {
1109 if (padvance == advance_requires(ptest, AR_ONE)) {
1110 if (advance_by_number(A_NONE) == advance_requires(ptest, AR_TWO)) {
1111 hbox = gtk_grid_new();
1112 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1113 w = gtk_label_new(_("Allows"));
1114 gtk_container_add(GTK_CONTAINER(hbox), w);
1115 w = help_slink_new(advance_name_translation(ptest), HELP_TECH);
1116 gtk_container_add(GTK_CONTAINER(hbox), w);
1117 gtk_widget_show_all(hbox);
1118 } else {
1119 hbox = gtk_grid_new();
1120 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1121 w = gtk_label_new(_("Allows"));
1122 gtk_container_add(GTK_CONTAINER(hbox), w);
1123 w = help_slink_new(advance_name_translation(ptest), HELP_TECH);
1124 gtk_container_add(GTK_CONTAINER(hbox), w);
1125 w = gtk_label_new(_("with"));
1126 gtk_container_add(GTK_CONTAINER(hbox), w);
1127 w = help_slink_new(advance_name_translation(advance_requires(ptest, AR_TWO)),
1128 HELP_TECH);
1129 gtk_container_add(GTK_CONTAINER(hbox), w);
1130 w = gtk_label_new(Q_("?techhelp:"));
1131 gtk_container_add(GTK_CONTAINER(hbox), w);
1132 gtk_widget_show_all(hbox);
1133 }
1134 }
1135 if (padvance == advance_requires(ptest, AR_TWO)) {
1136 hbox = gtk_grid_new();
1137 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1138 w = gtk_label_new(_("Allows"));
1139 gtk_container_add(GTK_CONTAINER(hbox), w);
1140 w = help_slink_new(advance_name_translation(ptest), HELP_TECH);
1141 gtk_container_add(GTK_CONTAINER(hbox), w);
1142 w = gtk_label_new(_("with"));
1143 gtk_container_add(GTK_CONTAINER(hbox), w);
1144 w = help_slink_new(advance_name_translation(advance_requires(ptest, AR_ONE)),
1145 HELP_TECH);
1146 gtk_container_add(GTK_CONTAINER(hbox), w);
1147 w = gtk_label_new(Q_("?techhelp:"));
1148 gtk_container_add(GTK_CONTAINER(hbox), w);
1149 gtk_widget_show_all(hbox);
1150 }
1151 } advance_iterate_end;
1152 gtk_widget_show(help_vbox);
1153 }
1154 }
1155
1156 /**********************************************************************//**
1157 Add a line for an activity linking to help for result
1158 **************************************************************************/
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)1159 static void add_act_help_for_terrain(const char *act_label,
1160 const char *result_link_label,
1161 enum help_page_type result_link_type,
1162 const char *descr_label)
1163 {
1164 GtkWidget *w;
1165 GtkWidget *hbox;
1166
1167 hbox = gtk_grid_new();
1168 gtk_container_add(GTK_CONTAINER(help_vbox), hbox);
1169 w = gtk_label_new(act_label);
1170 gtk_container_add(GTK_CONTAINER(hbox), w);
1171 w = help_slink_new(result_link_label, result_link_type);
1172 gtk_container_add(GTK_CONTAINER(hbox), w);
1173 w = gtk_label_new(descr_label);
1174 gtk_container_add(GTK_CONTAINER(hbox), w);
1175
1176 gtk_widget_show_all(hbox);
1177 }
1178
1179 /**************************************************************************
1180 Create widgets about all extras of one cause activity to the terrain.
1181 **************************************************************************/
help_extras_of_act_for_terrain(struct terrain * pterr,enum unit_activity act,char * label)1182 static void help_extras_of_act_for_terrain(struct terrain *pterr,
1183 enum unit_activity act,
1184 char *label)
1185 {
1186 enum extra_cause cause = activity_to_extra_cause(act);
1187
1188 extra_type_by_cause_iterate(cause, pextra) {
1189 if (pextra->buildable
1190 && requirement_fulfilled_by_terrain(pterr, &(pextra->reqs))) {
1191 add_act_help_for_terrain(label,
1192 extra_name_translation(pextra), HELP_EXTRA,
1193 helptext_extra_for_terrain_str(pextra, pterr,
1194 act));
1195 }
1196 } extra_type_by_cause_iterate_end;
1197 }
1198
1199 /**************************************************************************
1200 Display updated help about terrain
1201 **************************************************************************/
help_update_terrain(const struct help_item * pitem,char * title)1202 static void help_update_terrain(const struct help_item *pitem,
1203 char *title)
1204 {
1205 char buf[8192];
1206 struct terrain *pterrain = terrain_by_translated_name(title);
1207
1208 create_help_page(HELP_TERRAIN);
1209
1210 if (pterrain) {
1211 struct universal for_terr = { .kind = VUT_TERRAIN, .value = { .terrain = pterrain }};
1212
1213 set_help_tile_from_terrain(pterrain);
1214
1215 {
1216 /* 25 => "1.25"; 50 => "1.5"; 100 => "2.0" */
1217 int defbonus = pterrain->defense_bonus + 100;
1218 int frac = defbonus % 100;
1219
1220 if ((frac % 10) == 0) {
1221 frac /= 10;
1222 }
1223 sprintf(buf, "%d/%d.%d",
1224 pterrain->movement_cost, defbonus / 100, frac);
1225 }
1226 gtk_label_set_text(GTK_LABEL(help_tlabel[0][1]), buf);
1227
1228 sprintf(buf, "%d/%d/%d",
1229 pterrain->output[O_FOOD],
1230 pterrain->output[O_SHIELD],
1231 pterrain->output[O_TRADE]);
1232 gtk_label_set_text(GTK_LABEL(help_tlabel[0][4]), buf);
1233
1234 buf[0] = '\0';
1235 if (*(pterrain->resources)) {
1236 struct resource **r;
1237
1238 for (r = pterrain->resources; *r; r++) {
1239 /* TRANS: " Whales (2/1/2)," */
1240 sprintf (buf + strlen (buf), " %s (%d/%d/%d),",
1241 resource_name_translation(*r),
1242 pterrain->output[O_FOOD] + (*r)->output[O_FOOD],
1243 pterrain->output[O_SHIELD] + (*r)->output[O_SHIELD],
1244 pterrain->output[O_TRADE] + (*r)->output[O_TRADE]);
1245 }
1246 buf[strlen (buf) - 1] = '.';
1247 } else {
1248 /* TRANS: "Resources: (none)" */
1249 sprintf (buf + strlen (buf), _("(none)"));
1250 }
1251 gtk_label_set_text(GTK_LABEL(help_tlabel[1][1]), buf);
1252
1253 gtk_container_foreach(GTK_CONTAINER(help_vbox), (GtkCallback)gtk_widget_destroy, NULL);
1254
1255 if (pterrain->irrigation_result != pterrain
1256 && pterrain->irrigation_result != T_NONE
1257 && pterrain->irrigation_time != 0
1258 && effect_cumulative_max(EFT_IRRIG_TF_POSSIBLE, &for_terr) > 0) {
1259 fc_snprintf(buf, sizeof(buf),
1260 PL_("%d turn", "%d turns", pterrain->irrigation_time),
1261 pterrain->irrigation_time);
1262 add_act_help_for_terrain(_("Irrig. Rslt/Time"),
1263 terrain_name_translation(pterrain->irrigation_result),
1264 HELP_TERRAIN, buf);
1265 }
1266
1267 if (pterrain->mining_result != pterrain
1268 && pterrain->mining_result != T_NONE
1269 && pterrain->mining_time != 0
1270 && effect_cumulative_max(EFT_MINING_TF_POSSIBLE, &for_terr) > 0) {
1271 fc_snprintf(buf, sizeof(buf),
1272 PL_("%d turn", "%d turns", pterrain->mining_time),
1273 pterrain->mining_time);
1274 add_act_help_for_terrain(_("Mine Rslt/Time"),
1275 terrain_name_translation(pterrain->mining_result),
1276 HELP_TERRAIN, buf);
1277 }
1278
1279 if (pterrain->transform_result != T_NONE
1280 && pterrain->transform_time != 0
1281 && effect_cumulative_max(EFT_TRANSFORM_POSSIBLE, &for_terr) > 0) {
1282 fc_snprintf(buf, sizeof(buf),
1283 PL_("%d turn", "%d turns", pterrain->transform_time),
1284 pterrain->transform_time);
1285 add_act_help_for_terrain(_("Trans. Rslt/Time"),
1286 terrain_name_translation(pterrain->transform_result),
1287 HELP_TERRAIN, buf);
1288 }
1289
1290 if (pterrain->irrigation_result == pterrain
1291 && pterrain->irrigation_time != 0
1292 && effect_cumulative_max(EFT_IRRIG_POSSIBLE, &for_terr) > 0) {
1293 help_extras_of_act_for_terrain(pterrain, ACTIVITY_IRRIGATE, _("Build as irrigation"));
1294 }
1295 if (pterrain->mining_result == pterrain
1296 && pterrain->mining_time != 0
1297 && effect_cumulative_max(EFT_MINING_POSSIBLE, &for_terr) > 0) {
1298 help_extras_of_act_for_terrain(pterrain, ACTIVITY_MINE, _("Build as mine"));
1299 }
1300 if (pterrain->road_time != 0) {
1301 help_extras_of_act_for_terrain(pterrain, ACTIVITY_GEN_ROAD, _("Build as road"));
1302 }
1303 if (pterrain->base_time != 0) {
1304 help_extras_of_act_for_terrain(pterrain, ACTIVITY_BASE, _("Build as base"));
1305 }
1306 gtk_widget_show(help_vbox);
1307 }
1308
1309 helptext_terrain(buf, sizeof(buf), client.conn.playing, pitem->text, pterrain);
1310
1311 gtk_text_buffer_set_text(help_text, buf, -1);
1312 gtk_widget_show(help_text_sw);
1313
1314 gtk_widget_show(help_ttable);
1315 }
1316
1317 /**************************************************************************
1318 Help page for extras.
1319 **************************************************************************/
help_update_extra(const struct help_item * pitem,char * title)1320 static void help_update_extra(const struct help_item *pitem, char *title)
1321 {
1322 char buf[8192];
1323 struct extra_type *pextra = extra_type_by_translated_name(title);
1324
1325 create_help_page(HELP_EXTRA);
1326
1327 buf[0] = '\0';
1328 if (pextra == NULL) {
1329 strcat(buf, pitem->text);
1330 } else {
1331 struct road_type *proad = extra_road_get(pextra);
1332
1333 /* Cost to build */
1334 if (pextra->buildable) {
1335 if (pextra->build_time != 0) {
1336 /* TRANS: "MP" = movement points */
1337 sprintf(buf, _("%d MP"), pextra->build_time);
1338 } else {
1339 /* TRANS: Build time depends on terrain. */
1340 sprintf(buf, _("Terrain specific"));
1341 }
1342 } else {
1343 sprintf(buf, "-");
1344 }
1345 gtk_label_set_text(GTK_LABEL(help_elabel[1]), buf);
1346 /* Conflicting extras */
1347 buf[0] = '\0';
1348 extra_type_iterate(pextra2) {
1349 if (!can_extras_coexist(pextra, pextra2)) {
1350 if (buf[0] != '\0') {
1351 strcat(buf, "/");
1352 }
1353 strcat(buf, extra_name_translation(pextra2));
1354 }
1355 } extra_type_iterate_end;
1356 /* TRANS: "Conflicts with: (none)" (extras) */
1357 gtk_label_set_text(GTK_LABEL(help_elabel[3]), buf[0] ? buf : _("(none)"));
1358
1359 /* Bonus */
1360 if (proad != NULL) {
1361 const char *bonus = NULL;
1362
1363 output_type_iterate(o) {
1364 if (proad->tile_incr[o] > 0) {
1365 /* TRANS: Road bonus depends on terrain. */
1366 bonus = _("Terrain specific");
1367 break;
1368 }
1369 } output_type_iterate_end;
1370 if (!bonus) {
1371 bonus = helptext_road_bonus_str(NULL, proad);
1372 }
1373 if (!bonus) {
1374 /* TRANS: No output bonus from a road */
1375 bonus = Q_("?bonus:None");
1376 }
1377 gtk_label_set_text(GTK_LABEL(help_elabel[5]), bonus);
1378 } else {
1379 gtk_label_set_text(GTK_LABEL(help_elabel[5]), Q_("?bonus:None"));
1380 }
1381
1382 helptext_extra(buf, sizeof(buf), client.conn.playing, pitem->text, pextra);
1383 }
1384 gtk_widget_show(help_etable);
1385
1386 gtk_text_buffer_set_text(help_text, buf, -1);
1387 gtk_widget_show(help_text_sw);
1388 }
1389
1390 /**************************************************************************
1391 This is currently just a text page, with special text:
1392 **************************************************************************/
help_update_specialist(const struct help_item * pitem,char * title)1393 static void help_update_specialist(const struct help_item *pitem,
1394 char *title)
1395 {
1396 char buf[8192];
1397 struct specialist *pspec = specialist_by_translated_name(title);
1398
1399 if (!pspec) {
1400 strcat(buf, pitem->text);
1401 } else {
1402 helptext_specialist(buf, sizeof(buf), client.conn.playing, pitem->text,
1403 pspec);
1404 }
1405 create_help_page(HELP_TEXT);
1406 gtk_text_buffer_set_text(help_text, buf, -1);
1407 gtk_widget_show(help_text_sw);
1408 }
1409
1410 /**************************************************************************
1411 This is currently just a text page, with special text:
1412 **************************************************************************/
help_update_government(const struct help_item * pitem,char * title)1413 static void help_update_government(const struct help_item *pitem,
1414 char *title)
1415 {
1416 char buf[8192];
1417 struct government *gov = government_by_translated_name(title);
1418
1419 if (!gov) {
1420 strcat(buf, pitem->text);
1421 } else {
1422 helptext_government(buf, sizeof(buf), client.conn.playing, pitem->text, gov);
1423 }
1424 create_help_page(HELP_TEXT);
1425 gtk_text_buffer_set_text(help_text, buf, -1);
1426 gtk_widget_show(help_text_sw);
1427 }
1428
1429 /**************************************************************************
1430 This is currently just a text page, with special text
1431 **************************************************************************/
help_update_nation(const struct help_item * pitem,char * title,struct nation_type * pnation)1432 static void help_update_nation(const struct help_item *pitem, char *title,
1433 struct nation_type *pnation)
1434 {
1435 char buf[4096];
1436
1437 if (!pnation) {
1438 strcat(buf, pitem->text);
1439 } else {
1440 helptext_nation(buf, sizeof(buf), pnation, pitem->text);
1441 }
1442 create_help_page(HELP_TEXT);
1443 gtk_text_buffer_set_text(help_text, buf, -1);
1444 gtk_widget_show(help_text_sw);
1445 }
1446
1447 /**************************************************************************
1448 Display updated help dialog
1449 **************************************************************************/
help_update_dialog(const struct help_item * pitem)1450 static void help_update_dialog(const struct help_item *pitem)
1451 {
1452 char *top;
1453
1454 /* figure out what kind of item is required for pitem ingo */
1455
1456 for (top = pitem->topic; *top == ' '; top++) {
1457 /* nothing */
1458 }
1459
1460 help_box_hide();
1461 gtk_text_buffer_set_text(help_text, "", -1);
1462
1463 switch(pitem->type) {
1464 case HELP_IMPROVEMENT:
1465 help_update_improvement(pitem, top);
1466 break;
1467 case HELP_WONDER:
1468 help_update_wonder(pitem, top);
1469 break;
1470 case HELP_UNIT:
1471 help_update_unit_type(pitem, top);
1472 break;
1473 case HELP_TECH:
1474 help_update_tech(pitem, top);
1475 break;
1476 case HELP_TERRAIN:
1477 help_update_terrain(pitem, top);
1478 break;
1479 case HELP_EXTRA:
1480 help_update_extra(pitem, top);
1481 break;
1482 case HELP_SPECIALIST:
1483 help_update_specialist(pitem, top);
1484 break;
1485 case HELP_GOVERNMENT:
1486 help_update_government(pitem, top);
1487 break;
1488 case HELP_NATIONS:
1489 help_update_nation(pitem, top, nation_by_translated_plural(top));
1490 break;
1491 case HELP_TEXT:
1492 default:
1493 /* it was a pure text item */
1494 create_help_page(HELP_TEXT);
1495
1496 gtk_text_buffer_set_text(help_text, pitem->text, -1);
1497 gtk_widget_show(help_text_sw);
1498 break;
1499 }
1500 set_title_topic(pitem->topic);
1501
1502 gtk_widget_show(help_box);
1503 }
1504
1505 /**************************************************************************
1506 Add item at path to selection and scroll to its cell
1507 **************************************************************************/
help_item_zoom(GtkTreePath * path)1508 static void help_item_zoom(GtkTreePath *path)
1509 {
1510 GtkTreeModel *model;
1511 GtkTreeIter it, child, item;
1512 GtkTreeSelection *selection;
1513
1514 model = gtk_tree_view_get_model(GTK_TREE_VIEW(help_view));
1515 gtk_tree_model_get_iter(model, &item, path);
1516
1517 for (child=item; gtk_tree_model_iter_parent(model, &it, &child); child=it) {
1518 GtkTreePath *it_path;
1519
1520 it_path = gtk_tree_model_get_path(model, &it);
1521 gtk_tree_view_expand_row(GTK_TREE_VIEW(help_view), it_path, TRUE);
1522 gtk_tree_path_free(it_path);
1523 }
1524
1525 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(help_view));
1526 gtk_tree_selection_select_iter(selection, &item);
1527 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(help_view), path, NULL,
1528 TRUE, 0.0, 0.0);
1529 }
1530
1531 /****************************************************************
1532 Return path to help item.
1533 *****************************************************************/
help_item_path(const struct help_item * pitem)1534 static GtkTreePath *help_item_path(const struct help_item *pitem)
1535 {
1536 GtkTreePath *path;
1537 bool next;
1538
1539 path = gtk_tree_path_new_first();
1540 next = FALSE;
1541 help_items_iterate(pitem2) {
1542 const char *s;
1543 int depth;
1544
1545 for (s = pitem2->topic; *s == ' '; s++) {
1546 /* nothing */
1547 }
1548 depth = s - pitem2->topic + 1;
1549
1550 while (depth < gtk_tree_path_get_depth(path)) {
1551 gtk_tree_path_up(path);
1552 gtk_tree_path_next(path);
1553 next = FALSE;
1554 }
1555 while (depth > gtk_tree_path_get_depth(path)) {
1556 gtk_tree_path_down(path);
1557 next = FALSE;
1558 }
1559
1560 if (next) {
1561 gtk_tree_path_next(path);
1562 }
1563
1564 if (pitem == pitem2)
1565 break;
1566
1567 next = TRUE;
1568 } help_items_iterate_end;
1569
1570 return path;
1571 }
1572
1573 /****************************************************************
1574 Add item to selection
1575 *****************************************************************/
select_help_item_string(const char * item,enum help_page_type htype)1576 static void select_help_item_string(const char *item, enum help_page_type htype)
1577 {
1578 const struct help_item *pitem;
1579 int idx;
1580 GtkTreePath *path;
1581 GtkTreeViewColumn *col;
1582
1583 if (!(pitem = get_help_item_spec(item, htype, &idx))) {
1584 return;
1585 }
1586
1587 path = help_item_path(pitem);
1588 help_item_zoom(path);
1589
1590 col = gtk_tree_view_get_column(GTK_TREE_VIEW(help_view), 0);
1591 gtk_tree_view_set_cursor(GTK_TREE_VIEW(help_view), path, col, FALSE);
1592 gtk_tree_path_free(path);
1593 }
1594
1595 /**************************************************************************
1596 Set sensitivity of help dialog response buttons.
1597 **************************************************************************/
help_command_update(void)1598 static void help_command_update(void)
1599 {
1600 GtkDialog *dialog = GTK_DIALOG(help_dialog_shell);
1601
1602 if (help_history_pos < 0) {
1603 gtk_dialog_set_response_sensitive(dialog, 1, FALSE);
1604 gtk_dialog_set_response_sensitive(dialog, 2, FALSE);
1605 } else {
1606 gtk_dialog_set_response_sensitive(dialog, 1, TRUE);
1607 gtk_dialog_set_response_sensitive(dialog, 2, TRUE);
1608
1609 if (help_history_pos == 0) {
1610 gtk_dialog_set_response_sensitive(dialog, 1, FALSE);
1611 }
1612 if (help_history_pos >= help_history->len - 1) {
1613 gtk_dialog_set_response_sensitive(dialog, 2, FALSE);
1614 }
1615 }
1616 }
1617
1618 /**************************************************************************
1619 User gave response to help dialog
1620 **************************************************************************/
help_command_callback(GtkWidget * w,gint response_id)1621 static void help_command_callback(GtkWidget *w, gint response_id)
1622 {
1623 GtkTreePath *path;
1624 const struct help_item *pitem;
1625
1626 if (response_id == 1) {
1627 if (help_history_pos > 0) {
1628 help_history_pos--;
1629
1630 pitem = g_ptr_array_index(help_history, help_history_pos);
1631 path = help_item_path(pitem);
1632 help_item_zoom(path);
1633 help_update_dialog(pitem);
1634 help_command_update();
1635 }
1636 } else if (response_id == 2) {
1637 if (help_history_pos < help_history->len - 1) {
1638 help_history_pos++;
1639
1640 pitem = g_ptr_array_index(help_history, help_history_pos);
1641 path = help_item_path(pitem);
1642 help_item_zoom(path);
1643 help_update_dialog(pitem);
1644 help_command_update();
1645 }
1646 } else {
1647 gtk_widget_destroy(help_dialog_shell);
1648 }
1649 }
1650