1 /***********************************************************************
2  Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team
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 <gtk/gtk.h>
19 
20 /* utility */
21 #include "astring.h"
22 #include "support.h"
23 
24 /* common */
25 #include "actions.h"
26 #include "game.h"
27 #include "traderoutes.h"
28 #include "movement.h"
29 #include "research.h"
30 #include "unit.h"
31 #include "unitlist.h"
32 
33 /* client */
34 #include "dialogs_g.h"
35 #include "chatline.h"
36 #include "choice_dialog.h"
37 #include "client_main.h"
38 #include "climisc.h"
39 #include "connectdlg_common.h"
40 #include "control.h"
41 #include "gui_main.h"
42 #include "gui_stuff.h"
43 #include "mapview.h"
44 #include "packhand.h"
45 #include "text.h"
46 
47 /* client/gui-gtk-3.22 */
48 #include "citydlg.h"
49 #include "dialogs.h"
50 #include "unitselunitdlg.h"
51 #include "wldlg.h"
52 
53 /* Locations for non action enabler controlled buttons. */
54 #define BUTTON_MOVE ACTION_COUNT
55 #define BUTTON_NEW_UNIT_TGT BUTTON_MOVE + 1
56 #define BUTTON_LOCATION BUTTON_MOVE + 2
57 #define BUTTON_WAIT BUTTON_MOVE + 3
58 #define BUTTON_CANCEL BUTTON_MOVE + 4
59 #define BUTTON_COUNT BUTTON_MOVE + 5
60 
61 #define BUTTON_NOT_THERE -1
62 
63 
64 static GtkWidget *act_sel_dialog;
65 static int action_button_map[BUTTON_COUNT];
66 
67 static int actor_unit_id;
68 static int target_ids[ATK_COUNT];
69 static bool is_more_user_input_needed = FALSE;
70 static bool did_not_decide = FALSE;
71 static bool action_selection_restart = FALSE;
72 
73 static GtkWidget  *spy_tech_shell;
74 
75 static GtkWidget  *spy_sabotage_shell;
76 
77 /* A structure to hold parameters for actions inside the GUI in stead of
78  * storing the needed data in a global variable. */
79 struct action_data {
80   int actor_unit_id;
81   int target_city_id;
82   int target_unit_id;
83   int target_tile_id;
84   int value;
85 };
86 
87 /****************************************************************
88   Create a new action data structure that can be stored in the
89   dialogs.
90 *****************************************************************/
act_data(int actor_id,int target_city_id,int target_unit_id,int target_tile_id,int value)91 static struct action_data *act_data(int actor_id,
92                                     int target_city_id,
93                                     int target_unit_id,
94                                     int target_tile_id,
95                                     int value)
96 {
97   struct action_data *data = fc_malloc(sizeof(*data));
98 
99   data->actor_unit_id = actor_id;
100   data->target_city_id = target_city_id;
101   data->target_unit_id = target_unit_id;
102   data->target_tile_id = target_tile_id;
103   data->value = value;
104 
105   return data;
106 }
107 
108 /**************************************************************************
109   Move the queue of units that need user input forward unless the current
110   unit is going to need more input.
111 **************************************************************************/
diplomat_queue_handle_primary(void)112 static void diplomat_queue_handle_primary(void)
113 {
114   if (!is_more_user_input_needed) {
115     /* The client isn't waiting for information for any unanswered follow
116      * up questions. */
117 
118     struct unit *actor_unit;
119 
120     if ((actor_unit = game_unit_by_number(actor_unit_id))) {
121       /* The action selection dialog wasn't closed because the actor unit
122        * was lost. */
123 
124       /* The probabilities didn't just disappear, right? */
125       fc_assert_action(actor_unit->client.act_prob_cache,
126                        client_unit_init_act_prob_cache(actor_unit));
127 
128       FC_FREE(actor_unit->client.act_prob_cache);
129     }
130 
131     if (action_selection_restart) {
132       /* The action selection dialog was closed but only so it can be
133        * redrawn with fresh data. */
134 
135       action_selection_restart = FALSE;
136     } else {
137       /* The action selection process is over, at least for now. */
138       action_selection_no_longer_in_progress(actor_unit_id);
139     }
140 
141     if (did_not_decide) {
142       /* The action selection dialog was closed but the player didn't
143        * decide what the unit should do. */
144 
145       /* Reset so the next action selection dialog does the right thing. */
146       did_not_decide = FALSE;
147     } else {
148       /* An action, or no action at all, was selected. */
149       action_decision_clear_want(actor_unit_id);
150       action_selection_next_in_focus(actor_unit_id);
151     }
152   }
153 }
154 
155 /**************************************************************************
156   Move the queue of units that need user input forward since the
157   current unit doesn't require the extra input any more.
158 **************************************************************************/
diplomat_queue_handle_secondary(void)159 static void diplomat_queue_handle_secondary(void)
160 {
161   /* Stop waiting. Move on to the next queued unit. */
162   is_more_user_input_needed = FALSE;
163   diplomat_queue_handle_primary();
164 }
165 
166 /**************************************************************************
167   Let the non shared client code know that the action selection process
168   no longer is in progress for the specified unit.
169 
170   This allows the client to clean up any client specific assumptions.
171 **************************************************************************/
action_selection_no_longer_in_progress_gui_specific(int actor_id)172 void action_selection_no_longer_in_progress_gui_specific(int actor_id)
173 {
174   /* Stop assuming the answer to a follow up question will arrive. */
175   is_more_user_input_needed = FALSE;
176 }
177 
178 /****************************************************************
179   User selected enter market place from caravan dialog
180 *****************************************************************/
caravan_marketplace_callback(GtkWidget * w,gpointer data)181 static void caravan_marketplace_callback(GtkWidget *w, gpointer data)
182 {
183   struct action_data *args = (struct action_data *)data;
184 
185   if (NULL != game_unit_by_number(args->actor_unit_id)
186       && NULL != game_city_by_number(args->target_city_id)) {
187     request_do_action(ACTION_MARKETPLACE, args->actor_unit_id,
188                       args->target_city_id, 0);
189   }
190 
191   gtk_widget_destroy(act_sel_dialog);
192   free(args);
193 }
194 
195 /****************************************************************
196   User selected traderoute from caravan dialog
197 *****************************************************************/
caravan_establish_trade_callback(GtkWidget * w,gpointer data)198 static void caravan_establish_trade_callback(GtkWidget *w, gpointer data)
199 {
200   struct action_data *args = (struct action_data *)data;
201 
202   if (NULL != game_unit_by_number(args->actor_unit_id)
203       && NULL != game_city_by_number(args->target_city_id)) {
204     request_do_action(ACTION_TRADE_ROUTE, args->actor_unit_id,
205                       args->target_city_id, 0);
206   }
207 
208   gtk_widget_destroy(act_sel_dialog);
209   free(args);
210 }
211 
212 /****************************************************************
213   User selected wonder building helping from caravan dialog
214 *****************************************************************/
caravan_help_build_wonder_callback(GtkWidget * w,gpointer data)215 static void caravan_help_build_wonder_callback(GtkWidget *w, gpointer data)
216 {
217   struct action_data *args = (struct action_data *)data;
218 
219   if (NULL != game_unit_by_number(args->actor_unit_id)
220       && NULL != game_city_by_number(args->target_city_id)) {
221     request_do_action(ACTION_HELP_WONDER, args->actor_unit_id,
222                       args->target_city_id, 0);
223   }
224 
225   gtk_widget_destroy(act_sel_dialog);
226   free(args);
227 }
228 
229 /**********************************************************************
230   User responded to bribe dialog
231 **********************************************************************/
bribe_response(GtkWidget * w,gint response,gpointer data)232 static void bribe_response(GtkWidget *w, gint response, gpointer data)
233 {
234   struct action_data *args = (struct action_data *)data;
235 
236   if (response == GTK_RESPONSE_YES) {
237     request_do_action(ACTION_SPY_BRIBE_UNIT, args->actor_unit_id,
238                       args->target_unit_id, 0);
239   }
240 
241   gtk_widget_destroy(w);
242   free(args);
243 
244   /* The user have answered the follow up question. Move on. */
245   diplomat_queue_handle_secondary();
246 }
247 
248 /****************************************************************
249   Ask the server how much the bribe is
250 *****************************************************************/
diplomat_bribe_callback(GtkWidget * w,gpointer data)251 static void diplomat_bribe_callback(GtkWidget *w, gpointer data)
252 {
253   struct action_data *args = (struct action_data *)data;
254 
255   if (NULL != game_unit_by_number(args->actor_unit_id)
256       && NULL != game_unit_by_number(args->target_unit_id)) {
257     request_action_details(ACTION_SPY_BRIBE_UNIT, args->actor_unit_id,
258                            args->target_unit_id);
259   }
260 
261   /* Wait for the server's reply before moving on to the next unit that
262    * needs to know what action to take. */
263   is_more_user_input_needed = TRUE;
264 
265   gtk_widget_destroy(act_sel_dialog);
266   free(args);
267 }
268 
269 /*************************************************************************
270   Popup unit bribe dialog
271 **************************************************************************/
popup_bribe_dialog(struct unit * actor,struct unit * punit,int cost)272 void popup_bribe_dialog(struct unit *actor, struct unit *punit, int cost)
273 {
274   GtkWidget *shell;
275   char buf[1024];
276 
277   fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Treasury contains %d gold.",
278                                         "Treasury contains %d gold.",
279                                         client_player()->economic.gold),
280               client_player()->economic.gold);
281 
282   if (cost <= client_player()->economic.gold) {
283     shell = gtk_message_dialog_new(NULL, 0,
284       GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
285       /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */
286       PL_("Bribe unit for %d gold?\n%s",
287           "Bribe unit for %d gold?\n%s", cost), cost, buf);
288     gtk_window_set_title(GTK_WINDOW(shell), _("Bribe Enemy Unit"));
289     setup_dialog(shell, toplevel);
290   } else {
291     shell = gtk_message_dialog_new(NULL, 0,
292       GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
293       /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */
294       PL_("Bribing the unit costs %d gold.\n%s",
295           "Bribing the unit costs %d gold.\n%s", cost), cost, buf);
296     gtk_window_set_title(GTK_WINDOW(shell), _("Traitors Demand Too Much!"));
297     setup_dialog(shell, toplevel);
298   }
299   gtk_window_present(GTK_WINDOW(shell));
300 
301   g_signal_connect(shell, "response", G_CALLBACK(bribe_response),
302                    act_data(actor->id, 0, punit->id, 0, cost));
303 }
304 
305 /****************************************************************
306   User selected sabotaging from choice dialog
307 *****************************************************************/
diplomat_sabotage_callback(GtkWidget * w,gpointer data)308 static void diplomat_sabotage_callback(GtkWidget *w, gpointer data)
309 {
310   struct action_data *args = (struct action_data *)data;
311 
312   if (NULL != game_unit_by_number(args->actor_unit_id)
313       && NULL != game_city_by_number(args->target_city_id)) {
314     request_do_action(ACTION_SPY_SABOTAGE_CITY, args->actor_unit_id,
315                       args->target_city_id, B_LAST + 1);
316   }
317 
318   gtk_widget_destroy(act_sel_dialog);
319   free(args);
320 }
321 
322 /****************************************************************
323   User selected investigating from choice dialog
324 *****************************************************************/
diplomat_investigate_callback(GtkWidget * w,gpointer data)325 static void diplomat_investigate_callback(GtkWidget *w, gpointer data)
326 {
327   struct action_data *args = (struct action_data *)data;
328 
329   if (NULL != game_city_by_number(args->target_city_id)
330       && NULL != game_unit_by_number(args->actor_unit_id)) {
331     request_do_action(ACTION_SPY_INVESTIGATE_CITY, args->actor_unit_id,
332                       args->target_city_id, 0);
333   }
334 
335   gtk_widget_destroy(act_sel_dialog);
336   free(args);
337 }
338 
339 /****************************************************************
340   User selected unit sabotaging from choice dialog
341 *****************************************************************/
spy_sabotage_unit_callback(GtkWidget * w,gpointer data)342 static void spy_sabotage_unit_callback(GtkWidget *w, gpointer data)
343 {
344   struct action_data *args = (struct action_data *)data;
345 
346   request_do_action(ACTION_SPY_SABOTAGE_UNIT, args->actor_unit_id,
347                     args->target_unit_id, 0);
348 
349   gtk_widget_destroy(act_sel_dialog);
350   free(args);
351 }
352 
353 /****************************************************************
354   User selected embassy establishing from choice dialog
355 *****************************************************************/
diplomat_embassy_callback(GtkWidget * w,gpointer data)356 static void diplomat_embassy_callback(GtkWidget *w, gpointer data)
357 {
358   struct action_data *args = (struct action_data *)data;
359 
360   if (NULL != game_unit_by_number(args->actor_unit_id)
361       && NULL != game_city_by_number(args->target_city_id)) {
362     request_do_action(ACTION_ESTABLISH_EMBASSY, args->actor_unit_id,
363                       args->target_city_id, 0);
364   }
365 
366   gtk_widget_destroy(act_sel_dialog);
367   free(args);
368 }
369 
370 /****************************************************************
371   User selected to steal gold from choice dialog
372 *****************************************************************/
spy_steal_gold_callback(GtkWidget * w,gpointer data)373 static void spy_steal_gold_callback(GtkWidget *w, gpointer data)
374 {
375   struct action_data *args = (struct action_data *)data;
376 
377   if (NULL != game_unit_by_number(args->actor_unit_id)
378       && NULL != game_city_by_number(args->target_city_id)) {
379     request_do_action(ACTION_SPY_STEAL_GOLD, args->actor_unit_id,
380                       args->target_city_id, 0);
381   }
382 
383   gtk_widget_destroy(act_sel_dialog);
384   free(args);
385 }
386 
387 /****************************************************************
388   User selected poisoning from choice dialog
389 *****************************************************************/
spy_poison_callback(GtkWidget * w,gpointer data)390 static void spy_poison_callback(GtkWidget *w, gpointer data)
391 {
392   struct action_data *args = (struct action_data *)data;
393 
394   if (NULL != game_unit_by_number(args->actor_unit_id)
395       && NULL != game_city_by_number(args->target_city_id)) {
396     request_do_action(ACTION_SPY_POISON, args->actor_unit_id,
397                       args->target_city_id, 0);
398   }
399 
400   gtk_widget_destroy(act_sel_dialog);
401   free(args);
402 }
403 
404 /****************************************************************
405   User selected stealing from choice dialog
406 *****************************************************************/
diplomat_steal_callback(GtkWidget * w,gpointer data)407 static void diplomat_steal_callback(GtkWidget *w, gpointer data)
408 {
409   struct action_data *args = (struct action_data *)data;
410 
411   if (NULL != game_unit_by_number(args->actor_unit_id)
412       && NULL != game_city_by_number(args->target_city_id)) {
413     request_do_action(ACTION_SPY_STEAL_TECH, args->actor_unit_id,
414                       args->target_city_id, A_UNSET);
415   }
416 
417   gtk_widget_destroy(act_sel_dialog);
418   free(args);
419 }
420 
421 /****************************************************************
422   User responded to steal advances dialog
423 *****************************************************************/
spy_advances_response(GtkWidget * w,gint response,gpointer data)424 static void spy_advances_response(GtkWidget *w, gint response,
425                                   gpointer data)
426 {
427   struct action_data *args = (struct action_data *)data;
428 
429   if (response == GTK_RESPONSE_ACCEPT && args->value > 0) {
430     if (NULL != game_unit_by_number(args->actor_unit_id)
431         && NULL != game_city_by_number(args->target_city_id)) {
432       if (args->value == A_UNSET) {
433         /* This is the untargeted version. */
434         request_do_action(ACTION_SPY_STEAL_TECH,
435                           args->actor_unit_id, args->target_city_id,
436                           args->value);
437       } else {
438         /* This is the targeted version. */
439         request_do_action(ACTION_SPY_TARGETED_STEAL_TECH,
440                           args->actor_unit_id, args->target_city_id,
441                           args->value);
442       }
443     }
444   }
445 
446   gtk_widget_destroy(spy_tech_shell);
447   spy_tech_shell = NULL;
448   free(data);
449 
450   /* The user have answered the follow up question. Move on. */
451   diplomat_queue_handle_secondary();
452 }
453 
454 /****************************************************************
455   User selected entry in steal advances dialog
456 *****************************************************************/
spy_advances_callback(GtkTreeSelection * select,gpointer data)457 static void spy_advances_callback(GtkTreeSelection *select,
458                                   gpointer data)
459 {
460   struct action_data *args = (struct action_data *)data;
461 
462   GtkTreeModel *model;
463   GtkTreeIter it;
464 
465   if (gtk_tree_selection_get_selected(select, &model, &it)) {
466     gtk_tree_model_get(model, &it, 1, &(args->value), -1);
467 
468     gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_tech_shell),
469       GTK_RESPONSE_ACCEPT, TRUE);
470   } else {
471     args->value = 0;
472 
473     gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_tech_shell),
474       GTK_RESPONSE_ACCEPT, FALSE);
475   }
476 }
477 
478 /****************************************************************
479   Create spy's tech stealing dialog
480 *****************************************************************/
create_advances_list(struct player * pplayer,struct player * pvictim,struct action_data * args)481 static void create_advances_list(struct player *pplayer,
482 				 struct player *pvictim,
483 				 struct action_data *args)
484 {
485   GtkWidget *sw, *label, *vbox, *view;
486   GtkListStore *store;
487   GtkCellRenderer *rend;
488   GtkTreeViewColumn *col;
489 
490   struct unit *actor_unit = game_unit_by_number(args->actor_unit_id);
491 
492   spy_tech_shell = gtk_dialog_new_with_buttons(_("Steal Technology"),
493                                                NULL, 0,
494                                                _("_Cancel"), GTK_RESPONSE_CANCEL,
495                                                _("_Steal"), GTK_RESPONSE_ACCEPT,
496                                                NULL);
497   setup_dialog(spy_tech_shell, toplevel);
498   gtk_window_set_position(GTK_WINDOW(spy_tech_shell), GTK_WIN_POS_MOUSE);
499 
500   gtk_dialog_set_default_response(GTK_DIALOG(spy_tech_shell),
501 				  GTK_RESPONSE_ACCEPT);
502 
503   label = gtk_frame_new(_("Select Advance to Steal"));
504   gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(spy_tech_shell))), label);
505 
506   vbox = gtk_grid_new();
507   gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox),
508                                  GTK_ORIENTATION_VERTICAL);
509   gtk_grid_set_row_spacing(GTK_GRID(vbox), 6);
510   gtk_container_add(GTK_CONTAINER(label), vbox);
511 
512   store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
513 
514   view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
515   gtk_widget_set_hexpand(view, TRUE);
516   gtk_widget_set_vexpand(view, TRUE);
517   g_object_unref(store);
518   gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
519 
520   rend = gtk_cell_renderer_text_new();
521   col = gtk_tree_view_column_new_with_attributes(NULL, rend,
522 						 "text", 0, NULL);
523   gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
524 
525   label = g_object_new(GTK_TYPE_LABEL,
526     "use-underline", TRUE,
527     "mnemonic-widget", view,
528     "label", _("_Advances:"),
529     "xalign", 0.0,
530     "yalign", 0.5,
531     NULL);
532   gtk_container_add(GTK_CONTAINER(vbox), label);
533 
534   sw = gtk_scrolled_window_new(NULL, NULL);
535   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
536 				      GTK_SHADOW_ETCHED_IN);
537   gtk_container_add(GTK_CONTAINER(sw), view);
538 
539   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
540     GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
541   gtk_widget_set_size_request(sw, -1, 200);
542 
543   gtk_container_add(GTK_CONTAINER(vbox), sw);
544 
545   /* Now populate the list */
546   if (pvictim) { /* you don't want to know what lag can do -- Syela */
547     const struct research *presearch = research_get(pplayer);
548     const struct research *vresearch = research_get(pvictim);
549     GtkTreeIter it;
550     GValue value = { 0, };
551 
552     advance_index_iterate(A_FIRST, i) {
553       if (research_invention_gettable(presearch, i,
554                                       game.info.tech_steal_allow_holes)
555           && research_invention_state(vresearch, i) == TECH_KNOWN
556           && research_invention_state(presearch, i) != TECH_KNOWN) {
557 	gtk_list_store_append(store, &it);
558 
559 	g_value_init(&value, G_TYPE_STRING);
560         g_value_set_static_string(&value, research_advance_name_translation
561                                               (presearch, i));
562 	gtk_list_store_set_value(store, &it, 0, &value);
563 	g_value_unset(&value);
564 	gtk_list_store_set(store, &it, 1, i, -1);
565       }
566     } advance_index_iterate_end;
567 
568     if (action_prob_possible(
569           actor_unit->client.act_prob_cache[ACTION_SPY_STEAL_TECH])) {
570       gtk_list_store_append(store, &it);
571 
572       g_value_init(&value, G_TYPE_STRING);
573       {
574         struct astring str = ASTRING_INIT;
575         /* TRANS: %s is a unit name, e.g., Spy */
576         astr_set(&str, _("At %s's Discretion"),
577                  unit_name_translation(actor_unit));
578         g_value_set_string(&value, astr_str(&str));
579         astr_free(&str);
580       }
581       gtk_list_store_set_value(store, &it, 0, &value);
582       g_value_unset(&value);
583       gtk_list_store_set(store, &it, 1, A_UNSET, -1);
584     }
585   }
586 
587   gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_tech_shell),
588     GTK_RESPONSE_ACCEPT, FALSE);
589 
590   gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(spy_tech_shell)));
591 
592   g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), "changed",
593                    G_CALLBACK(spy_advances_callback), args);
594   g_signal_connect(spy_tech_shell, "response",
595                    G_CALLBACK(spy_advances_response), args);
596 
597   args->value = 0;
598 
599   gtk_tree_view_focus(GTK_TREE_VIEW(view));
600 }
601 
602 /****************************************************************
603   User has responded to spy's sabotage building dialog
604 *****************************************************************/
spy_improvements_response(GtkWidget * w,gint response,gpointer data)605 static void spy_improvements_response(GtkWidget *w, gint response, gpointer data)
606 {
607   struct action_data *args = (struct action_data *)data;
608 
609   if (response == GTK_RESPONSE_ACCEPT && args->value > -2) {
610     if (NULL != game_unit_by_number(args->actor_unit_id)
611         && NULL != game_city_by_number(args->target_city_id)) {
612       if (args->value == B_LAST) {
613         /* This is the untargeted version. */
614         request_do_action(ACTION_SPY_SABOTAGE_CITY,
615                           args->actor_unit_id,
616                           args->target_city_id,
617                           args->value + 1);
618       } else {
619         /* This is the targeted version. */
620         request_do_action(ACTION_SPY_TARGETED_SABOTAGE_CITY,
621                           args->actor_unit_id,
622                           args->target_city_id,
623                           args->value + 1);
624       }
625     }
626   }
627 
628   gtk_widget_destroy(spy_sabotage_shell);
629   spy_sabotage_shell = NULL;
630   free(args);
631 
632   /* The user have answered the follow up question. Move on. */
633   diplomat_queue_handle_secondary();
634 }
635 
636 /****************************************************************
637   User has selected new building from spy's sabotage dialog
638 *****************************************************************/
spy_improvements_callback(GtkTreeSelection * select,gpointer data)639 static void spy_improvements_callback(GtkTreeSelection *select, gpointer data)
640 {
641   struct action_data *args = (struct action_data *)data;
642 
643   GtkTreeModel *model;
644   GtkTreeIter it;
645 
646   if (gtk_tree_selection_get_selected(select, &model, &it)) {
647     gtk_tree_model_get(model, &it, 1, &(args->value), -1);
648 
649     gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_sabotage_shell),
650       GTK_RESPONSE_ACCEPT, TRUE);
651   } else {
652     args->value = -2;
653 
654     gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_sabotage_shell),
655       GTK_RESPONSE_ACCEPT, FALSE);
656   }
657 }
658 
659 /****************************************************************
660   Creates spy's building sabotaging dialog
661 *****************************************************************/
create_improvements_list(struct player * pplayer,struct city * pcity,struct action_data * args)662 static void create_improvements_list(struct player *pplayer,
663 				     struct city *pcity,
664 				     struct action_data *args)
665 {
666   GtkWidget *sw, *label, *vbox, *view;
667   GtkListStore *store;
668   GtkCellRenderer *rend;
669   GtkTreeViewColumn *col;
670   GtkTreeIter it;
671 
672   struct unit *actor_unit = game_unit_by_number(args->actor_unit_id);
673 
674   spy_sabotage_shell = gtk_dialog_new_with_buttons(_("Sabotage Improvements"),
675                                                    NULL, 0,
676                                                    _("_Cancel"), GTK_RESPONSE_CANCEL,
677                                                    _("_Sabotage"), GTK_RESPONSE_ACCEPT,
678                                                    NULL);
679   setup_dialog(spy_sabotage_shell, toplevel);
680   gtk_window_set_position(GTK_WINDOW(spy_sabotage_shell), GTK_WIN_POS_MOUSE);
681 
682   gtk_dialog_set_default_response(GTK_DIALOG(spy_sabotage_shell),
683 				  GTK_RESPONSE_ACCEPT);
684 
685   label = gtk_frame_new(_("Select Improvement to Sabotage"));
686   gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(spy_sabotage_shell))), label);
687 
688   vbox = gtk_grid_new();
689   gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox),
690                                  GTK_ORIENTATION_VERTICAL);
691   gtk_grid_set_row_spacing(GTK_GRID(vbox), 6);
692   gtk_container_add(GTK_CONTAINER(label), vbox);
693 
694   store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
695 
696   view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
697   gtk_widget_set_hexpand(view, TRUE);
698   gtk_widget_set_vexpand(view, TRUE);
699   g_object_unref(store);
700   gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
701 
702   rend = gtk_cell_renderer_text_new();
703   col = gtk_tree_view_column_new_with_attributes(NULL, rend,
704 						 "text", 0, NULL);
705   gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
706 
707   label = g_object_new(GTK_TYPE_LABEL,
708     "use-underline", TRUE,
709     "mnemonic-widget", view,
710     "label", _("_Improvements:"),
711     "xalign", 0.0,
712     "yalign", 0.5,
713     NULL);
714   gtk_container_add(GTK_CONTAINER(vbox), label);
715 
716   sw = gtk_scrolled_window_new(NULL, NULL);
717   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
718 				      GTK_SHADOW_ETCHED_IN);
719   gtk_container_add(GTK_CONTAINER(sw), view);
720 
721   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
722     GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
723   gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw), 200);
724 
725   gtk_container_add(GTK_CONTAINER(vbox), sw);
726 
727   /* Now populate the list */
728   gtk_list_store_append(store, &it);
729   gtk_list_store_set(store, &it, 0, _("City Production"), 1, -1, -1);
730 
731   city_built_iterate(pcity, pimprove) {
732     if (pimprove->sabotage > 0) {
733       gtk_list_store_append(store, &it);
734       gtk_list_store_set(store, &it,
735                          0, city_improvement_name_translation(pcity, pimprove),
736                          1, improvement_number(pimprove),
737                          -1);
738     }
739   } city_built_iterate_end;
740 
741   if (action_prob_possible(
742         actor_unit->client.act_prob_cache[ACTION_SPY_SABOTAGE_CITY])) {
743     struct astring str = ASTRING_INIT;
744 
745     gtk_list_store_append(store, &it);
746 
747     /* TRANS: %s is a unit name, e.g., Spy */
748     astr_set(&str, _("At %s's Discretion"),
749              unit_name_translation(actor_unit));
750     gtk_list_store_set(store, &it, 0, astr_str(&str), 1, B_LAST, -1);
751 
752     astr_free(&str);
753   }
754 
755   gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_sabotage_shell),
756     GTK_RESPONSE_ACCEPT, FALSE);
757 
758   gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(spy_sabotage_shell)));
759 
760   g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), "changed",
761                    G_CALLBACK(spy_improvements_callback), args);
762   g_signal_connect(spy_sabotage_shell, "response",
763                    G_CALLBACK(spy_improvements_response), args);
764 
765   args->value = -2;
766 
767   gtk_tree_view_focus(GTK_TREE_VIEW(view));
768 }
769 
770 /****************************************************************
771   Popup tech stealing dialog with list of possible techs
772 *****************************************************************/
spy_steal_popup(GtkWidget * w,gpointer data)773 static void spy_steal_popup(GtkWidget *w, gpointer data)
774 {
775   struct action_data *args = (struct action_data *)data;
776 
777   struct city *pvcity = game_city_by_number(args->target_city_id);
778   struct player *pvictim = NULL;
779 
780   if (pvcity) {
781     pvictim = city_owner(pvcity);
782   }
783 
784 /* it is concievable that pvcity will not be found, because something
785 has happened to the city during latency.  Therefore we must initialize
786 pvictim to NULL and account for !pvictim in create_advances_list. -- Syela */
787 
788   /* FIXME: Don't discard the second tech choice dialog. */
789   if (!spy_tech_shell) {
790     create_advances_list(client.conn.playing, pvictim, args);
791     gtk_window_present(GTK_WINDOW(spy_tech_shell));
792   } else {
793     free(args);
794   }
795 
796   /* Wait for the server's reply before moving on to the next unit that
797    * needs to know what action to take. */
798   is_more_user_input_needed = TRUE;
799 
800   gtk_widget_destroy(act_sel_dialog);
801 }
802 
803 /****************************************************************
804  Requests up-to-date list of improvements, the return of
805  which will trigger the popup_sabotage_dialog() function.
806 *****************************************************************/
spy_request_sabotage_list(GtkWidget * w,gpointer data)807 static void spy_request_sabotage_list(GtkWidget *w, gpointer data)
808 {
809   struct action_data *args = (struct action_data *)data;
810 
811   if (NULL != game_unit_by_number(args->actor_unit_id)
812       && NULL != game_city_by_number(args->target_city_id)) {
813     request_action_details(ACTION_SPY_TARGETED_SABOTAGE_CITY,
814                            args->actor_unit_id,
815                            args->target_city_id);
816   }
817 
818   /* Wait for the server's reply before moving on to the next unit that
819    * needs to know what action to take. */
820   is_more_user_input_needed = TRUE;
821 
822   gtk_widget_destroy(act_sel_dialog);
823   free(args);
824 }
825 
826 /*************************************************************************
827  Pops-up the Spy sabotage dialog, upon return of list of
828  available improvements requested by the above function.
829 **************************************************************************/
popup_sabotage_dialog(struct unit * actor,struct city * pcity)830 void popup_sabotage_dialog(struct unit *actor, struct city *pcity)
831 {
832   /* FIXME: Don't discard the second target choice dialog. */
833   if (!spy_sabotage_shell) {
834     create_improvements_list(client.conn.playing, pcity,
835                              act_data(actor->id, pcity->id, 0, 0, 0));
836     gtk_window_present(GTK_WINDOW(spy_sabotage_shell));
837   }
838 }
839 
840 /****************************************************************
841 ...  Ask the server how much the revolt is going to cost us
842 *****************************************************************/
diplomat_incite_callback(GtkWidget * w,gpointer data)843 static void diplomat_incite_callback(GtkWidget *w, gpointer data)
844 {
845   struct action_data *args = (struct action_data *)data;
846 
847   if (NULL != game_unit_by_number(args->actor_unit_id)
848       && NULL != game_city_by_number(args->target_city_id)) {
849     request_action_details(ACTION_SPY_INCITE_CITY, args->actor_unit_id,
850                            args->target_city_id);
851   }
852 
853   /* Wait for the server's reply before moving on to the next unit that
854    * needs to know what action to take. */
855   is_more_user_input_needed = TRUE;
856 
857   gtk_widget_destroy(act_sel_dialog);
858   free(args);
859 }
860 
861 /************************************************************************
862   User has responded to incite dialog
863 ************************************************************************/
incite_response(GtkWidget * w,gint response,gpointer data)864 static void incite_response(GtkWidget *w, gint response, gpointer data)
865 {
866   struct action_data *args = (struct action_data *)data;
867 
868   if (response == GTK_RESPONSE_YES) {
869     request_do_action(ACTION_SPY_INCITE_CITY, args->actor_unit_id,
870                       args->target_city_id, 0);
871   }
872 
873   gtk_widget_destroy(w);
874   free(args);
875 
876   /* The user have answered the follow up question. Move on. */
877   diplomat_queue_handle_secondary();
878 }
879 
880 /*************************************************************************
881 Popup the yes/no dialog for inciting, since we know the cost now
882 **************************************************************************/
popup_incite_dialog(struct unit * actor,struct city * pcity,int cost)883 void popup_incite_dialog(struct unit *actor, struct city *pcity, int cost)
884 {
885   GtkWidget *shell;
886   char buf[1024];
887 
888   fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Treasury contains %d gold.",
889                                         "Treasury contains %d gold.",
890                                         client_player()->economic.gold),
891               client_player()->economic.gold);
892 
893   if (INCITE_IMPOSSIBLE_COST == cost) {
894     shell = gtk_message_dialog_new(NULL, 0,
895                                    GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
896                                    _("You can't incite a revolt in %s."),
897                                    city_name_get(pcity));
898     gtk_window_set_title(GTK_WINDOW(shell), _("City can't be incited!"));
899   setup_dialog(shell, toplevel);
900   } else if (cost <= client_player()->economic.gold) {
901     shell = gtk_message_dialog_new(NULL, 0,
902       GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
903       /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */
904       PL_("Incite a revolt for %d gold?\n%s",
905           "Incite a revolt for %d gold?\n%s", cost), cost, buf);
906     gtk_window_set_title(GTK_WINDOW(shell), _("Incite a Revolt!"));
907     setup_dialog(shell, toplevel);
908   } else {
909     shell = gtk_message_dialog_new(NULL,
910       0,
911       GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
912       /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */
913       PL_("Inciting a revolt costs %d gold.\n%s",
914           "Inciting a revolt costs %d gold.\n%s", cost), cost, buf);
915     gtk_window_set_title(GTK_WINDOW(shell), _("Traitors Demand Too Much!"));
916     setup_dialog(shell, toplevel);
917   }
918   gtk_window_present(GTK_WINDOW(shell));
919 
920   g_signal_connect(shell, "response", G_CALLBACK(incite_response),
921                    act_data(actor->id, pcity->id, 0, 0, cost));
922 }
923 
924 /**************************************************************************
925   Callback from the unit target selection dialog.
926 **************************************************************************/
tgt_unit_change_callback(GtkWidget * dlg,gint arg)927 static void tgt_unit_change_callback(GtkWidget *dlg, gint arg)
928 {
929   int act_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dlg), "actor"));
930 
931   if (arg == GTK_RESPONSE_YES) {
932     struct unit *actor = game_unit_by_number(act_id);
933 
934     if (actor != NULL) {
935       int tgt_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dlg),
936                                                      "target"));
937       struct unit *tgt_unit = game_unit_by_number(tgt_id);
938       struct tile *tgt_tile = g_object_get_data(G_OBJECT(dlg), "tile");
939 
940       if (tgt_unit == NULL) {
941         /* Make the action dialog pop up again. */
942         dsend_packet_unit_get_actions(&client.conn,
943                                       actor->id,
944                                       /* Let the server choose the target
945                                        * unit. */
946                                       IDENTITY_NUMBER_ZERO,
947                                       /* Let the server choose the target
948                                        * city. */
949                                       IDENTITY_NUMBER_ZERO,
950                                       tgt_tile->index,
951                                       TRUE);
952       } else {
953         dsend_packet_unit_get_actions(&client.conn,
954                                       actor->id,
955                                       tgt_id,
956                                       /* Let the server choose the target
957                                        * city. */
958                                       IDENTITY_NUMBER_ZERO,
959                                       tgt_tile->index,
960                                       TRUE);
961       }
962     }
963   } else {
964     /* Dialog canceled. This ends the action selection process. */
965     action_selection_no_longer_in_progress(act_id);
966   }
967 
968   gtk_widget_destroy(dlg);
969 }
970 
971 /**************************************************************************
972   Callback from action selection dialog for "Change unit target".
973 **************************************************************************/
act_sel_new_unit_tgt_callback(GtkWidget * w,gpointer data)974 static void act_sel_new_unit_tgt_callback(GtkWidget *w, gpointer data)
975 {
976   struct action_data *args = (struct action_data *)data;
977 
978   struct unit *punit;
979   struct unit *tunit;
980   struct tile *ptile;
981 
982   if ((punit = game_unit_by_number(args->actor_unit_id))
983       && (ptile = index_to_tile(args->target_tile_id))
984       && (tunit = game_unit_by_number(args->target_unit_id))) {
985     select_tgt_unit(punit, ptile, ptile->units, tunit,
986                     _("Target unit selection"),
987                     _("Looking for target unit:"),
988                     _("Units at tile:"),
989                     _("Select"),
990                     G_CALLBACK(tgt_unit_change_callback));
991   }
992 
993   did_not_decide = TRUE;
994   action_selection_restart = TRUE;
995   gtk_widget_destroy(act_sel_dialog);
996   free(args);
997 }
998 
999 /**************************************************************************
1000   Callback from action selection dialog for "Show Location".
1001 **************************************************************************/
act_sel_location_callback(GtkWidget * w,gpointer data)1002 static void act_sel_location_callback(GtkWidget *w, gpointer data)
1003 {
1004   struct action_data *args = (struct action_data *)data;
1005 
1006   struct unit *punit;
1007 
1008   if ((punit = game_unit_by_number(args->actor_unit_id))) {
1009     center_tile_mapcanvas(unit_tile(punit));
1010   }
1011 }
1012 
1013 /**************************************************************************
1014   Callback from action selection dialog for "keep moving".
1015   (This should only occur when entering a tile that has an allied city or
1016   an allied unit.)
1017 **************************************************************************/
act_sel_keep_moving_callback(GtkWidget * w,gpointer data)1018 static void act_sel_keep_moving_callback(GtkWidget *w, gpointer data)
1019 {
1020   struct action_data *args = (struct action_data *)data;
1021 
1022   struct unit *punit;
1023   struct tile *ptile;
1024 
1025   if ((punit = game_unit_by_number(args->actor_unit_id))
1026       && (ptile = index_to_tile(args->target_tile_id))
1027       && !same_pos(unit_tile(punit), ptile)) {
1028     request_unit_non_action_move(punit, ptile);
1029   }
1030 
1031   gtk_widget_destroy(act_sel_dialog);
1032   free(args);
1033 }
1034 
1035 /**************************************************************************
1036   Delay selection of what action to take.
1037 **************************************************************************/
act_sel_wait_callback(GtkWidget * w,gpointer data)1038 static void act_sel_wait_callback(GtkWidget *w, gpointer data)
1039 {
1040   struct action_data *args = (struct action_data *)data;
1041 
1042   key_unit_wait();
1043 
1044   /* the dialog was destroyed when key_unit_wait() resulted in
1045    * action_selection_close() being called. */
1046 
1047   free(args);
1048 }
1049 
1050 /****************************************************************
1051   Action selection dialog has been destroyed
1052 *****************************************************************/
act_sel_destroy_callback(GtkWidget * w,gpointer data)1053 static void act_sel_destroy_callback(GtkWidget *w, gpointer data)
1054 {
1055   act_sel_dialog = NULL;
1056   diplomat_queue_handle_primary();
1057 }
1058 
1059 /****************************************************************
1060   Action selection dialog has been canceled
1061 *****************************************************************/
act_sel_cancel_callback(GtkWidget * w,gpointer data)1062 static void act_sel_cancel_callback(GtkWidget *w, gpointer data)
1063 {
1064   gtk_widget_destroy(act_sel_dialog);
1065   free(data);
1066 }
1067 
1068 /****************************************************************
1069   Action selection dialog has been closed
1070 *****************************************************************/
act_sel_close_callback(GtkWidget * w,gint response_id,gpointer data)1071 static void act_sel_close_callback(GtkWidget *w,
1072                                    gint response_id,
1073                                    gpointer data)
1074 {
1075   gtk_widget_destroy(act_sel_dialog);
1076   free(data);
1077 }
1078 
1079 /* Mapping from an action to the function to call when its button is
1080  * pushed. */
1081 static const GCallback af_map[ACTION_COUNT] = {
1082   /* Unit acting against a city target. */
1083   [ACTION_ESTABLISH_EMBASSY] = (GCallback)diplomat_embassy_callback,
1084   [ACTION_SPY_INVESTIGATE_CITY] = (GCallback)diplomat_investigate_callback,
1085   [ACTION_SPY_POISON] = (GCallback)spy_poison_callback,
1086   [ACTION_SPY_STEAL_GOLD] = (GCallback)spy_steal_gold_callback,
1087   [ACTION_SPY_SABOTAGE_CITY] = (GCallback)diplomat_sabotage_callback,
1088   [ACTION_SPY_TARGETED_SABOTAGE_CITY] =
1089       (GCallback)spy_request_sabotage_list,
1090   [ACTION_SPY_STEAL_TECH] = (GCallback)diplomat_steal_callback,
1091   [ACTION_SPY_TARGETED_STEAL_TECH] = (GCallback)spy_steal_popup,
1092   [ACTION_SPY_INCITE_CITY] = (GCallback)diplomat_incite_callback,
1093   [ACTION_TRADE_ROUTE] = (GCallback)caravan_establish_trade_callback,
1094   [ACTION_MARKETPLACE] = (GCallback)caravan_marketplace_callback,
1095   [ACTION_HELP_WONDER] = (GCallback)caravan_help_build_wonder_callback,
1096 
1097   /* Unit acting against a unit target. */
1098   [ACTION_SPY_BRIBE_UNIT] = (GCallback)diplomat_bribe_callback,
1099   [ACTION_SPY_SABOTAGE_UNIT] = (GCallback)spy_sabotage_unit_callback
1100 };
1101 
1102 /******************************************************************
1103   Show the user the action if it is enabled.
1104 *******************************************************************/
action_entry(GtkWidget * shl,int act_id,const struct act_prob * act_probs,const char * custom,struct action_data * handler_args)1105 static void action_entry(GtkWidget *shl,
1106                          int act_id,
1107                          const struct act_prob *act_probs,
1108                          const char *custom,
1109                          struct action_data *handler_args)
1110 {
1111   const gchar *label;
1112   const gchar *tooltip;
1113 
1114   if (act_id == ACTION_SPY_SABOTAGE_CITY
1115       && action_prob_possible(
1116         act_probs[ACTION_SPY_TARGETED_SABOTAGE_CITY])) {
1117     /* The player can select Sabotage City from the target selection dialog
1118      * of Targeted Sabotage City. */
1119     return;
1120   }
1121 
1122   if (act_id == ACTION_SPY_STEAL_TECH
1123       && action_prob_possible(
1124         act_probs[ACTION_SPY_TARGETED_STEAL_TECH])) {
1125     /* The player can select Steal Tech from the target selection dialog of
1126      * Targeted Steal Tech. */
1127     return;
1128   }
1129 
1130   /* Don't show disabled actions. */
1131   if (!action_prob_possible(act_probs[act_id])) {
1132     return;
1133   }
1134 
1135   label = action_prepare_ui_name(act_id, "_",
1136                                  act_probs[act_id],
1137                                  custom);
1138 
1139   tooltip = action_get_tool_tip(act_id,
1140                                 act_probs[act_id]);
1141 
1142   action_button_map[act_id] = choice_dialog_get_number_of_buttons(shl);
1143   choice_dialog_add(shl, label, af_map[act_id], handler_args,
1144                     FALSE, tooltip);
1145 }
1146 
1147 /******************************************************************
1148   Update an existing button.
1149 *******************************************************************/
action_entry_update(GtkWidget * shl,int act_id,const struct act_prob * act_probs,const char * custom,struct action_data * handler_args)1150 static void action_entry_update(GtkWidget *shl,
1151                                 int act_id,
1152                                 const struct act_prob *act_probs,
1153                                 const char *custom,
1154                                 struct action_data *handler_args)
1155 {
1156   const gchar *label;
1157   const gchar *tooltip;
1158 
1159   /* An action that just became impossible has its button disabled.
1160    * An action that became possible again must be reenabled. */
1161   choice_dialog_button_set_sensitive(act_sel_dialog,
1162       action_button_map[act_id],
1163       action_prob_possible(act_probs[act_id]));
1164 
1165   /* The probability may have changed. */
1166   label = action_prepare_ui_name(act_id, "_",
1167                                  act_probs[act_id], custom);
1168 
1169   tooltip = action_get_tool_tip(act_id,
1170                                 act_probs[act_id]);
1171 
1172   choice_dialog_button_set_label(act_sel_dialog,
1173                                  action_button_map[act_id],
1174                                  label);
1175   choice_dialog_button_set_tooltip(act_sel_dialog,
1176                                    action_button_map[act_id],
1177                                    tooltip);
1178 }
1179 
1180 /**************************************************************************
1181   Popup a dialog that allows the player to select what action a unit
1182   should take.
1183 **************************************************************************/
popup_action_selection(struct unit * actor_unit,struct city * target_city,struct unit * target_unit,struct tile * target_tile,const struct act_prob * act_probs)1184 void popup_action_selection(struct unit *actor_unit,
1185                             struct city *target_city,
1186                             struct unit *target_unit,
1187                             struct tile *target_tile,
1188                             const struct act_prob *act_probs)
1189 {
1190   GtkWidget *shl;
1191   struct astring title = ASTRING_INIT, text = ASTRING_INIT;
1192   struct city *actor_homecity;
1193 
1194   int button_id;
1195 
1196   struct action_data *data =
1197       act_data(actor_unit->id,
1198                (target_city) ? target_city->id : 0,
1199                (target_unit) ? target_unit->id : 0,
1200                (target_tile) ? target_tile->index : 0,
1201                0);
1202 
1203   /* Could be caused by the server failing to reply to a request for more
1204    * information or a bug in the client code. */
1205   fc_assert_msg(!is_more_user_input_needed,
1206                 "Diplomat queue problem. Is another diplomat window open?");
1207 
1208   /* No extra input is required as no action has been chosen yet. */
1209   is_more_user_input_needed = FALSE;
1210 
1211   /* No buttons are added yet. */
1212   for (button_id = 0; button_id < BUTTON_COUNT; button_id++) {
1213     action_button_map[button_id] = BUTTON_NOT_THERE;
1214   }
1215 
1216   actor_homecity = game_city_by_number(actor_unit->homecity);
1217 
1218   actor_unit_id = actor_unit->id;
1219   target_ids[ATK_CITY] = target_city ?
1220                          target_city->id :
1221                          IDENTITY_NUMBER_ZERO;
1222   target_ids[ATK_UNIT] = target_unit ?
1223                          target_unit->id :
1224                          IDENTITY_NUMBER_ZERO;
1225 
1226   astr_set(&title,
1227            /* TRANS: %s is a unit name, e.g., Spy */
1228            _("Choose Your %s's Strategy"),
1229            unit_name_translation(actor_unit));
1230 
1231   if (target_city && actor_homecity) {
1232     astr_set(&text,
1233              _("Your %s from %s reaches the city of %s.\nWhat now?"),
1234              unit_name_translation(actor_unit),
1235              city_name_get(actor_homecity),
1236              city_name_get(target_city));
1237   } else if (target_city) {
1238     astr_set(&text,
1239              _("Your %s has arrived at %s.\nWhat is your command?"),
1240              unit_name_translation(actor_unit),
1241              city_name_get(target_city));
1242   } else if (target_unit) {
1243     astr_set(&text,
1244              /* TRANS: Your Spy is ready to act against Roman Freight. */
1245              _("Your %s is ready to act against %s %s."),
1246              unit_name_translation(actor_unit),
1247              nation_adjective_for_player(unit_owner(target_unit)),
1248              unit_name_translation(target_unit));
1249   } else {
1250     fc_assert_msg(target_unit || target_city,
1251                   "No target unit or target city specified.");
1252     astr_set(&text,
1253              /* TRANS: %s is a unit name, e.g., Diplomat, Spy */
1254              _("Your %s is waiting for your command."),
1255              unit_name_translation(actor_unit));
1256   }
1257 
1258   shl = choice_dialog_start(GTK_WINDOW(toplevel), astr_str(&title),
1259                             astr_str(&text));
1260 
1261   /* Unit acting against a city */
1262 
1263   action_iterate(act) {
1264     if (action_id_get_actor_kind(act) == AAK_UNIT
1265         && action_id_get_target_kind(act) == ATK_CITY) {
1266       action_entry(shl,
1267                    (enum gen_action)act,
1268                    act_probs,
1269                    get_act_sel_action_custom_text(action_by_number(act),
1270                                                   act_probs[act],
1271                                                   actor_unit,
1272                                                   target_city),
1273                    data);
1274     }
1275   } action_iterate_end;
1276 
1277   /* Unit acting against another unit */
1278 
1279   action_iterate(act) {
1280     if (action_id_get_actor_kind(act) == AAK_UNIT
1281         && action_id_get_target_kind(act) == ATK_UNIT) {
1282       action_entry(shl,
1283                    (enum gen_action)act,
1284                    act_probs,
1285                    get_act_sel_action_custom_text(action_by_number(act),
1286                                                   act_probs[act],
1287                                                   actor_unit,
1288                                                   target_city),
1289                    data);
1290     }
1291   } action_iterate_end;
1292 
1293   if (unit_can_move_to_tile(actor_unit, target_tile, FALSE)
1294       || (is_military_unit(actor_unit) || is_attack_unit(actor_unit))
1295       || (can_unit_bombard(actor_unit) && !is_ocean_tile(target_tile))
1296       || (!target_city && unit_has_type_flag(actor_unit, UTYF_CAPTURER))) {
1297     action_button_map[BUTTON_MOVE] =
1298         choice_dialog_get_number_of_buttons(shl);
1299     choice_dialog_add(shl, _("_Keep moving"),
1300                       (GCallback)act_sel_keep_moving_callback,
1301                       data, FALSE, NULL);
1302   }
1303 
1304   if (target_unit != NULL
1305       && unit_list_size(target_tile->units) > 1) {
1306     action_button_map[BUTTON_NEW_UNIT_TGT] =
1307         choice_dialog_get_number_of_buttons(shl);
1308     choice_dialog_add(shl, _("Change unit target"),
1309                       (GCallback)act_sel_new_unit_tgt_callback,
1310                       data, TRUE, NULL);
1311   }
1312 
1313   action_button_map[BUTTON_LOCATION] =
1314       choice_dialog_get_number_of_buttons(shl);
1315   choice_dialog_add(shl, _("Show Location"),
1316                     (GCallback)act_sel_location_callback, data,
1317                     TRUE, NULL);
1318 
1319   action_button_map[BUTTON_WAIT] =
1320       choice_dialog_get_number_of_buttons(shl);
1321   choice_dialog_add(shl, _("_Wait"),
1322                     (GCallback)act_sel_wait_callback, data,
1323                     TRUE, NULL);
1324 
1325   action_button_map[BUTTON_CANCEL] =
1326       choice_dialog_get_number_of_buttons(shl);
1327   choice_dialog_add(shl, _("_Cancel"),
1328                     (GCallback)act_sel_cancel_callback, data,
1329                     FALSE, NULL);
1330 
1331   choice_dialog_end(shl);
1332 
1333   act_sel_dialog = shl;
1334 
1335   choice_dialog_set_hide(shl, TRUE);
1336   g_signal_connect(shl, "destroy",
1337                    G_CALLBACK(act_sel_destroy_callback), NULL);
1338   g_signal_connect(shl, "delete_event",
1339                    G_CALLBACK(act_sel_close_callback), data);
1340 
1341   /* Give follow up questions access to action probabilities. */
1342   client_unit_init_act_prob_cache(actor_unit);
1343   action_iterate(act) {
1344     actor_unit->client.act_prob_cache[act] = act_probs[act];
1345   } action_iterate_end;
1346 
1347   astr_free(&title);
1348   astr_free(&text);
1349 }
1350 
1351 /**************************************************************************
1352   Returns the id of the actor unit currently handled in action selection
1353   dialog when the action selection dialog is open.
1354   Returns IDENTITY_NUMBER_ZERO if no action selection dialog is open.
1355 **************************************************************************/
action_selection_actor_unit(void)1356 int action_selection_actor_unit(void)
1357 {
1358   if (act_sel_dialog == NULL) {
1359     return IDENTITY_NUMBER_ZERO;
1360   }
1361   return actor_unit_id;
1362 }
1363 
1364 /**************************************************************************
1365   Returns id of the target city of the actions currently handled in action
1366   selection dialog when the action selection dialog is open and it has a
1367   city target. Returns IDENTITY_NUMBER_ZERO if no action selection dialog
1368   is open or no city target is present in the action selection dialog.
1369 **************************************************************************/
action_selection_target_city(void)1370 int action_selection_target_city(void)
1371 {
1372   if (act_sel_dialog == NULL) {
1373     return IDENTITY_NUMBER_ZERO;
1374   }
1375   return target_ids[ATK_CITY];
1376 }
1377 
1378 /**************************************************************************
1379   Returns id of the target unit of the actions currently handled in action
1380   selection dialog when the action selection dialog is open and it has a
1381   unit target. Returns IDENTITY_NUMBER_ZERO if no action selection dialog
1382   is open or no unit target is present in the action selection dialog.
1383 **************************************************************************/
action_selection_target_unit(void)1384 int action_selection_target_unit(void)
1385 {
1386   if (act_sel_dialog == NULL) {
1387     return IDENTITY_NUMBER_ZERO;
1388   }
1389 
1390   return target_ids[ATK_UNIT];
1391 }
1392 
1393 /**************************************************************************
1394   Updates the action selection dialog with new information.
1395 **************************************************************************/
action_selection_refresh(struct unit * actor_unit,struct city * target_city,struct unit * target_unit,struct tile * target_tile,const struct act_prob * act_probs)1396 void action_selection_refresh(struct unit *actor_unit,
1397                               struct city *target_city,
1398                               struct unit *target_unit,
1399                               struct tile *target_tile,
1400                               const struct act_prob *act_probs)
1401 {
1402   struct action_data *data;
1403 
1404   if (act_sel_dialog == NULL) {
1405     fc_assert_msg(act_sel_dialog != NULL,
1406                   "The action selection dialog should have been open");
1407     return;
1408   }
1409 
1410   if (actor_unit->id != action_selection_actor_unit()) {
1411     fc_assert_msg(actor_unit->id == action_selection_actor_unit(),
1412                   "The action selection dialog is for another actor unit.");
1413     return;
1414   }
1415 
1416   data = act_data(actor_unit->id,
1417                   (target_city) ? target_city->id : IDENTITY_NUMBER_ZERO,
1418                   (target_unit) ? target_unit->id : IDENTITY_NUMBER_ZERO,
1419                   (target_tile) ? target_tile->index : 0,
1420                   0);
1421 
1422   action_iterate(act) {
1423     const char *custom;
1424 
1425     if (action_id_get_actor_kind(act) != AAK_UNIT) {
1426       /* Not relevant. */
1427       continue;
1428     }
1429 
1430     custom = get_act_sel_action_custom_text(action_by_number(act),
1431                                             act_probs[act],
1432                                             actor_unit,
1433                                             target_city);
1434 
1435     if (BUTTON_NOT_THERE == action_button_map[act]) {
1436       /* Add the button (unless its probability is 0). */
1437       action_entry(act_sel_dialog, act, act_probs, custom, data);
1438     } else {
1439       /* Update the existing button. */
1440       action_entry_update(act_sel_dialog, act, act_probs, custom, data);
1441     }
1442   } action_iterate_end;
1443 
1444   /* DO NOT change the action_button_map[] for any button to reflect its
1445    * new position. A button keeps its choice dialog internal name when its
1446    * position changes. A button's id number is therefore based on when
1447    * it was added, not on its current position. */
1448 
1449   if (BUTTON_NOT_THERE != action_button_map[BUTTON_WAIT]) {
1450     /* Move the wait button below the recently added button. */
1451     choice_dialog_button_move_to_the_end(act_sel_dialog,
1452         action_button_map[BUTTON_WAIT]);
1453   }
1454 
1455   if (BUTTON_NOT_THERE != action_button_map[BUTTON_CANCEL]) {
1456     /* Move the cancel button below the recently added button. */
1457     choice_dialog_button_move_to_the_end(act_sel_dialog,
1458         action_button_map[BUTTON_CANCEL]);
1459   }
1460 
1461   choice_dialog_end(act_sel_dialog);
1462 }
1463 
1464 /****************************************************************
1465   Closes the action selection dialog
1466 ****************************************************************/
action_selection_close(void)1467 void action_selection_close(void)
1468 {
1469   if (act_sel_dialog != NULL) {
1470     did_not_decide = TRUE;
1471     gtk_widget_destroy(act_sel_dialog);
1472   }
1473 }
1474