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