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