Feature #1686 » 0018-Drop-gtk4x-sources.patch
| bootstrap/org.freeciv.gtk4x.desktop | ||
|---|---|---|
| [Desktop Entry] | ||
| Name=Freeciv | ||
| Name[ca]=Freeciv | ||
| Name[es]=Freeciv | ||
| Name[fr]=Freeciv | ||
| Name[nb]=Freeciv | ||
| Name[pt]=Freeciv | ||
| Name[ru]=Freeciv | ||
| Comment=Turn-based strategy game inspired by the history of human civilization | ||
| Comment[ca]=Joc d'estratègia inspirat en la història de la civilització humana | ||
| Comment[da]=Strategispil inspireret af den menneskelige civilisations historie | ||
| Comment[de]=Rundenbasiertes Strategiespiel, inspiriert durch die Geschichte der menschlichen Zivilisation | ||
| Comment[fi]=Ihmiskunnan historian inspiroima vuoropohjainen strategiapeli | ||
| Comment[nb]=Strategispill inspirert av historien til menneskelig sivilisasjon | ||
| Comment[pt]=Jogo de estratégia por turnos inspirado na História da civilização humana | ||
| Comment[ru]=Пошаговая стратегическая игра, вдохновлённая историей человеческой цивилизации | ||
| Comment[sv]=Turordningsbaserat strategispel inspirerat av den mänskliga historien | ||
| Exec=freeciv-gtk4x | ||
| Icon=freeciv-client | ||
| StartupNotify=true | ||
| Terminal=false | ||
| Type=Application | ||
| Categories=GTK;Game;StrategyGame; | ||
| Keywords=strategy;simulation;civilization;tiles;history;mankind;multiplayer; | ||
| bootstrap/org.freeciv.gtk4x.metainfo.xml.in | ||
|---|---|---|
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <component type="desktop-application"> | ||
|     <name>Freeciv gtk4x client</name> | ||
|     <id>org.freeciv.gtk4x</id> | ||
|     <project_license>GPL-2.0-or-later</project_license> | ||
|     <metadata_license>CC0</metadata_license> | ||
|     <developer id="org.freeciv"> | ||
|       <name>Freeciv Team</name> | ||
|     </developer> | ||
|     <summary>Gtk4 based client for the Freeciv game</summary> | ||
|     <description> | ||
|         <p> | ||
|             Freeciv is a Free and Open Source empire-building strategy game inspired by the history of human civilization. The game commences in prehistory and your | ||
|             mission is to lead your tribe from the Stone Age to the Space Age... | ||
|         </p> | ||
|         <p> | ||
|             This client for connecting to network games, or to launch local single-player games, is based on gtk4 widget set. | ||
|         </p> | ||
|     </description> | ||
|     <launchable type="desktop-id">org.freeciv.gtk4x.desktop</launchable> | ||
|     <url type="homepage">https://www.freeciv.org/</url> | ||
|     <url type="bugtracker">https://osdn.net/projects/freeciv/ticket/</url> | ||
|     <url type="faq">https://www.freeciv.org/wiki/FAQ</url> | ||
|     <url type="donation">https://www.freeciv.org/donate.html</url> | ||
|     <url type="translate">https://www.freeciv.org/wiki/Translations</url> | ||
|     <url type="contact">https://www.freeciv.org/maillists.html</url> | ||
|     <!-- flatpak does not understand these | ||
|     <url type="vcs-browser">https://github.com/freeciv/freeciv/</url> | ||
|     <url type="contribute">https://www.freeciv.org/wiki/How_to_Contribute</url> | ||
|     --> | ||
|     <releases> | ||
|       [release] | ||
|     </releases> | ||
|     <content_rating type="oars-1.0"> | ||
|       <content_attribute id="social-chat">intense</content_attribute> | ||
|     </content_rating> | ||
|     <screenshots> | ||
|       <screenshot type="default"> | ||
|         <image>https://files.freeciv.org/screenshots/3.1/client.gtk4-3.1.0-beta2.png</image> | ||
|       </screenshot> | ||
|     </screenshots> | ||
|     <update_contact>freeciv-dev@freelists.org</update_contact> | ||
| </component> | ||
| bootstrap/org.freeciv.gtk4x.mp.desktop | ||
|---|---|---|
| [Desktop Entry] | ||
| Name=Freeciv modpack installer (gtk4x) | ||
| Name[ru]=Установщик модпаков Freeciv (gtk4x) | ||
| Comment=Download and install add-ons for Freeciv | ||
| Comment[ru]=Скачивайте и устанавливайте дополнения для Freeciv | ||
| Exec=freeciv-mp-gtk4x | ||
| Icon=freeciv-modpack | ||
| StartupNotify=true | ||
| Terminal=false | ||
| Type=Application | ||
| Categories=GTK;Game;StrategyGame; | ||
| Keywords=strategy;simulation;civilization;tiles;history;mankind;multiplayer;download;installer; | ||
| bootstrap/org.freeciv.gtk4x.mp.metainfo.xml.in | ||
|---|---|---|
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <component type="desktop-application"> | ||
|     <name>Freeciv gtk-4+ modpack installer</name> | ||
|     <id>org.freeciv.gtk4x.mp</id> | ||
|     <project_license>GPL-2.0-or-later</project_license> | ||
|     <metadata_license>CC0</metadata_license> | ||
|     <developer id="org.freeciv"> | ||
|       <name>Freeciv Team</name> | ||
|     </developer> | ||
|     <summary>Gtk-4 based modpack installer for the Freeciv game</summary> | ||
|     <description> | ||
|         <p> | ||
|             Freeciv is a Free and Open Source empire-building strategy game inspired by the history of human civilization. The game commences in prehistory and your | ||
|             mission is to lead your tribe from the Stone Age to the Space Age... | ||
|         </p> | ||
|         <p> | ||
|             Freeciv modpack utility can be used to automatically download and install custom rulesets, tilesets, soundsets, and maps for freeciv to use. | ||
|         </p> | ||
|     </description> | ||
|     <launchable type="desktop-id">org.freeciv.gtk4x.mp.desktop</launchable> | ||
|     <url type="homepage">https://www.freeciv.org/</url> | ||
|     <url type="bugtracker">https://osdn.net/projects/freeciv/ticket/</url> | ||
|     <url type="faq">https://www.freeciv.org/wiki/FAQ</url> | ||
|     <url type="donation">https://www.freeciv.org/donate.html</url> | ||
|     <url type="translate">https://www.freeciv.org/wiki/Translations</url> | ||
|     <url type="contact">https://www.freeciv.org/maillists.html</url> | ||
|     <!-- flatpak does not understand these | ||
|     <url type="vcs-browser">https://github.com/freeciv/freeciv/</url> | ||
|     <url type="contribute">https://www.freeciv.org/wiki/How_to_Contribute</url> | ||
|     --> | ||
|     <releases> | ||
|       [release] | ||
|     </releases> | ||
|     <content_rating type="oars-1.0" /> | ||
|     <screenshots> | ||
|       <screenshot type="default"> | ||
|         <image>https://files.freeciv.org/screenshots/3.0/modinst.gtk4-3.0.6.png</image> | ||
|       </screenshot> | ||
|     </screenshots> | ||
|     <update_contact>freeciv-dev@freelists.org</update_contact> | ||
| </component> | ||
| client/gui-gtk-5.0/.gitignore | ||
|---|---|---|
| /Makefile.in | ||
| client/gui-gtk-5.0/Makefile.am | ||
|---|---|---|
| ## Process this file with automake to produce Makefile.in | ||
| noinst_LTLIBRARIES = libgui-gtk5.la | ||
| AM_CPPFLAGS = \ | ||
| 	-I$(srcdir)/.. \ | ||
| 	-I$(srcdir)/../include \ | ||
| 	-I$(top_srcdir)/utility \ | ||
| 	-I$(top_srcdir)/common \ | ||
| 	-I$(top_srcdir)/common/aicore \ | ||
| 	-I$(top_srcdir)/common/networking \ | ||
| 	-I$(top_srcdir)/common/scriptcore \ | ||
| 	-I$(srcdir)/../agents \ | ||
| 	-I$(srcdir)/../luascript \ | ||
| 	-I$(top_srcdir)/dependencies/tinycthread \ | ||
| 	-I$(top_srcdir)/gen_headers/enums \ | ||
| 	$(gui_gtk5_cflags) $(SOUND_CFLAGS) | ||
| libgui_gtk5_la_SOURCES = \ | ||
| 	action_dialog.c \ | ||
| 	canvas.c	\ | ||
| 	canvas.h	\ | ||
| 	chatline.h	\ | ||
| 	chatline.c	\ | ||
| 	choice_dialog.c	\ | ||
| 	choice_dialog.h \ | ||
| 	citizensinfo.c	\ | ||
| 	citizensinfo.h	\ | ||
| 	citydlg.c	\ | ||
| 	citydlg.h	\ | ||
| 	cityrep.c	\ | ||
| 	cityrep.h	\ | ||
| 	cma_fe.c	\ | ||
| 	cma_fe.h	\ | ||
| 	colors.c	\ | ||
| 	colors.h	\ | ||
| 	connectdlg.c	\ | ||
| 	connectdlg.h	\ | ||
| 	dialogs.c	\ | ||
| 	dialogs.h	\ | ||
| 	diplodlg.c	\ | ||
| 	diplodlg.h	\ | ||
| 	editgui.c	\ | ||
| 	editgui.h	\ | ||
| 	editprop.c	\ | ||
| 	editprop.h	\ | ||
| 	finddlg.c	\ | ||
| 	finddlg.h	\ | ||
| 	gamedlgs.c	\ | ||
| 	gamedlgs.h	\ | ||
| 	gotodlg.c	\ | ||
| 	gotodlg.h	\ | ||
| 	graphics.c	\ | ||
| 	graphics.h	\ | ||
| 	gui_main.c	\ | ||
| 	gui_main.h	\ | ||
| 	gui_stuff.c	\ | ||
| 	gui_stuff.h	\ | ||
| 	happiness.c	\ | ||
| 	happiness.h	\ | ||
| 	helpdlg.c	\ | ||
| 	helpdlg.h	\ | ||
| 	infradlg.c	\ | ||
| 	infradlg.h	\ | ||
| 	inputdlg.c	\ | ||
| 	inputdlg.h	\ | ||
| 	inteldlg.c	\ | ||
| 	inteldlg.h	\ | ||
| 	luaconsole.c	\ | ||
| 	luaconsole.h	\ | ||
| 	mapctrl.c	\ | ||
| 	mapctrl.h	\ | ||
| 	mapview.c	\ | ||
| 	mapview.h	\ | ||
| 	menu.c		\ | ||
| 	menu.h		\ | ||
| 	messagedlg.c	\ | ||
| 	messagedlg.h	\ | ||
| 	messagewin.c	\ | ||
| 	messagewin.h	\ | ||
| 	optiondlg.c	\ | ||
| 	optiondlg.h	\ | ||
| 	pages.c		\ | ||
| 	pages.h		\ | ||
| 	plrdlg.c	\ | ||
| 	plrdlg.h	\ | ||
| 	rallypointdlg.c	\ | ||
| 	rallypointdlg.h	\ | ||
| 	ratesdlg.h	\ | ||
| 	repodlgs.c	\ | ||
| 	repodlgs.h	\ | ||
| 	soundset_dlg.c	\ | ||
| 	spaceshipdlg.c	\ | ||
| 	spaceshipdlg.h  \ | ||
| 	sprite.c	\ | ||
| 	sprite.h	\ | ||
| 	theme_dlg.c	\ | ||
| 	themes.c	\ | ||
| 	tileset_dlg.c	\ | ||
| 	transportdlg.c	\ | ||
| 	transportdlg.h	\ | ||
| 	unitselextradlg.c	\ | ||
| 	unitselextradlg.h	\ | ||
| 	unitselunitdlg.c	\ | ||
| 	unitselunitdlg.h	\ | ||
| 	unitselect.h	\ | ||
| 	unitselect.c	\ | ||
| 	voteinfo_bar.c	\ | ||
| 	voteinfo_bar.h	\ | ||
| 	wldlg.c		\ | ||
| 	wldlg.h | ||
| libgui_gtk5_la_LIBADD = -lm | ||
| client/gui-gtk-5.0/action_dialog.c | ||
|---|---|---|
| /*********************************************************************** | ||
|  Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team | ||
|    This program is free software; you can redistribute it and/or modify | ||
|    it under the terms of the GNU General Public License as published by | ||
|    the Free Software Foundation; either version 2, or (at your option) | ||
|    any later version. | ||
|    This program is distributed in the hope that it will be useful, | ||
|    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||
|    GNU General Public License for more details. | ||
| ***********************************************************************/ | ||
| #ifdef HAVE_CONFIG_H | ||
| #include <fc_config.h> | ||
| #endif | ||
| #include <gtk/gtk.h> | ||
| /* utility */ | ||
| #include "astring.h" | ||
| #include "support.h" | ||
| /* common */ | ||
| #include "actions.h" | ||
| #include "game.h" | ||
| #include "traderoutes.h" | ||
| #include "movement.h" | ||
| #include "research.h" | ||
| #include "unit.h" | ||
| #include "unitlist.h" | ||
| /* client */ | ||
| #include "dialogs_g.h" | ||
| #include "chatline.h" | ||
| #include "choice_dialog.h" | ||
| #include "client_main.h" | ||
| #include "climisc.h" | ||
| #include "connectdlg_common.h" | ||
| #include "control.h" | ||
| #include "gui_main.h" | ||
| #include "gui_stuff.h" | ||
| #include "mapview.h" | ||
| #include "packhand.h" | ||
| #include "text.h" | ||
| /* client/gui-gtk-5.0 */ | ||
| #include "citydlg.h" | ||
| #include "dialogs.h" | ||
| #include "unitselextradlg.h" | ||
| #include "unitselunitdlg.h" | ||
| #include "wldlg.h" | ||
| /* Locations for non action enabler controlled buttons. */ | ||
| #define BUTTON_NEW_UNIT_TGT (ACTION_COUNT + 1) | ||
| #define BUTTON_NEW_EXTRA_TGT (BUTTON_NEW_UNIT_TGT + 1) | ||
| #define BUTTON_LOCATION (BUTTON_NEW_EXTRA_TGT + 1) | ||
| #define BUTTON_WAIT (BUTTON_LOCATION + 1) | ||
| #define BUTTON_CANCEL (BUTTON_WAIT + 1) | ||
| #define BUTTON_COUNT (BUTTON_CANCEL + 1) | ||
| #define BUTTON_NOT_THERE -1 | ||
| static GtkWidget *act_sel_dialog; | ||
| static int action_button_map[BUTTON_COUNT]; | ||
| static int actor_unit_id; | ||
| static int target_ids[ATK_COUNT]; | ||
| static int target_extra_id; | ||
| static bool is_more_user_input_needed = FALSE; | ||
| static bool did_not_decide = FALSE; | ||
| static bool action_selection_restart = FALSE; | ||
| static GtkWidget  *spy_tech_shell; | ||
| static GtkWidget  *spy_sabotage_shell; | ||
| /* A structure to hold parameters for actions inside the GUI instead of | ||
|  * storing the needed data in a global variable. */ | ||
| struct action_data { | ||
|   action_id act_id; | ||
|   int actor_unit_id; | ||
|   int target_city_id; | ||
|   int target_unit_id; | ||
|   int target_tile_id; | ||
|   int target_building_id; | ||
|   int target_tech_id; | ||
|   int target_extra_id; | ||
| }; | ||
| /* TODO: Maybe this should be in the dialog itself? */ | ||
| static struct action_data *act_sel_dialog_data; | ||
| #define FC_TYPE_ACTION_ROW (fc_action_row_get_type()) | ||
| G_DECLARE_FINAL_TYPE(FcActionRow, fc_action_row, FC, ACTION_ROW, GObject) | ||
| struct _FcActionRow | ||
| { | ||
|   GObject parent_instance; | ||
|   char *name; | ||
|   int id; | ||
| }; | ||
| struct _FcActionRowClass | ||
| { | ||
|   GObjectClass parent_class; | ||
| }; | ||
| G_DEFINE_TYPE(FcActionRow, fc_action_row, G_TYPE_OBJECT) | ||
| /**********************************************************************//** | ||
|   Finalizing method for FcActionRow | ||
| **************************************************************************/ | ||
| static void fc_action_row_finalize(GObject *gobject) | ||
| { | ||
|   FcActionRow *row = FC_ACTION_ROW(gobject); | ||
|   free(row->name); | ||
|   row->name = nullptr; | ||
|   G_OBJECT_CLASS(fc_action_row_parent_class)->finalize(gobject); | ||
| } | ||
| /**********************************************************************//** | ||
|   Initialization method for FcActionRow class | ||
| **************************************************************************/ | ||
| static void | ||
| fc_action_row_class_init(FcActionRowClass *klass) | ||
| { | ||
|   GObjectClass *object_class = G_OBJECT_CLASS(klass); | ||
|   object_class->finalize = fc_action_row_finalize; | ||
| } | ||
| /**********************************************************************//** | ||
|   Initialization method for FcActionRow | ||
| **************************************************************************/ | ||
| static void | ||
| fc_action_row_init(FcActionRow *self) | ||
| { | ||
|   self->name = nullptr; | ||
| } | ||
| /**********************************************************************//** | ||
|   FcActionRow creation method | ||
| **************************************************************************/ | ||
| static FcActionRow *fc_action_row_new(void) | ||
| { | ||
|   FcActionRow *result; | ||
|   result = g_object_new(FC_TYPE_ACTION_ROW, nullptr); | ||
|   return result; | ||
| } | ||
| /**********************************************************************//** | ||
|   Create a new action data structure that can be stored in the | ||
|   dialogs. | ||
| **************************************************************************/ | ||
| static struct action_data *act_data(action_id act_id, | ||
|                                     int actor_id, | ||
|                                     int target_city_id, | ||
|                                     int target_unit_id, | ||
|                                     int target_tile_id, | ||
|                                     int target_building_id, | ||
|                                     int target_tech_id, | ||
|                                     int tgt_extra_id) | ||
| { | ||
|   struct action_data *data = fc_malloc(sizeof(*data)); | ||
|   data->act_id = act_id; | ||
|   data->actor_unit_id = actor_id; | ||
|   data->target_city_id = target_city_id; | ||
|   data->target_unit_id = target_unit_id; | ||
|   data->target_tile_id = target_tile_id; | ||
|   data->target_building_id = target_building_id; | ||
|   data->target_tech_id = target_tech_id; | ||
|   data->target_extra_id = tgt_extra_id; | ||
|   return data; | ||
| } | ||
| /**********************************************************************//** | ||
|   Move the queue of units that need user input forward unless the current | ||
|   unit is going to need more input. | ||
| **************************************************************************/ | ||
| static void diplomat_queue_handle_primary(void) | ||
| { | ||
|   if (!is_more_user_input_needed) { | ||
|     /* The client isn't waiting for information for any unanswered follow | ||
|      * up questions. */ | ||
|     struct unit *actor_unit; | ||
|     if ((actor_unit = game_unit_by_number(actor_unit_id))) { | ||
|       /* The action selection dialog wasn't closed because the actor unit | ||
|        * was lost. */ | ||
|       /* The probabilities didn't just disappear, right? */ | ||
|       fc_assert_action(actor_unit->client.act_prob_cache, | ||
|                        client_unit_init_act_prob_cache(actor_unit)); | ||
|       FC_FREE(actor_unit->client.act_prob_cache); | ||
|     } | ||
|     if (action_selection_restart) { | ||
|       /* The action selection dialog was closed but only so it can be | ||
|        * redrawn with fresh data. */ | ||
|       action_selection_restart = FALSE; | ||
|     } else { | ||
|       /* The action selection process is over, at least for now. */ | ||
|       action_selection_no_longer_in_progress(actor_unit_id); | ||
|     } | ||
|     if (did_not_decide) { | ||
|       /* The action selection dialog was closed but the player didn't | ||
|        * decide what the unit should do. */ | ||
|       /* Reset so the next action selection dialog does the right thing. */ | ||
|       did_not_decide = FALSE; | ||
|     } else { | ||
|       /* An action, or no action at all, was selected. */ | ||
|       action_decision_clear_want(actor_unit_id); | ||
|       action_selection_next_in_focus(actor_unit_id); | ||
|     } | ||
|   } | ||
| } | ||
| /**********************************************************************//** | ||
|   Move the queue of units that need user input forward since the | ||
|   current unit doesn't require the extra input any more. | ||
| **************************************************************************/ | ||
| static void diplomat_queue_handle_secondary(void) | ||
| { | ||
|   /* Stop waiting. Move on to the next queued unit. */ | ||
|   is_more_user_input_needed = FALSE; | ||
|   diplomat_queue_handle_primary(); | ||
| } | ||
| /**********************************************************************//** | ||
|   Let the non shared client code know that the action selection process | ||
|   no longer is in progress for the specified unit. | ||
|   This allows the client to clean up any client specific assumptions. | ||
| **************************************************************************/ | ||
| void action_selection_no_longer_in_progress_gui_specific(int actor_id) | ||
| { | ||
|   /* Stop assuming the answer to a follow up question will arrive. */ | ||
|   is_more_user_input_needed = FALSE; | ||
| } | ||
| /**********************************************************************//** | ||
|   Get the non targeted version of an action so it, if enabled, can appear | ||
|   in the target selection dialog. | ||
| **************************************************************************/ | ||
| static action_id get_non_targeted_action_id(action_id tgt_action_id) | ||
| { | ||
|   /* Don't add an action mapping here unless the non targeted version is | ||
|    * selectable in the targeted version's target selection dialog. */ | ||
|   switch ((enum gen_action)tgt_action_id) { | ||
|   case ACTION_SPY_TARGETED_SABOTAGE_CITY: | ||
|     return ACTION_SPY_SABOTAGE_CITY; | ||
|   case ACTION_SPY_TARGETED_SABOTAGE_CITY_ESC: | ||
|     return ACTION_SPY_SABOTAGE_CITY_ESC; | ||
|   case ACTION_SPY_TARGETED_STEAL_TECH: | ||
|     return ACTION_SPY_STEAL_TECH; | ||
|   case ACTION_SPY_TARGETED_STEAL_TECH_ESC: | ||
|     return ACTION_SPY_STEAL_TECH_ESC; | ||
|   default: | ||
|     /* No non targeted version found. */ | ||
|     return ACTION_NONE; | ||
|   } | ||
| } | ||
| /**********************************************************************//** | ||
|   Get the production targeted version of an action so it, if enabled, can | ||
|   appear in the target selection dialog. | ||
| **************************************************************************/ | ||
| static action_id get_production_targeted_action_id(action_id tgt_action_id) | ||
| { | ||
|   /* Don't add an action mapping here unless the non targeted version is | ||
|    * selectable in the targeted version's target selection dialog. */ | ||
|   switch ((enum gen_action)tgt_action_id) { | ||
|   case ACTION_SPY_TARGETED_SABOTAGE_CITY: | ||
|     return ACTION_SPY_SABOTAGE_CITY_PRODUCTION; | ||
|   case ACTION_SPY_TARGETED_SABOTAGE_CITY_ESC: | ||
|     return ACTION_SPY_SABOTAGE_CITY_PRODUCTION_ESC; | ||
|   case ACTION_STRIKE_BUILDING: | ||
|     return ACTION_STRIKE_PRODUCTION; | ||
|   default: | ||
|     /* No non targeted version found. */ | ||
|     return ACTION_NONE; | ||
|   } | ||
| } | ||
| /**********************************************************************//** | ||
|   User selected an action from the choice dialog and the action has no | ||
|   special needs. | ||
| **************************************************************************/ | ||
| static void simple_action_callback(GtkWidget *w, gpointer data) | ||
| { | ||
|   int actor_id, target_id, sub_target; | ||
|   struct action *paction; | ||
|   struct action_data *args = act_sel_dialog_data; | ||
|   bool failed = FALSE; | ||
|   /* Data */ | ||
|   args->act_id = GPOINTER_TO_INT(data); | ||
|   paction = action_by_number(args->act_id); | ||
|   /* Actor */ | ||
|   fc_assert(action_get_actor_kind(paction) == AAK_UNIT); | ||
|   actor_id = args->actor_unit_id; | ||
|   if (NULL == game_unit_by_number(actor_id)) { | ||
|     /* Probably dead. */ | ||
|     failed = TRUE; | ||
|   } | ||
|   /* Target */ | ||
|   target_id = IDENTITY_NUMBER_ZERO; | ||
|   switch (action_get_target_kind(paction)) { | ||
|   case ATK_CITY: | ||
|     target_id = args->target_city_id; | ||
|     if (NULL == game_city_by_number(target_id)) { | ||
|       /* Probably destroyed. */ | ||
|       failed = TRUE; | ||
|     } | ||
|     break; | ||
|   case ATK_UNIT: | ||
|     target_id = args->target_unit_id; | ||
|     if (NULL == game_unit_by_number(target_id)) { | ||
|       /* Probably dead. */ | ||
|       failed = TRUE; | ||
|     } | ||
|     break; | ||
|   case ATK_STACK: | ||
|   case ATK_TILE: | ||
|   case ATK_EXTRAS: | ||
|     target_id = args->target_tile_id; | ||
|     if (NULL == index_to_tile(&(wld.map), target_id)) { | ||
|       /* TODO: Should this be possible at all? If not: add assertion. */ | ||
|       failed = TRUE; | ||
|     } | ||
|     break; | ||
|   case ATK_SELF: | ||
|     target_id = IDENTITY_NUMBER_ZERO; | ||
|     break; | ||
|   case ATK_COUNT: | ||
|     fc_assert(action_get_target_kind(paction) != ATK_COUNT); | ||
|     failed = TRUE; | ||
|   } | ||
|   /* Sub target. */ | ||
|   sub_target = NO_TARGET; | ||
|   if (paction->target_complexity != ACT_TGT_COMPL_SIMPLE) { | ||
|     switch (action_get_sub_target_kind(paction)) { | ||
|     case ASTK_BUILDING: | ||
|       sub_target = args->target_building_id; | ||
|       if (NULL == improvement_by_number(sub_target)) { | ||
|         /* Did the ruleset change? */ | ||
|         failed = TRUE; | ||
|       } | ||
|       break; | ||
|     case ASTK_TECH: | ||
|       sub_target = args->target_tech_id; | ||
|       if (NULL == valid_advance_by_number(sub_target)) { | ||
|         /* Did the ruleset change? */ | ||
|         failed = TRUE; | ||
|       } | ||
|       break; | ||
|     case ASTK_EXTRA: | ||
|     case ASTK_EXTRA_NOT_THERE: | ||
|       /* TODO: Validate if the extra is there? */ | ||
|       sub_target = args->target_extra_id; | ||
|       if (NULL == extra_by_number(sub_target)) { | ||
|         /* Did the ruleset change? */ | ||
|         failed = TRUE; | ||
|       } | ||
|       break; | ||
|     case ASTK_NONE: | ||
|     case ASTK_COUNT: | ||
|       /* Shouldn't happen. */ | ||
|       fc_assert(action_get_sub_target_kind(paction) != ASTK_NONE); | ||
|       failed = TRUE; | ||
|       break; | ||
|     } | ||
|   } | ||
|   /* Send request. */ | ||
|   if (!failed) { | ||
|     request_do_action(paction->id, actor_id, target_id, sub_target, ""); | ||
|   } | ||
|   /* Clean up. */ | ||
|   choice_dialog_destroy(act_sel_dialog); | ||
|   /* No follow up questions. */ | ||
|   act_sel_dialog_data = NULL; | ||
|   FC_FREE(args); | ||
| } | ||
| /**********************************************************************//** | ||
|   User selected an action from the choice dialog that needs details from | ||
|   the server. | ||
| **************************************************************************/ | ||
| static void request_action_details_callback(GtkWidget *w, gpointer data) | ||
| { | ||
|   int actor_id, target_id; | ||
|   struct action *paction; | ||
|   struct action_data *args = act_sel_dialog_data; | ||
|   bool failed = FALSE; | ||
|   /* Data */ | ||
|   args->act_id = GPOINTER_TO_INT(data); | ||
|   paction = action_by_number(args->act_id); | ||
|   /* Actor */ | ||
|   fc_assert(action_get_actor_kind(paction) == AAK_UNIT); | ||
|   actor_id = args->actor_unit_id; | ||
|   if (NULL == game_unit_by_number(actor_id)) { | ||
|     /* Probably dead. */ | ||
|     failed = TRUE; | ||
|   } | ||
|   /* Target */ | ||
|   target_id = IDENTITY_NUMBER_ZERO; | ||
|   switch (action_get_target_kind(paction)) { | ||
|   case ATK_CITY: | ||
|     target_id = args->target_city_id; | ||
|     if (NULL == game_city_by_number(target_id)) { | ||
|       /* Probably destroyed. */ | ||
|       failed = TRUE; | ||
|     } | ||
|     break; | ||
|   case ATK_UNIT: | ||
|     target_id = args->target_unit_id; | ||
|     if (NULL == game_unit_by_number(target_id)) { | ||
|       /* Probably dead. */ | ||
|       failed = TRUE; | ||
|     } | ||
|     break; | ||
|   case ATK_STACK: | ||
|   case ATK_TILE: | ||
|   case ATK_EXTRAS: | ||
|     target_id = args->target_tile_id; | ||
|     if (NULL == index_to_tile(&(wld.map), target_id)) { | ||
|       /* TODO: Should this be possible at all? If not: add assertion. */ | ||
|       failed = TRUE; | ||
|     } | ||
|     break; | ||
|   case ATK_SELF: | ||
|     target_id = IDENTITY_NUMBER_ZERO; | ||
|     break; | ||
|   case ATK_COUNT: | ||
|     fc_assert(action_get_target_kind(paction) != ATK_COUNT); | ||
|     failed = TRUE; | ||
|   } | ||
|   /* Send request. */ | ||
|   if (!failed) { | ||
|     request_action_details(paction->id, actor_id, target_id); | ||
|   } | ||
|   /* Wait for the server's reply before moving on to the next unit that | ||
|    * needs to know what action to take. */ | ||
|   is_more_user_input_needed = TRUE; | ||
|   /* Clean up. */ | ||
|   choice_dialog_destroy(act_sel_dialog); | ||
|   /* No client side follow up questions. */ | ||
|   act_sel_dialog_data = NULL; | ||
|   FC_FREE(args); | ||
| } | ||
| /**********************************************************************//** | ||
|   User selected build city from the choice dialog | ||
| **************************************************************************/ | ||
| static void found_city_callback(GtkWidget *w, gpointer data) | ||
| { | ||
|   struct action_data *args = act_sel_dialog_data; | ||
|   dsend_packet_city_name_suggestion_req(&client.conn, | ||
|                                         args->actor_unit_id); | ||
|   choice_dialog_destroy(act_sel_dialog); | ||
|   free(args); | ||
| } | ||
| /**********************************************************************//** | ||
|   User selected "Upgrade Unit" from choice dialog. | ||
| **************************************************************************/ | ||
| static void upgrade_callback(GtkWidget *w, gpointer data) | ||
| { | ||
|   struct unit *punit; | ||
|   struct action_data *args = act_sel_dialog_data; | ||
|   if ((punit = game_unit_by_number(args->actor_unit_id)) | ||
|       && NULL != game_city_by_number(args->target_city_id)) { | ||
|     struct unit_list *as_list; | ||
|     as_list = unit_list_new(); | ||
|     unit_list_append(as_list, punit); | ||
|     popup_upgrade_dialog(as_list); | ||
|     unit_list_destroy(as_list); | ||
|   } | ||
|   choice_dialog_destroy(act_sel_dialog); | ||
|   free(args); | ||
| } | ||
| /**********************************************************************//** | ||
|   User responded to bribe unit dialog | ||
| **************************************************************************/ | ||
| static void bribe_unit_response(GtkWidget *w, gint response, gpointer data) | ||
| { | ||
|   struct action_data *args = (struct action_data *)data; | ||
|   if (response == GTK_RESPONSE_YES) { | ||
|     request_do_action(args->act_id, args->actor_unit_id, | ||
|                       args->target_unit_id, 0, ""); | ||
|   } | ||
|   gtk_window_destroy(GTK_WINDOW(w)); | ||
|   free(args); | ||
|   /* The user have answered the follow up question. Move on. */ | ||
|   diplomat_queue_handle_secondary(); | ||
| } | ||
| /**********************************************************************//** | ||
|   User responded to bribe stack dialog | ||
| **************************************************************************/ | ||
| static void bribe_stack_response(GtkWidget *w, gint response, gpointer data) | ||
| { | ||
|   struct action_data *args = (struct action_data *)data; | ||
|   if (response == GTK_RESPONSE_YES) { | ||
|     request_do_action(args->act_id, args->actor_unit_id, | ||
|                       args->target_tile_id, 0, ""); | ||
|   } | ||
|   gtk_window_destroy(GTK_WINDOW(w)); | ||
|   free(args); | ||
|   /* The user have answered the follow up question. Move on. */ | ||
|   diplomat_queue_handle_secondary(); | ||
| } | ||
| /**********************************************************************//** | ||
|   Popup unit bribe dialog | ||
| **************************************************************************/ | ||
| void popup_bribe_unit_dialog(struct unit *actor, struct unit *punit, int cost, | ||
|                              const struct action *paction) | ||
| { | ||
|   GtkWidget *shell; | ||
|   char buf[1024]; | ||
|   fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Treasury contains %d gold.", | ||
|                                         "Treasury contains %d gold.", | ||
|                                         client_player()->economic.gold), | ||
|               client_player()->economic.gold); | ||
|   if (cost <= client_player()->economic.gold) { | ||
|     shell = gtk_message_dialog_new(NULL, 0, | ||
|       GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, | ||
|       /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */ | ||
|       PL_("Bribe unit for %d gold?\n%s", | ||
|           "Bribe unit for %d gold?\n%s", cost), cost, buf); | ||
|     gtk_window_set_title(GTK_WINDOW(shell), _("Bribe Enemy Unit")); | ||
|     setup_dialog(shell, toplevel); | ||
|   } else { | ||
|     shell = gtk_message_dialog_new(NULL, 0, | ||
|       GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, | ||
|       /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */ | ||
|       PL_("Bribing the unit costs %d gold.\n%s", | ||
|           "Bribing the unit costs %d gold.\n%s", cost), cost, buf); | ||
|     gtk_window_set_title(GTK_WINDOW(shell), _("Traitors Demand Too Much!")); | ||
|     setup_dialog(shell, toplevel); | ||
|   } | ||
|   gtk_window_present(GTK_WINDOW(shell)); | ||
|   g_signal_connect(shell, "response", G_CALLBACK(bribe_unit_response), | ||
|                    act_data(paction->id, actor->id, | ||
|                             0, punit->id, 0, | ||
|                             0, 0, 0)); | ||
| } | ||
| /**********************************************************************//** | ||
|   Popup stack bribe dialog | ||
| **************************************************************************/ | ||
| void popup_bribe_stack_dialog(struct unit *actor, struct tile *ptile, int cost, | ||
|                              const struct action *paction) | ||
| { | ||
|   GtkWidget *shell; | ||
|   char buf[1024]; | ||
|   fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Treasury contains %d gold.", | ||
|                                         "Treasury contains %d gold.", | ||
|                                         client_player()->economic.gold), | ||
|               client_player()->economic.gold); | ||
|   if (cost <= client_player()->economic.gold) { | ||
|     shell = gtk_message_dialog_new(NULL, 0, | ||
|       GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, | ||
|       /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */ | ||
|       PL_("Bribe unit stack for %d gold?\n%s", | ||
|           "Bribe unit stack for %d gold?\n%s", cost), cost, buf); | ||
|     gtk_window_set_title(GTK_WINDOW(shell), _("Bribe Enemy Stack")); | ||
|     setup_dialog(shell, toplevel); | ||
|   } else { | ||
|     shell = gtk_message_dialog_new(NULL, 0, | ||
|       GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, | ||
|       /* TRANS: %s is pre-pluralised "Treasury contains %d gold." */ | ||
|       PL_("Bribing units costs %d gold.\n%s", | ||
|           "Bribing units costs %d gold.\n%s", cost), cost, buf); | ||
|     gtk_window_set_title(GTK_WINDOW(shell), _("Traitors Demand Too Much!")); | ||
|     setup_dialog(shell, toplevel); | ||
|   } | ||
|   gtk_window_present(GTK_WINDOW(shell)); | ||
|   g_signal_connect(shell, "response", G_CALLBACK(bribe_stack_response), | ||
|                    act_data(paction->id, actor->id, | ||
|                             0, 0, ptile->index, | ||
|                             0, 0, 0)); | ||
| } | ||
| /**********************************************************************//** | ||
|   User responded to steal advances dialog | ||
| **************************************************************************/ | ||
| static void spy_advances_response(GtkWidget *w, gint response, | ||
|                                   gpointer data) | ||
| { | ||
|   struct action_data *args = (struct action_data *)data; | ||
|   if (response == GTK_RESPONSE_ACCEPT && args->target_tech_id > 0) { | ||
|     if (NULL != game_unit_by_number(args->actor_unit_id) | ||
|         && NULL != game_city_by_number(args->target_city_id)) { | ||
|       if (args->target_tech_id == A_UNSET) { | ||
|         /* This is the untargeted version. */ | ||
|         request_do_action(get_non_targeted_action_id(args->act_id), | ||
|                           args->actor_unit_id, args->target_city_id, | ||
|                           args->target_tech_id, ""); | ||
|       } else { | ||
|         /* This is the targeted version. */ | ||
|         request_do_action(args->act_id, | ||
|                           args->actor_unit_id, args->target_city_id, | ||
|                           args->target_tech_id, ""); | ||
|       } | ||
|     } | ||
|   } | ||
|   gtk_window_destroy(GTK_WINDOW(spy_tech_shell)); | ||
|   spy_tech_shell = NULL; | ||
|   free(data); | ||
|   /* The user have answered the follow up question. Move on. */ | ||
|   diplomat_queue_handle_secondary(); | ||
| } | ||
| /**********************************************************************//** | ||
|   User selected entry in steal advances dialog | ||
| **************************************************************************/ | ||
| static void spy_advances_callback(GtkSelectionModel *self, | ||
|                                   guint position, | ||
|                                   guint n_items, | ||
|                                   gpointer data) | ||
| { | ||
|   struct action_data *args = (struct action_data *)data; | ||
|   FcActionRow *row = gtk_single_selection_get_selected_item( | ||
|                                     GTK_SINGLE_SELECTION(self)); | ||
|   if (row != NULL) { | ||
|     args->target_tech_id = row->id; | ||
|     gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_tech_shell), | ||
|       GTK_RESPONSE_ACCEPT, TRUE); | ||
|   } else { | ||
|     args->target_tech_id = 0; | ||
|     gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_tech_shell), | ||
|       GTK_RESPONSE_ACCEPT, FALSE); | ||
|   } | ||
| } | ||
| /**********************************************************************//** | ||
|   Action table cell bind function | ||
| **************************************************************************/ | ||
| static void action_factory_bind(GtkSignalListItemFactory *self, | ||
|                                 GtkListItem *list_item, | ||
|                                 gpointer user_data) | ||
| { | ||
|   FcActionRow *row; | ||
|   row = gtk_list_item_get_item(list_item); | ||
|   gtk_label_set_text(GTK_LABEL(gtk_list_item_get_child(list_item)), | ||
|                      row->name); | ||
| } | ||
| /**********************************************************************//** | ||
|   Action table cell setup function | ||
| **************************************************************************/ | ||
| static void action_factory_setup(GtkSignalListItemFactory *self, | ||
|                                  GtkListItem *list_item, | ||
|                                  gpointer user_data) | ||
| { | ||
|   gtk_list_item_set_child(list_item, gtk_label_new("")); | ||
| } | ||
| /**********************************************************************//** | ||
|   Create spy's tech stealing dialog | ||
| **************************************************************************/ | ||
| static void create_advances_list(struct player *pplayer, | ||
|                                  struct player *pvictim, | ||
|                                  struct action_data *args) | ||
| { | ||
|   GtkWidget *frame, *label, *vgrid; | ||
|   GListStore *store; | ||
|   GtkWidget *list; | ||
|   GtkColumnViewColumn *column; | ||
|   GtkListItemFactory *factory; | ||
|   GtkSingleSelection *selection; | ||
|   struct unit *actor_unit = game_unit_by_number(args->actor_unit_id); | ||
|   spy_tech_shell = gtk_dialog_new_with_buttons(_("Steal Technology"), | ||
|                                                NULL, 0, | ||
|                                                _("_Cancel"), GTK_RESPONSE_CANCEL, | ||
|                                                _("_Steal"), GTK_RESPONSE_ACCEPT, | ||
|                                                NULL); | ||
|   setup_dialog(spy_tech_shell, toplevel); | ||
|   gtk_dialog_set_default_response(GTK_DIALOG(spy_tech_shell), | ||
|                                   GTK_RESPONSE_ACCEPT); | ||
|   frame = gtk_frame_new(_("Select Advance to Steal")); | ||
|   gtk_box_append(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(spy_tech_shell))), frame); | ||
|   vgrid = gtk_grid_new(); | ||
|   gtk_orientable_set_orientation(GTK_ORIENTABLE(vgrid), | ||
|                                  GTK_ORIENTATION_VERTICAL); | ||
|   gtk_grid_set_row_spacing(GTK_GRID(vgrid), 6); | ||
|   gtk_frame_set_child(GTK_FRAME(frame), vgrid); | ||
|   store = g_list_store_new(FC_TYPE_ACTION_ROW); | ||
|   selection = gtk_single_selection_new(G_LIST_MODEL(store)); | ||
|   list = gtk_column_view_new(GTK_SELECTION_MODEL(selection)); | ||
|   factory = gtk_signal_list_item_factory_new(); | ||
|   g_signal_connect(factory, "bind", G_CALLBACK(action_factory_bind), | ||
|                    nullptr); | ||
|   g_signal_connect(factory, "setup", G_CALLBACK(action_factory_setup), | ||
|                    nullptr); | ||
|   column = gtk_column_view_column_new(_("Tech"), factory); | ||
|   gtk_column_view_append_column(GTK_COLUMN_VIEW(list), column); | ||
|   label = g_object_new(GTK_TYPE_LABEL, | ||
|     "use-underline", TRUE, | ||
|     "mnemonic-widget", list, | ||
|     "label", _("_Advances:"), | ||
|     "xalign", 0.0, | ||
|     "yalign", 0.5, | ||
|     NULL); | ||
|   gtk_grid_attach(GTK_GRID(vgrid), label, 0, 0, 1, 1); | ||
|   gtk_grid_attach(GTK_GRID(vgrid), list, 0, 1, 1, 1); | ||
|   /* Now populate the list */ | ||
|   if (pvictim) { /* You don't want to know what lag can do -- Syela */ | ||
|     const struct research *presearch = research_get(pplayer); | ||
|     const struct research *vresearch = research_get(pvictim); | ||
|     GValue value = { 0, }; | ||
|     advance_index_iterate(A_FIRST, i) { | ||
|       if (research_invention_gettable(presearch, i, | ||
|                                       game.info.tech_steal_allow_holes) | ||
|           && research_invention_state(vresearch, i) == TECH_KNOWN | ||
|           && research_invention_state(presearch, i) != TECH_KNOWN) { | ||
|         FcActionRow *row = fc_action_row_new(); | ||
|         row->name = fc_strdup(research_advance_name_translation(presearch, i)); | ||
|         row->id = i; | ||
|         g_list_store_append(store, row); | ||
|         g_object_unref(row); | ||
|       } | ||
|     } advance_index_iterate_end; | ||
|     if (action_prob_possible(actor_unit->client.act_prob_cache[ | ||
|                              get_non_targeted_action_id(args->act_id)])) { | ||
|       FcActionRow *row = fc_action_row_new(); | ||
|       { | ||
|         struct astring str = ASTRING_INIT; | ||
|         /* TRANS: %s is a unit name, e.g., Spy */ | ||
|         astr_set(&str, _("At %s's Discretion"), | ||
|                  unit_name_translation(actor_unit)); | ||
|         g_value_set_string(&value, astr_str(&str)); | ||
|         row->name = fc_strdup(astr_str(&str)); | ||
|         astr_free(&str); | ||
|       } | ||
|       row->id = A_UNSET; | ||
|       g_list_store_append(store, row); | ||
|       g_object_unref(row); | ||
|     } | ||
|   } | ||
|   gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_tech_shell), | ||
|     GTK_RESPONSE_ACCEPT, FALSE); | ||
|   gtk_widget_set_visible(gtk_dialog_get_content_area(GTK_DIALOG(spy_tech_shell)), | ||
|                          TRUE); | ||
|   g_signal_connect(selection, "selection-changed", | ||
|                    G_CALLBACK(spy_advances_callback), args); | ||
|   g_signal_connect(spy_tech_shell, "response", | ||
|                    G_CALLBACK(spy_advances_response), args); | ||
|   args->target_tech_id = 0; | ||
| } | ||
| /**********************************************************************//** | ||
|   User has responded to spy's sabotage building dialog | ||
| **************************************************************************/ | ||
| static void spy_improvements_response(GtkWidget *w, gint response, gpointer data) | ||
| { | ||
|   struct action_data *args = (struct action_data *)data; | ||
|   if (response == GTK_RESPONSE_ACCEPT && args->target_building_id > -2) { | ||
|     if (NULL != game_unit_by_number(args->actor_unit_id) | ||
|         && NULL != game_city_by_number(args->target_city_id)) { | ||
|       if (args->target_building_id == B_LAST) { | ||
|         /* This is the untargeted version. */ | ||
|         request_do_action(get_non_targeted_action_id(args->act_id), | ||
|                           args->actor_unit_id, | ||
|                           args->target_city_id, | ||
|                           args->target_building_id, ""); | ||
|       } else if (args->target_building_id == -1) { | ||
|         /* This is the city production version. */ | ||
|         request_do_action(get_production_targeted_action_id(args->act_id), | ||
|                           args->actor_unit_id, | ||
|                           args->target_city_id, | ||
|                           args->target_building_id, ""); | ||
|       } else { | ||
|         /* This is the targeted version. */ | ||
|         request_do_action(args->act_id, | ||
|                           args->actor_unit_id, | ||
|                           args->target_city_id, | ||
|                           args->target_building_id, ""); | ||
|       } | ||
|     } | ||
|   } | ||
|   gtk_window_destroy(GTK_WINDOW(spy_sabotage_shell)); | ||
|   spy_sabotage_shell = NULL; | ||
|   free(args); | ||
|   /* The user have answered the follow up question. Move on. */ | ||
|   diplomat_queue_handle_secondary(); | ||
| } | ||
| /**********************************************************************//** | ||
|   User has selected new building from spy's sabotage dialog | ||
| **************************************************************************/ | ||
| static void spy_improvements_callback(GtkSelectionModel *self, | ||
|                                       guint position, | ||
|                                       guint n_items, | ||
|                                       gpointer data) | ||
| { | ||
|   struct action_data *args = (struct action_data *)data; | ||
|   FcActionRow *row = gtk_single_selection_get_selected_item( | ||
|                                     GTK_SINGLE_SELECTION(self)); | ||
|   if (row != NULL) { | ||
|     args->target_building_id = row->id; | ||
|     gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_sabotage_shell), | ||
|                                       GTK_RESPONSE_ACCEPT, TRUE); | ||
|   } else { | ||
|     args->target_building_id = -2; | ||
|     gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_sabotage_shell), | ||
|                                       GTK_RESPONSE_ACCEPT, FALSE); | ||
|   } | ||
| } | ||
| /**********************************************************************//** | ||
|   Creates spy's building sabotaging dialog | ||
| **************************************************************************/ | ||
| static void create_improvements_list(struct player *pplayer, | ||
|                                      struct city *pcity, | ||
|                                      struct action_data *args) | ||
| { | ||
|   GtkWidget *frame, *label, *vgrid; | ||
|   GListStore *store; | ||
|   GtkWidget *list; | ||
|   GtkColumnViewColumn *column; | ||
|   GtkListItemFactory *factory; | ||
|   GtkSingleSelection *selection; | ||
|   struct unit *actor_unit = game_unit_by_number(args->actor_unit_id); | ||
|   spy_sabotage_shell = gtk_dialog_new_with_buttons(_("Sabotage Improvements"), | ||
|                                                    NULL, 0, | ||
|                                                    _("_Cancel"), GTK_RESPONSE_CANCEL, | ||
|                                                    _("_Sabotage"), GTK_RESPONSE_ACCEPT, | ||
|                                                    NULL); | ||
|   setup_dialog(spy_sabotage_shell, toplevel); | ||
|   gtk_dialog_set_default_response(GTK_DIALOG(spy_sabotage_shell), | ||
|                                   GTK_RESPONSE_ACCEPT); | ||
|   frame = gtk_frame_new(_("Select Improvement to Sabotage")); | ||
|   gtk_box_append(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(spy_sabotage_shell))), frame); | ||
|   vgrid = gtk_grid_new(); | ||
|   gtk_orientable_set_orientation(GTK_ORIENTABLE(vgrid), | ||
|                                  GTK_ORIENTATION_VERTICAL); | ||
|   gtk_grid_set_row_spacing(GTK_GRID(vgrid), 6); | ||
|   gtk_frame_set_child(GTK_FRAME(frame), vgrid); | ||
|   store = g_list_store_new(FC_TYPE_ACTION_ROW); | ||
|   selection = gtk_single_selection_new(G_LIST_MODEL(store)); | ||
|   list = gtk_column_view_new(GTK_SELECTION_MODEL(selection)); | ||
|   factory = gtk_signal_list_item_factory_new(); | ||
|   g_signal_connect(factory, "bind", G_CALLBACK(action_factory_bind), | ||
|                    nullptr); | ||
|   g_signal_connect(factory, "setup", G_CALLBACK(action_factory_setup), | ||
|                    nullptr); | ||
|   column = gtk_column_view_column_new(_("Improvement"), factory); | ||
|   gtk_column_view_append_column(GTK_COLUMN_VIEW(list), column); | ||
|   label = g_object_new(GTK_TYPE_LABEL, | ||
|     "use-underline", TRUE, | ||
|     "mnemonic-widget", list, | ||
|     "label", _("_Improvements:"), | ||
|     "xalign", 0.0, | ||
|     "yalign", 0.5, | ||
|     NULL); | ||
|   gtk_grid_attach(GTK_GRID(vgrid), label, 0, 0, 1, 1); | ||
|   gtk_grid_attach(GTK_GRID(vgrid), list, 0, 1, 1, 1); | ||
|   /* Now populate the list */ | ||
|   if (action_prob_possible(actor_unit->client.act_prob_cache[ | ||
|                            get_production_targeted_action_id( | ||
|                                args->act_id)])) { | ||
|     FcActionRow *row = fc_action_row_new(); | ||
|     row->name = fc_strdup(_("City Production")); | ||
|     row->id = -1; | ||
|     g_list_store_append(store, row); | ||
|     g_object_unref(row); | ||
|   } | ||
|   city_built_iterate(pcity, pimprove) { | ||
|     if (pimprove->sabotage > 0) { | ||
|       FcActionRow *row = fc_action_row_new(); | ||
|       row->name = fc_strdup(city_improvement_name_translation(pcity, pimprove)); | ||
|       row->id = improvement_number(pimprove); | ||
|       g_list_store_append(store, row); | ||
|       g_object_unref(row); | ||
|     } | ||
|   } city_built_iterate_end; | ||
|   if (action_prob_possible(actor_unit->client.act_prob_cache[ | ||
|                            get_non_targeted_action_id(args->act_id)])) { | ||
|     struct astring str = ASTRING_INIT; | ||
|     FcActionRow *row = fc_action_row_new(); | ||
|     /* TRANS: %s is a unit name, e.g., Spy */ | ||
|     astr_set(&str, _("At %s's Discretion"), | ||
|              unit_name_translation(actor_unit)); | ||
|     row->name = fc_strdup(astr_str(&str)); | ||
|     row->id = B_LAST; | ||
|     g_list_store_append(store, row); | ||
|     g_object_unref(row); | ||
|     astr_free(&str); | ||
|   } | ||
|   gtk_dialog_set_response_sensitive(GTK_DIALOG(spy_sabotage_shell), | ||
|                                     GTK_RESPONSE_ACCEPT, FALSE); | ||