1 /***********************************************************************
2  Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 ***********************************************************************/
13 
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 
22 #include <gtk/gtk.h>
23 #include <gdk/gdkkeysyms.h>
24 
25 /* utility */
26 #include "bitvector.h"
27 #include "fcintl.h"
28 #include "log.h"
29 #include "mem.h"
30 #include "shared.h"
31 #include "support.h"
32 
33 /* common */
34 #include "city.h"
35 #include "game.h"
36 #include "map.h"
37 #include "movement.h"
38 #include "packets.h"
39 #include "player.h"
40 #include "unitlist.h"
41 
42 /* client */
43 #include "chatline_common.h"
44 #include "client_main.h"
45 #include "colors.h"
46 #include "control.h"
47 #include "climap.h"
48 #include "options.h"
49 #include "text.h"
50 #include "tilespec.h"
51 
52 /* client/agents */
53 #include "cma_fec.h"
54 
55 /* client/gui-gtk-3.0 */
56 #include "choice_dialog.h"
57 #include "citizensinfo.h"
58 #include "cityrep.h"
59 #include "cma_fe.h"
60 #include "dialogs.h"
61 #include "graphics.h"
62 #include "gui_main.h"
63 #include "gui_stuff.h"
64 #include "happiness.h"
65 #include "helpdlg.h"
66 #include "inputdlg.h"
67 #include "mapview.h"
68 #include "repodlgs.h"
69 #include "wldlg.h"
70 
71 #include "citydlg.h"
72 
73 #define CITYMAP_WIDTH MIN(512, canvas_width)
74 #define CITYMAP_HEIGHT (CITYMAP_WIDTH * canvas_height / canvas_width)
75 #define CITYMAP_SCALE ((double)CITYMAP_WIDTH / (double)canvas_width)
76 
77 #define TINYSCREEN_MAX_HEIGHT (500 - 1)
78 
79 /* Only CDLGR_UNITS button currently uses these, others have
80  * direct callback. */
81 enum citydlg_response { CDLGR_UNITS, CDLGR_PREV, CDLGR_NEXT };
82 
83 struct city_dialog;
84 
85 /* get 'struct dialog_list' and related function */
86 #define SPECLIST_TAG dialog
87 #define SPECLIST_TYPE struct city_dialog
88 #include "speclist.h"
89 
90 #define dialog_list_iterate(dialoglist, pdialog) \
91     TYPED_LIST_ITERATE(struct city_dialog, dialoglist, pdialog)
92 #define dialog_list_iterate_end  LIST_ITERATE_END
93 
94 struct unit_node {
95   GtkWidget *cmd;
96   GtkWidget *pix;
97   int height;
98 };
99 
100 /* get 'struct unit_node' and related function */
101 #define SPECVEC_TAG unit_node
102 #define SPECVEC_TYPE struct unit_node
103 #include "specvec.h"
104 
105 #define unit_node_vector_iterate(list, elt) \
106     TYPED_VECTOR_ITERATE(struct unit_node, list, elt)
107 #define unit_node_vector_iterate_end  VECTOR_ITERATE_END
108 
109 #define NUM_CITIZENS_SHOWN 30
110 
111 enum { OVERVIEW_PAGE, WORKLIST_PAGE, HAPPINESS_PAGE, CMA_PAGE,
112        SETTINGS_PAGE, STICKY_PAGE,
113        NUM_PAGES  /* the number of pages in city dialog notebook
114                    * must match the entries of misc_whichtab_label[] */
115 };
116 
117 enum {
118     INFO_SIZE, INFO_FOOD, INFO_SHIELD, INFO_TRADE, INFO_GOLD,
119     INFO_LUXURY, INFO_SCIENCE, INFO_GRANARY, INFO_GROWTH,
120     INFO_CORRUPTION, INFO_WASTE, INFO_CULTURE, INFO_POLLUTION,
121     INFO_ILLNESS, INFO_AIRLIFT,
122     NUM_INFO_FIELDS  /* number of fields in city_info
123                       * must match the entries of output_label[] */
124 };
125 
126 
127 /* minimal size for the city map scrolling windows*/
128 #define CITY_MAP_MIN_SIZE_X  200
129 #define CITY_MAP_MIN_SIZE_Y  150
130 
131 struct city_map_canvas {
132   GtkWidget *sw;
133   GtkWidget *ebox;
134   GtkWidget *darea;
135 };
136 
137 struct city_dialog {
138   struct city *pcity;
139 
140   GtkWidget *shell;
141   GtkWidget *name_label;
142   cairo_surface_t *map_canvas_store_unscaled;
143   GtkWidget *notebook;
144 
145   GtkWidget *popup_menu;
146   GtkWidget *citizen_images;
147   cairo_surface_t *citizen_surface;
148 
149   struct {
150     struct city_map_canvas map_canvas;
151 
152     GtkWidget *production_bar;
153     GtkWidget *production_combo;
154     GtkWidget *buy_command;
155     GtkWidget *improvement_list;
156 
157     GtkWidget *supported_units_frame;
158     GtkWidget *supported_unit_table;
159 
160     GtkWidget *present_units_frame;
161     GtkWidget *present_unit_table;
162 
163     struct unit_node_vector supported_units;
164     struct unit_node_vector present_units;
165 
166     GtkWidget *info_ebox[NUM_INFO_FIELDS];
167     GtkWidget *info_label[NUM_INFO_FIELDS];
168 
169     GtkListStore* change_production_store;
170   } overview;
171 
172   struct {
173     GtkWidget *production_label;
174     GtkWidget *production_bar;
175     GtkWidget *buy_command;
176     GtkWidget *worklist;
177   } production;
178 
179   struct {
180     struct city_map_canvas map_canvas;
181 
182     GtkWidget *widget;
183     GtkWidget *info_ebox[NUM_INFO_FIELDS];
184     GtkWidget *info_label[NUM_INFO_FIELDS];
185     GtkWidget *citizens;
186   } happiness;
187 
188   struct cma_dialog *cma_editor;
189 
190   struct {
191     GtkWidget *rename_command;
192     GtkWidget *new_citizens_radio[3];
193     GtkWidget *disband_on_settler;
194     GtkWidget *whichtab_radio[NUM_PAGES];
195     short block_signal;
196   } misc;
197 
198   GtkWidget *buy_shell, *sell_shell;
199   GtkTreeSelection *change_selection;
200   GtkWidget *rename_shell, *rename_input;
201 
202   GtkWidget *show_units_command;
203   GtkWidget *prev_command, *next_command;
204 
205   Impr_type_id sell_id;
206 
207   int cwidth;
208 };
209 
210 static struct dialog_list *dialog_list;
211 static bool city_dialogs_have_been_initialised = FALSE;
212 static int canvas_width, canvas_height;
213 static int new_dialog_def_page = OVERVIEW_PAGE;
214 static int last_page = OVERVIEW_PAGE;
215 
216 static bool is_showing_workertask_dialog = FALSE;
217 
218 static struct
219 {
220   struct city *owner;
221   struct tile *loc;
222 } workertask_req;
223 
224 static bool low_citydlg;
225 
226 /****************************************/
227 
228 static void initialize_city_dialogs(void);
229 static void city_dialog_map_create(struct city_dialog *pdialog,
230                                    struct city_map_canvas *cmap_canvas);
231 static void city_dialog_map_recenter(GtkWidget *map_canvas_sw);
232 
233 static struct city_dialog *get_city_dialog(struct city *pcity);
234 static gboolean keyboard_handler(GtkWidget * widget, GdkEventKey * event,
235 				 struct city_dialog *pdialog);
236 
237 static GtkWidget *create_city_info_table(struct city_dialog *pdialog,
238     					 GtkWidget **info_ebox,
239 					 GtkWidget **info_label);
240 static void create_and_append_overview_page(struct city_dialog *pdialog);
241 static void create_and_append_map_page(struct city_dialog *pdialog);
242 static void create_and_append_buildings_page(struct city_dialog *pdialog);
243 static void create_and_append_worklist_page(struct city_dialog *pdialog);
244 static void create_and_append_happiness_page(struct city_dialog *pdialog);
245 static void create_and_append_cma_page(struct city_dialog *pdialog);
246 static void create_and_append_settings_page(struct city_dialog *pdialog);
247 
248 static struct city_dialog *create_city_dialog(struct city *pcity);
249 
250 static void city_dialog_update_title(struct city_dialog *pdialog);
251 static void city_dialog_update_citizens(struct city_dialog *pdialog);
252 static void city_dialog_update_information(GtkWidget **info_ebox,
253 					   GtkWidget **info_label,
254                                            struct city_dialog *pdialog);
255 static void city_dialog_update_map(struct city_dialog *pdialog);
256 static void city_dialog_update_building(struct city_dialog *pdialog);
257 static void city_dialog_update_improvement_list(struct city_dialog
258 						*pdialog);
259 static void city_dialog_update_supported_units(struct city_dialog
260 					       *pdialog);
261 static void city_dialog_update_present_units(struct city_dialog *pdialog);
262 static void city_dialog_update_prev_next(void);
263 
264 static void show_units_response(void *data);
265 
266 static gboolean supported_unit_callback(GtkWidget * w, GdkEventButton * ev,
267 				        gpointer data);
268 static gboolean present_unit_callback(GtkWidget * w, GdkEventButton * ev,
269 				      gpointer data);
270 static gboolean supported_unit_middle_callback(GtkWidget * w,
271 					       GdkEventButton * ev,
272 					       gpointer data);
273 static gboolean present_unit_middle_callback(GtkWidget * w,
274 					     GdkEventButton * ev,
275 					     gpointer data);
276 
277 static void unit_center_callback(GtkWidget * w, gpointer data);
278 static void unit_activate_callback(GtkWidget * w, gpointer data);
279 static void supported_unit_activate_close_callback(GtkWidget * w,
280 						   gpointer data);
281 static void present_unit_activate_close_callback(GtkWidget * w,
282 						 gpointer data);
283 static void unit_load_callback(GtkWidget * w, gpointer data);
284 static void unit_unload_callback(GtkWidget * w, gpointer data);
285 static void unit_sentry_callback(GtkWidget * w, gpointer data);
286 static void unit_fortify_callback(GtkWidget * w, gpointer data);
287 static void unit_disband_callback(GtkWidget * w, gpointer data);
288 static void unit_homecity_callback(GtkWidget * w, gpointer data);
289 static void unit_upgrade_callback(GtkWidget * w, gpointer data);
290 
291 static gboolean citizens_callback(GtkWidget * w, GdkEventButton * ev,
292 			      gpointer data);
293 static gboolean button_down_citymap(GtkWidget * w, GdkEventButton * ev,
294 				    gpointer data);
295 static void draw_map_canvas(struct city_dialog *pdialog);
296 
297 static void buy_callback(GtkWidget * w, gpointer data);
298 static void change_production_callback(GtkComboBox *combo,
299                                        struct city_dialog *pdialog);
300 
301 static void sell_callback(struct impr_type *pimprove, gpointer data);
302 static void sell_callback_response(GtkWidget *w, gint response, gpointer data);
303 
304 static void impr_callback(GtkTreeView *view, GtkTreePath *path,
305 			  GtkTreeViewColumn *col, gpointer data);
306 
307 static void rename_callback(GtkWidget * w, gpointer data);
308 static void rename_popup_callback(gpointer data, gint response,
309                                   const char *input);
310 static void set_cityopt_values(struct city_dialog *pdialog);
311 static void cityopt_callback(GtkWidget * w, gpointer data);
312 static void misc_whichtab_callback(GtkWidget * w, gpointer data);
313 
314 static void city_destroy_callback(GtkWidget *w, gpointer data);
315 static void close_city_dialog(struct city_dialog *pdialog);
316 static void citydlg_response_callback(GtkDialog *dlg, gint response,
317                                       void *data);
318 static void close_callback(GtkWidget *w, gpointer data);
319 static void switch_city_callback(GtkWidget *w, gpointer data);
320 
321 /****************************************************************
322   Called to set the dimensions of the city dialog, both on
323   startup and if the tileset is changed.
324 *****************************************************************/
init_citydlg_dimensions(void)325 static void init_citydlg_dimensions(void)
326 {
327   canvas_width = get_citydlg_canvas_width();
328   canvas_height = get_citydlg_canvas_height();
329 }
330 
331 /****************************************************************
332   Initialize stuff needed for city dialogs
333 *****************************************************************/
initialize_city_dialogs(void)334 static void initialize_city_dialogs(void)
335 {
336   int height;
337 
338   fc_assert_ret(!city_dialogs_have_been_initialised);
339 
340   dialog_list = dialog_list_new();
341   init_citydlg_dimensions();
342   height = screen_height();
343 
344   /* Use default layout when height cannot be determined
345    * (when height == 0) */
346   if (height > 0 && height <= TINYSCREEN_MAX_HEIGHT) {
347     low_citydlg = TRUE;
348   } else {
349     low_citydlg = FALSE;
350   }
351 
352   city_dialogs_have_been_initialised = TRUE;
353 }
354 
355 /****************************************************************
356   Called when the tileset changes.
357 *****************************************************************/
reset_city_dialogs(void)358 void reset_city_dialogs(void)
359 {
360   if (!city_dialogs_have_been_initialised) {
361     return;
362   }
363 
364   init_citydlg_dimensions();
365 
366   dialog_list_iterate(dialog_list, pdialog) {
367     /* There's no reasonable way to resize a GtkImage, so we don't try.
368        Instead we just redraw the overview within the existing area.  The
369        player has to close and reopen the dialog to fix this. */
370     city_dialog_update_map(pdialog);
371   } dialog_list_iterate_end;
372 
373   popdown_all_city_dialogs();
374 }
375 
376 /****************************************************************
377   Return city dialog of the given city, or NULL is it doesn't
378   already exist
379 *****************************************************************/
get_city_dialog(struct city * pcity)380 static struct city_dialog *get_city_dialog(struct city *pcity)
381 {
382   if (!city_dialogs_have_been_initialised) {
383     initialize_city_dialogs();
384   }
385 
386   dialog_list_iterate(dialog_list, pdialog) {
387     if (pdialog->pcity == pcity)
388       return pdialog;
389   }
390   dialog_list_iterate_end;
391   return NULL;
392 }
393 
394 /***************************************************************************
395   Redraw map canvas on expose.
396 ****************************************************************************/
canvas_exposed_cb(GtkWidget * w,cairo_t * cr,gpointer data)397 static gboolean canvas_exposed_cb(GtkWidget *w, cairo_t *cr,
398                                   gpointer data)
399 {
400   struct city_dialog *pdialog = data;
401 
402   cairo_scale(cr, CITYMAP_SCALE, CITYMAP_SCALE);
403   cairo_set_source_surface(cr, pdialog->map_canvas_store_unscaled, 0, 0);
404   if (!gtk_widget_get_sensitive(pdialog->overview.map_canvas.ebox)) {
405     cairo_paint_with_alpha(cr, 0.5);
406   } else {
407     cairo_paint(cr);
408   }
409 
410   return TRUE;
411 }
412 
413 /***************************************************************************
414   Create a city map widget; used in the overview and in the happiness page.
415 ****************************************************************************/
city_dialog_map_create(struct city_dialog * pdialog,struct city_map_canvas * cmap_canvas)416 static void city_dialog_map_create(struct city_dialog *pdialog,
417                                    struct city_map_canvas *cmap_canvas)
418 {
419   GtkWidget *sw, *ebox, *darea;
420 
421   sw = gtk_scrolled_window_new(NULL, NULL);
422   gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(sw),
423                                             CITYMAP_WIDTH);
424   gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw),
425                                              CITYMAP_HEIGHT);
426   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
427                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
428   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
429                                       GTK_SHADOW_NONE);
430 
431   ebox = gtk_event_box_new();
432   gtk_widget_set_halign(ebox, GTK_ALIGN_CENTER);
433   gtk_widget_set_valign(ebox, GTK_ALIGN_CENTER);
434   gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
435   gtk_container_add(GTK_CONTAINER(sw), ebox);
436 
437   darea = gtk_drawing_area_new();
438   gtk_widget_add_events(darea, GDK_BUTTON_PRESS_MASK);
439   gtk_widget_set_size_request(darea, CITYMAP_WIDTH, CITYMAP_HEIGHT);
440   g_signal_connect(ebox, "button-press-event",
441                    G_CALLBACK(button_down_citymap), pdialog);
442   g_signal_connect(darea, "draw",
443                    G_CALLBACK(canvas_exposed_cb), pdialog);
444   gtk_container_add(GTK_CONTAINER(ebox), darea);
445 
446   /* save all widgets for the city map */
447   cmap_canvas->sw = sw;
448   cmap_canvas->ebox = ebox;
449   cmap_canvas->darea = darea;
450 }
451 
452 /****************************************************************
453   Center city dialog map.
454 *****************************************************************/
city_dialog_map_recenter(GtkWidget * map_canvas_sw)455 static void city_dialog_map_recenter(GtkWidget *map_canvas_sw) {
456   GtkAdjustment *adjust = NULL;
457   gdouble value;
458 
459   fc_assert_ret(map_canvas_sw != NULL);
460 
461   adjust = gtk_scrolled_window_get_hadjustment(
462     GTK_SCROLLED_WINDOW(map_canvas_sw));
463   value = (gtk_adjustment_get_lower(adjust)
464     + gtk_adjustment_get_upper(adjust)
465     - gtk_adjustment_get_page_size(adjust)) / 2;
466   gtk_adjustment_set_value(adjust, value);
467   gtk_adjustment_value_changed(adjust);
468 
469   adjust = gtk_scrolled_window_get_vadjustment(
470     GTK_SCROLLED_WINDOW(map_canvas_sw));
471   value = (gtk_adjustment_get_lower(adjust)
472     + gtk_adjustment_get_upper(adjust)
473     - gtk_adjustment_get_page_size(adjust)) / 2;
474   gtk_adjustment_set_value(adjust, value);
475   gtk_adjustment_value_changed(adjust);
476 }
477 
478 /****************************************************************
479   Refresh city dialog of the given city
480 *****************************************************************/
real_city_dialog_refresh(struct city * pcity)481 void real_city_dialog_refresh(struct city *pcity)
482 {
483   struct city_dialog *pdialog = get_city_dialog(pcity);
484 
485   log_debug("CITYMAP_WIDTH:  %d", CITYMAP_WIDTH);
486   log_debug("CITYMAP_HEIGHT: %d", CITYMAP_HEIGHT);
487   log_debug("CITYMAP_SCALE:  %.3f", CITYMAP_SCALE);
488 
489   if (city_owner(pcity) == client.conn.playing) {
490     city_report_dialog_update_city(pcity);
491     economy_report_dialog_update();
492   }
493 
494   if (!pdialog)
495     return;
496 
497   city_dialog_update_title(pdialog);
498   city_dialog_update_citizens(pdialog);
499   city_dialog_update_information(pdialog->overview.info_ebox,
500 				 pdialog->overview.info_label, pdialog);
501   city_dialog_update_map(pdialog);
502   city_dialog_update_building(pdialog);
503   city_dialog_update_improvement_list(pdialog);
504   city_dialog_update_supported_units(pdialog);
505   city_dialog_update_present_units(pdialog);
506 
507   if (!client_has_player() || city_owner(pcity) == client_player()) {
508     bool have_present_units = (unit_list_size(pcity->tile->units) > 0);
509 
510     refresh_worklist(pdialog->production.worklist);
511 
512     if (!low_citydlg) {
513       city_dialog_update_information(pdialog->happiness.info_ebox,
514                                      pdialog->happiness.info_label, pdialog);
515     }
516     refresh_happiness_dialog(pdialog->pcity);
517     if (game.info.citizen_nationality) {
518       citizens_dialog_refresh(pdialog->pcity);
519     }
520 
521     if (!client_is_observer()) {
522       refresh_cma_dialog(pdialog->pcity, REFRESH_ALL);
523     }
524 
525     gtk_widget_set_sensitive(pdialog->show_units_command,
526 			     can_client_issue_orders() &&
527 			     have_present_units);
528   } else {
529     /* Set the buttons we do not want live while a Diplomat investigates */
530     gtk_widget_set_sensitive(pdialog->show_units_command, FALSE);
531   }
532 }
533 
534 /****************************************************************
535   Refresh city dialogs of unit's homecity and city where unit
536   currently is.
537 *****************************************************************/
refresh_unit_city_dialogs(struct unit * punit)538 void refresh_unit_city_dialogs(struct unit *punit)
539 {
540   struct city *pcity_sup, *pcity_pre;
541   struct city_dialog *pdialog;
542 
543   pcity_sup = game_city_by_number(punit->homecity);
544   pcity_pre = tile_city(unit_tile(punit));
545 
546   if (pcity_sup && (pdialog = get_city_dialog(pcity_sup))) {
547     city_dialog_update_supported_units(pdialog);
548   }
549 
550   if (pcity_pre && (pdialog = get_city_dialog(pcity_pre))) {
551     city_dialog_update_present_units(pdialog);
552   }
553 }
554 
555 /****************************************************************
556 popup the dialog 10% inside the main-window
557 *****************************************************************/
real_city_dialog_popup(struct city * pcity)558 void real_city_dialog_popup(struct city *pcity)
559 {
560   struct city_dialog *pdialog;
561 
562   if (!(pdialog = get_city_dialog(pcity))) {
563     pdialog = create_city_dialog(pcity);
564   }
565 
566   gtk_window_present(GTK_WINDOW(pdialog->shell));
567 
568   /* center the city map(s); this must be *after* the city dialog was drawn
569    * else the size information is missing! */
570   city_dialog_map_recenter(pdialog->overview.map_canvas.sw);
571   if (pdialog->happiness.map_canvas.sw) {
572     city_dialog_map_recenter(pdialog->happiness.map_canvas.sw);
573   }
574 }
575 
576 /****************************************************************
577   Return whether city dialog for given city is open
578 *****************************************************************/
city_dialog_is_open(struct city * pcity)579 bool city_dialog_is_open(struct city *pcity)
580 {
581   return get_city_dialog(pcity) != NULL;
582 }
583 
584 /****************************************************************
585 popdown the dialog
586 *****************************************************************/
popdown_city_dialog(struct city * pcity)587 void popdown_city_dialog(struct city *pcity)
588 {
589   struct city_dialog *pdialog = get_city_dialog(pcity);
590 
591   if (pdialog) {
592     close_city_dialog(pdialog);
593   }
594 }
595 
596 /****************************************************************
597 popdown all dialogs
598 *****************************************************************/
popdown_all_city_dialogs(void)599 void popdown_all_city_dialogs(void)
600 {
601   if (!city_dialogs_have_been_initialised) {
602     return;
603   }
604 
605   while (dialog_list_size(dialog_list)) {
606     close_city_dialog(dialog_list_get(dialog_list, 0));
607   }
608   dialog_list_destroy(dialog_list);
609 
610   city_dialogs_have_been_initialised = FALSE;
611 }
612 
613 /**************************************************************************
614   Keyboard handler for city dialog
615 **************************************************************************/
keyboard_handler(GtkWidget * widget,GdkEventKey * event,struct city_dialog * pdialog)616 static gboolean keyboard_handler(GtkWidget * widget, GdkEventKey * event,
617 				 struct city_dialog *pdialog)
618 {
619   if (event->state & GDK_CONTROL_MASK) {
620     switch (event->keyval) {
621     case GDK_KEY_Left:
622       gtk_notebook_prev_page(GTK_NOTEBOOK(pdialog->notebook));
623       return TRUE;
624 
625     case GDK_KEY_Right:
626       gtk_notebook_next_page(GTK_NOTEBOOK(pdialog->notebook));
627       return TRUE;
628 
629     default:
630       break;
631     }
632   }
633 
634   return FALSE;
635 }
636 
637 /**************************************************************************
638   Destroy info popup dialog when button released
639 **************************************************************************/
show_info_button_release(GtkWidget * w,GdkEventButton * ev,gpointer data)640 static gboolean show_info_button_release(GtkWidget *w, GdkEventButton *ev,
641 					 gpointer data)
642 {
643   gtk_grab_remove(w);
644   gdk_device_ungrab(ev->device, ev->time);
645   gtk_widget_destroy(w);
646   return FALSE;
647 }
648 
649 /****************************************************************
650   Popup info dialog
651 *****************************************************************/
show_info_popup(GtkWidget * w,GdkEventButton * ev,gpointer data)652 static gboolean show_info_popup(GtkWidget *w, GdkEventButton *ev,
653     				gpointer data)
654 {
655   struct city_dialog *pdialog = g_object_get_data(G_OBJECT(w), "pdialog");
656 
657   if (ev->button == 1) {
658     GtkWidget *p, *label, *frame;
659     char buf[1024];
660 
661     switch (GPOINTER_TO_UINT(data)) {
662     case INFO_FOOD:
663       get_city_dialog_output_text(pdialog->pcity, O_FOOD, buf, sizeof(buf));
664       break;
665     case INFO_SHIELD:
666       get_city_dialog_output_text(pdialog->pcity, O_SHIELD,
667 				  buf, sizeof(buf));
668       break;
669     case INFO_TRADE:
670       get_city_dialog_output_text(pdialog->pcity, O_TRADE, buf, sizeof(buf));
671       break;
672     case INFO_GOLD:
673       get_city_dialog_output_text(pdialog->pcity, O_GOLD, buf, sizeof(buf));
674       break;
675     case INFO_SCIENCE:
676       get_city_dialog_output_text(pdialog->pcity, O_SCIENCE,
677 				  buf, sizeof(buf));
678       break;
679     case INFO_LUXURY:
680       get_city_dialog_output_text(pdialog->pcity, O_LUXURY,
681 				  buf, sizeof(buf));
682       break;
683     case INFO_CULTURE:
684       get_city_dialog_culture_text(pdialog->pcity, buf, sizeof(buf));
685       break;
686     case INFO_POLLUTION:
687       get_city_dialog_pollution_text(pdialog->pcity, buf, sizeof(buf));
688       break;
689     case INFO_ILLNESS:
690       get_city_dialog_illness_text(pdialog->pcity, buf, sizeof(buf));
691       break;
692     case INFO_AIRLIFT:
693       get_city_dialog_airlift_text(pdialog->pcity, buf, sizeof(buf));
694       break;
695     default:
696       return TRUE;
697     }
698 
699     p = gtk_window_new(GTK_WINDOW_POPUP);
700     gtk_widget_set_name(p, "Freeciv");
701     gtk_container_set_border_width(GTK_CONTAINER(p), 2);
702     gtk_window_set_transient_for(GTK_WINDOW(p), GTK_WINDOW(pdialog->shell));
703     gtk_window_set_position(GTK_WINDOW(p), GTK_WIN_POS_MOUSE);
704 
705     frame = gtk_frame_new(NULL);
706     gtk_container_add(GTK_CONTAINER(p), frame);
707 
708     label = gtk_label_new(buf);
709     gtk_widget_set_name(label, "city_label");
710     gtk_widget_set_margin_left(label, 4);
711     gtk_widget_set_margin_right(label, 4);
712     gtk_widget_set_margin_top(label, 4);
713     gtk_widget_set_margin_bottom(label, 4);
714     gtk_container_add(GTK_CONTAINER(frame), label);
715     gtk_widget_show_all(p);
716 
717     gdk_device_grab(ev->device, gtk_widget_get_window(p),
718                     GDK_OWNERSHIP_NONE, TRUE, GDK_BUTTON_RELEASE_MASK, NULL,
719                     ev->time);
720     gtk_grab_add(p);
721 
722     g_signal_connect_after(p, "button_release_event",
723                            G_CALLBACK(show_info_button_release), NULL);
724   }
725   return TRUE;
726 }
727 
728 /****************************************************************
729  used once in the overview page and once in the happiness page
730  **info_label points to the info_label in the respective struct
731 ****************************************************************/
create_city_info_table(struct city_dialog * pdialog,GtkWidget ** info_ebox,GtkWidget ** info_label)732 static GtkWidget *create_city_info_table(struct city_dialog *pdialog,
733     					 GtkWidget **info_ebox,
734 					 GtkWidget **info_label)
735 {
736   int i;
737   GtkWidget *table, *label, *ebox;
738 
739   static const char* output_label[NUM_INFO_FIELDS] = {
740     N_("Size:"),
741     N_("Food:"),
742     N_("Prod:"),
743     N_("Trade:"),
744     N_("Gold:"),
745     N_("Luxury:"),
746     N_("Science:"),
747     N_("Granary:"),
748     N_("Change in:"),
749     N_("Corruption:"),
750     N_("Waste:"),
751     N_("Culture:"),
752     N_("Pollution:"),
753     N_("Plague risk:"),
754     N_("Airlift:"),
755   };
756   static bool output_label_done;
757 
758   table = gtk_grid_new();
759   g_object_set(table, "margin", 4, NULL);
760 
761   intl_slist(ARRAY_SIZE(output_label), output_label, &output_label_done);
762 
763   for (i = 0; i < NUM_INFO_FIELDS; i++) {
764     label = gtk_label_new(output_label[i]);
765     switch (i) {
766     case INFO_SIZE:
767     case INFO_TRADE:
768     case INFO_SCIENCE:
769     case INFO_GROWTH:
770         gtk_widget_set_margin_bottom(label, 5);
771         break;
772 
773     case INFO_FOOD:
774     case INFO_GOLD:
775     case INFO_GRANARY:
776     case INFO_CORRUPTION:
777         gtk_widget_set_margin_top(label, 5);
778         break;
779       default:
780         break;
781     }
782     gtk_widget_set_margin_right(label, 5);
783     gtk_widget_set_name(label, "city_label");	/* for font style? */
784     gtk_widget_set_halign(label, GTK_ALIGN_START);
785     gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
786     gtk_grid_attach(GTK_GRID(table), label, 0, i, 1, 1);
787 
788     ebox = gtk_event_box_new();
789     switch (i) {
790     case INFO_TRADE:
791     case INFO_SCIENCE:
792     case INFO_GROWTH:
793         gtk_widget_set_margin_bottom(ebox, 5);
794         break;
795 
796     case INFO_GOLD:
797     case INFO_GRANARY:
798     case INFO_CORRUPTION:
799         gtk_widget_set_margin_top(ebox, 5);
800         break;
801       default:
802         break;
803     }
804     gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
805     g_object_set_data(G_OBJECT(ebox), "pdialog", pdialog);
806     g_signal_connect(ebox, "button_press_event",
807 	G_CALLBACK(show_info_popup), GUINT_TO_POINTER(i));
808     info_ebox[i] = ebox;
809 
810     label = gtk_label_new("");
811     info_label[i] = label;
812     gtk_widget_set_name(label, "city_label");	/* ditto */
813     gtk_widget_set_halign(label, GTK_ALIGN_START);
814     gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
815 
816     gtk_container_add(GTK_CONTAINER(ebox), label);
817 
818     gtk_grid_attach(GTK_GRID(table), ebox, 1, i, 1, 1);
819   }
820 
821   gtk_widget_show_all(table);
822 
823   return table;
824 }
825 
826 /****************************************************************
827   Create main citydlg map
828 *****************************************************************/
create_citydlg_main_map(struct city_dialog * pdialog,GtkWidget * container)829 static void create_citydlg_main_map(struct city_dialog *pdialog,
830                                     GtkWidget *container)
831 {
832   GtkWidget *frame;
833 
834   frame = gtk_frame_new(_("City map"));
835   gtk_widget_set_size_request(frame, CITY_MAP_MIN_SIZE_X,
836                               CITY_MAP_MIN_SIZE_Y);
837   gtk_container_add(GTK_CONTAINER(container), frame);
838 
839   city_dialog_map_create(pdialog, &pdialog->overview.map_canvas);
840   gtk_container_add(GTK_CONTAINER(frame), pdialog->overview.map_canvas.sw);
841 }
842 
843 /****************************************************************
844   Create improvements list
845 *****************************************************************/
create_citydlg_improvement_list(struct city_dialog * pdialog,GtkWidget * vbox)846 static GtkWidget *create_citydlg_improvement_list(struct city_dialog *pdialog,
847                                                   GtkWidget *vbox)
848 {
849   GtkWidget *view;
850   GtkListStore *store;
851   GtkCellRenderer *rend;
852 
853   /* improvements */
854   /* gtk list store columns: 0 - sell value, 1 - sprite,
855   2 - description, 3 - upkeep, 4 - is redundant, 5 - tooltip */
856   store = gtk_list_store_new(6, G_TYPE_POINTER, GDK_TYPE_PIXBUF,
857                              G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN,
858                              G_TYPE_STRING);
859 
860   view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
861   gtk_widget_set_hexpand(view, TRUE);
862   gtk_widget_set_vexpand(view, TRUE);
863   g_object_unref(store);
864   gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
865   gtk_widget_set_name(view, "small_font");
866   pdialog->overview.improvement_list = view;
867 
868   rend = gtk_cell_renderer_pixbuf_new();
869   gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL,
870                                               rend, "pixbuf", 1, NULL);
871   rend = gtk_cell_renderer_text_new();
872   gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL,
873                                               rend, "text", 2,
874                                               "strikethrough", 4, NULL);
875   rend = gtk_cell_renderer_text_new();
876   g_object_set(rend, "xalign", 1.0, NULL);
877   gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL,
878                                               rend, "text", 3,
879                                               "strikethrough", 4, NULL);
880 
881   gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(view), 5);
882 
883   g_signal_connect(view, "row_activated", G_CALLBACK(impr_callback),
884                    pdialog);
885 
886   return view;
887 }
888 
889 /****************************************************************
890                   **** Overview page ****
891  +- GtkWidget *page ------------------------------------------+
892  | +- GtkWidget *middle -----------+------------------------+ |
893  | | City map                      |  Production            | |
894  | +-------------------------------+------------------------+ |
895  +------------------------------------------------------------+
896  | +- GtkWidget *bottom -------+----------------------------+ |
897  | | Info                      | +- GtkWidget *right -----+ | |
898  | |                           | | supported units        | | |
899  | |                           | +------------------------+ | |
900  | |                           | | present units          | | |
901  | |                           | +------------------------+ | |
902  | +---------------------------+----------------------------+ |
903  +------------------------------------------------------------+
904 *****************************************************************/
create_and_append_overview_page(struct city_dialog * pdialog)905 static void create_and_append_overview_page(struct city_dialog *pdialog)
906 {
907   GtkWidget *page, *bottom;
908   GtkWidget *hbox, *right, *vbox, *frame, *table;
909   GtkWidget *label, *sw, *view, *bar, *production_combo;
910   GtkCellRenderer *rend;
911   GtkListStore *production_store;
912   /* TRANS: Overview tab in city dialog */
913   const char *tab_title = _("_Overview");
914   int unit_height = tileset_unit_with_upkeep_height(tileset);
915 
916   /* main page */
917   page = gtk_grid_new();
918   gtk_orientable_set_orientation(GTK_ORIENTABLE(page),
919                                  GTK_ORIENTATION_VERTICAL);
920   gtk_container_set_border_width(GTK_CONTAINER(page), 8);
921   label = gtk_label_new_with_mnemonic(tab_title);
922   gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
923 
924   if (!low_citydlg) {
925     GtkWidget *middle;
926 
927     /* middle: city map, improvements */
928     middle = gtk_grid_new();
929     gtk_grid_set_column_spacing(GTK_GRID(middle), 6);
930     gtk_container_add(GTK_CONTAINER(page), middle);
931 
932     /* city map */
933     create_citydlg_main_map(pdialog, middle);
934 
935     /* improvements */
936     vbox = gtk_grid_new();
937     gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox),
938                                    GTK_ORIENTATION_VERTICAL);
939     gtk_container_add(GTK_CONTAINER(middle), vbox);
940 
941     view = create_citydlg_improvement_list(pdialog, middle);
942 
943     label = g_object_new(GTK_TYPE_LABEL, "label", _("Production:"),
944                          "xalign", 0.0, "yalign", 0.5, NULL);
945     gtk_container_add(GTK_CONTAINER(vbox), label);
946 
947     hbox = gtk_grid_new();
948     gtk_grid_set_column_spacing(GTK_GRID(hbox), 10);
949     gtk_container_add(GTK_CONTAINER(vbox), hbox);
950 
951     production_store = gtk_list_store_new(4, GDK_TYPE_PIXBUF, G_TYPE_STRING,
952                                           G_TYPE_INT, G_TYPE_BOOLEAN);
953     pdialog->overview.change_production_store = production_store;
954 
955     production_combo =
956       gtk_combo_box_new_with_model(GTK_TREE_MODEL(production_store));
957     gtk_widget_set_hexpand(production_combo, TRUE);
958     pdialog->overview.production_combo = production_combo;
959     gtk_container_add(GTK_CONTAINER(hbox), production_combo);
960     g_object_unref(production_store);
961     g_signal_connect(production_combo, "changed",
962                      G_CALLBACK(change_production_callback), pdialog);
963 
964     gtk_cell_layout_clear(GTK_CELL_LAYOUT(production_combo));
965     rend = gtk_cell_renderer_pixbuf_new();
966     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(production_combo), rend, TRUE);
967     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(production_combo),
968                                    rend, "pixbuf", 0, NULL);
969     g_object_set(rend, "xalign", 0.0, NULL);
970 
971     rend = gtk_cell_renderer_text_new();
972     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(production_combo), rend, TRUE);
973     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(production_combo),
974                                    rend, "text", 1, "strikethrough", 3, NULL);
975 
976     bar = gtk_progress_bar_new();
977     gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(bar), TRUE);
978     pdialog->overview.production_bar = bar;
979     gtk_container_add(GTK_CONTAINER(production_combo), bar);
980     gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(production_combo), 3);
981 
982     gtk_progress_bar_set_text(GTK_PROGRESS_BAR(bar), _("%d/%d %d turns"));
983 
984     pdialog->overview.buy_command = gtk_stockbutton_new(GTK_STOCK_EXECUTE,
985                                                         _("_Buy"));
986     gtk_container_add(GTK_CONTAINER(hbox), pdialog->overview.buy_command);
987     g_signal_connect(pdialog->overview.buy_command, "clicked",
988                      G_CALLBACK(buy_callback), pdialog);
989 
990     label = g_object_new(GTK_TYPE_LABEL, "use-underline", TRUE,
991                          "mnemonic-widget", view,
992                          "label", _("I_mprovements:"),
993                          "xalign", 0.0, "yalign", 0.5, NULL);
994     gtk_container_add(GTK_CONTAINER(vbox), label);
995 
996     sw = gtk_scrolled_window_new(NULL, NULL);
997     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
998                                         GTK_SHADOW_ETCHED_IN);
999     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1000                                    GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1001     gtk_container_add(GTK_CONTAINER(vbox), sw);
1002 
1003     gtk_container_add(GTK_CONTAINER(sw), view);
1004   } else {
1005     pdialog->overview.buy_command = NULL;
1006     pdialog->overview.production_bar = NULL;
1007     pdialog->overview.production_combo = NULL;
1008     pdialog->overview.change_production_store = NULL;
1009   }
1010 
1011   /* bottom: info, units */
1012   bottom = gtk_grid_new();
1013   gtk_grid_set_column_spacing(GTK_GRID(bottom), 6);
1014   gtk_container_add(GTK_CONTAINER(page), bottom);
1015 
1016   /* info */
1017   frame = gtk_frame_new(_("Info"));
1018   gtk_container_add(GTK_CONTAINER(bottom), frame);
1019 
1020   table = create_city_info_table(pdialog,
1021                                  pdialog->overview.info_ebox,
1022                                  pdialog->overview.info_label);
1023   gtk_widget_set_halign(table, GTK_ALIGN_CENTER);
1024   gtk_widget_set_valign(table, GTK_ALIGN_CENTER);
1025   gtk_container_add(GTK_CONTAINER(frame), table);
1026 
1027   /* right: present and supported units (overview page) */
1028   right = gtk_grid_new();
1029   gtk_orientable_set_orientation(GTK_ORIENTABLE(right),
1030                                  GTK_ORIENTATION_VERTICAL);
1031   gtk_container_add(GTK_CONTAINER(bottom), right);
1032 
1033   pdialog->overview.supported_units_frame = gtk_frame_new("");
1034   gtk_container_add(GTK_CONTAINER(right),
1035                     pdialog->overview.supported_units_frame);
1036   pdialog->overview.present_units_frame = gtk_frame_new("");
1037   gtk_container_add(GTK_CONTAINER(right),
1038                     pdialog->overview.present_units_frame);
1039 
1040   /* supported units */
1041   sw = gtk_scrolled_window_new(NULL, NULL);
1042   gtk_widget_set_hexpand(sw, TRUE);
1043   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1044                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
1045   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1046                                       GTK_SHADOW_NONE);
1047   gtk_container_add(GTK_CONTAINER(pdialog->overview.supported_units_frame),
1048                                   sw);
1049 
1050 
1051   table = gtk_grid_new();
1052   gtk_grid_set_column_spacing(GTK_GRID(table), 2);
1053   gtk_widget_set_size_request(table, -1, unit_height);
1054   gtk_container_add(GTK_CONTAINER(sw), table);
1055 
1056   gtk_container_set_focus_hadjustment(GTK_CONTAINER(table),
1057     gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(sw)));
1058   gtk_container_set_focus_vadjustment(GTK_CONTAINER(table),
1059     gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw)));
1060 
1061   pdialog->overview.supported_unit_table = table;
1062   unit_node_vector_init(&pdialog->overview.supported_units);
1063 
1064   /* present units */
1065   sw = gtk_scrolled_window_new(NULL, NULL);
1066   gtk_widget_set_hexpand(sw, TRUE);
1067   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1068                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
1069   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1070                                       GTK_SHADOW_NONE);
1071   gtk_container_add(GTK_CONTAINER(pdialog->overview.present_units_frame), sw);
1072 
1073   table = gtk_grid_new();
1074   gtk_grid_set_column_spacing(GTK_GRID(table), 2);
1075   gtk_widget_set_size_request(table, -1, unit_height);
1076   gtk_container_add(GTK_CONTAINER(sw), table);
1077 
1078   gtk_container_set_focus_hadjustment(GTK_CONTAINER(table),
1079     gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(sw)));
1080   gtk_container_set_focus_vadjustment(GTK_CONTAINER(table),
1081     gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw)));
1082 
1083   pdialog->overview.present_unit_table = table;
1084   unit_node_vector_init(&pdialog->overview.present_units);
1085 
1086   /* show page */
1087   gtk_widget_show_all(page);
1088 }
1089 
1090 /****************************************************************
1091   Create map page for small screens
1092 *****************************************************************/
create_and_append_map_page(struct city_dialog * pdialog)1093 static void create_and_append_map_page(struct city_dialog *pdialog)
1094 {
1095   if (low_citydlg) {
1096     GtkWidget *page;
1097     GtkWidget *label;
1098     const char *tab_title = _("Citymap");
1099 
1100     page = gtk_grid_new();
1101     gtk_orientable_set_orientation(GTK_ORIENTABLE(page),
1102                                    GTK_ORIENTATION_VERTICAL);
1103     gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1104     label = gtk_label_new_with_mnemonic(tab_title);
1105     gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1106 
1107     create_citydlg_main_map(pdialog, page);
1108 
1109     gtk_widget_show_all(page);
1110   }
1111 }
1112 
1113 /****************************************************************
1114   Something dragged to worklist dialog.
1115 *****************************************************************/
target_drag_data_received(GtkWidget * w,GdkDragContext * context,gint x,gint y,GtkSelectionData * data,guint info,guint time,gpointer user_data)1116 static void target_drag_data_received(GtkWidget *w,
1117                                       GdkDragContext *context,
1118                                       gint x, gint y,
1119                                       GtkSelectionData *data,
1120                                       guint info, guint time,
1121                                       gpointer user_data)
1122 {
1123   struct city_dialog *pdialog = (struct city_dialog *) user_data;
1124   GtkTreeModel *model;
1125   GtkTreePath *path;
1126 
1127   if (NULL != client.conn.playing
1128       && city_owner(pdialog->pcity) != client.conn.playing) {
1129     gtk_drag_finish(context, FALSE, FALSE, time);
1130   }
1131 
1132   if (gtk_tree_get_row_drag_data(data, &model, &path)) {
1133     GtkTreeIter it;
1134 
1135     if (gtk_tree_model_get_iter(model, &it, path)) {
1136       cid id;
1137       struct universal univ;
1138 
1139       gtk_tree_model_get(model, &it, 0, &id, -1);
1140       univ = cid_production(id);
1141       city_change_production(pdialog->pcity, &univ);
1142       gtk_drag_finish(context, TRUE, FALSE, time);
1143     }
1144     gtk_tree_path_free(path);
1145   }
1146 
1147   gtk_drag_finish(context, FALSE, FALSE, time);
1148 }
1149 
1150 /****************************************************************
1151   Create production page header - what tab this actually is,
1152   depends on screen size and layout.
1153 *****************************************************************/
create_production_header(struct city_dialog * pdialog,GtkContainer * contain)1154 static void create_production_header(struct city_dialog *pdialog, GtkContainer *contain)
1155 {
1156   GtkWidget *hbox, *bar;
1157 
1158   hbox = gtk_grid_new();
1159   g_object_set(hbox, "margin", 2, NULL);
1160   gtk_grid_set_column_spacing(GTK_GRID(hbox), 10);
1161   gtk_container_add(contain, hbox);
1162 
1163   /* The label is set in city_dialog_update_building() */
1164   bar = gtk_progress_bar_new();
1165   gtk_widget_set_hexpand(bar, TRUE);
1166   gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(bar), TRUE);
1167   pdialog->production.production_bar = bar;
1168   gtk_container_add(GTK_CONTAINER(hbox), bar);
1169   gtk_progress_bar_set_text(GTK_PROGRESS_BAR(bar), _("%d/%d %d turns"));
1170 
1171   add_worklist_dnd_target(bar);
1172 
1173   g_signal_connect(bar, "drag_data_received",
1174                    G_CALLBACK(target_drag_data_received), pdialog);
1175 
1176   pdialog->production.buy_command = gtk_stockbutton_new(GTK_STOCK_EXECUTE,
1177                                                         _("_Buy"));
1178   gtk_container_add(GTK_CONTAINER(hbox), pdialog->production.buy_command);
1179 
1180   g_signal_connect(pdialog->production.buy_command, "clicked",
1181                    G_CALLBACK(buy_callback), pdialog);
1182 }
1183 
1184 /****************************************************************
1185   Create buildings list page for small screens
1186 *****************************************************************/
create_and_append_buildings_page(struct city_dialog * pdialog)1187 static void create_and_append_buildings_page(struct city_dialog *pdialog)
1188 {
1189   if (low_citydlg) {
1190     GtkWidget *page;
1191     GtkWidget *label;
1192     GtkWidget *vbox;
1193     GtkWidget *view;
1194     const char *tab_title = _("Buildings");
1195 
1196     page = gtk_grid_new();
1197     gtk_orientable_set_orientation(GTK_ORIENTABLE(page),
1198                                    GTK_ORIENTATION_VERTICAL);
1199     gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1200     label = gtk_label_new_with_mnemonic(tab_title);
1201 
1202     create_production_header(pdialog, GTK_CONTAINER(page));
1203     gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1204 
1205     vbox = gtk_grid_new();
1206     gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox),
1207                                    GTK_ORIENTATION_VERTICAL);
1208     gtk_container_add(GTK_CONTAINER(page), vbox);
1209 
1210     view = create_citydlg_improvement_list(pdialog, vbox);
1211 
1212     gtk_container_add(GTK_CONTAINER(vbox), view);
1213 
1214     gtk_widget_show_all(page);
1215   }
1216 }
1217 
1218 /****************************************************************
1219                     **** Production Page ****
1220 *****************************************************************/
create_and_append_worklist_page(struct city_dialog * pdialog)1221 static void create_and_append_worklist_page(struct city_dialog *pdialog)
1222 {
1223   const char *tab_title = _("P_roduction");
1224   GtkWidget *label = gtk_label_new_with_mnemonic(tab_title);
1225   GtkWidget *page, *editor;
1226 
1227   page = gtk_grid_new();
1228   gtk_orientable_set_orientation(GTK_ORIENTABLE(page),
1229                                  GTK_ORIENTATION_VERTICAL);
1230   gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1231   gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1232 
1233   /* stuff that's being currently built */
1234   if (!low_citydlg) {
1235     label = g_object_new(GTK_TYPE_LABEL,
1236                          "label", _("Production:"),
1237                          "xalign", 0.0, "yalign", 0.5, NULL);
1238     pdialog->production.production_label = label;
1239     gtk_container_add(GTK_CONTAINER(page), label);
1240 
1241     create_production_header(pdialog, GTK_CONTAINER(page));
1242   } else {
1243     pdialog->production.production_label = NULL;
1244   }
1245 
1246   editor = create_worklist();
1247   g_object_set(editor, "margin", 6, NULL);
1248   reset_city_worklist(editor, pdialog->pcity);
1249   gtk_container_add(GTK_CONTAINER(page), editor);
1250   pdialog->production.worklist = editor;
1251 
1252   gtk_widget_show_all(page);
1253 }
1254 
1255 /***************************************************************************
1256                      **** Happiness Page ****
1257  +- GtkWidget *page ----------+-------------------------------------------+
1258  | +- GtkWidget *left ------+ | +- GtkWidget *right --------------------+ |
1259  | | Info                   | | | City map                              | |
1260  | +- GtkWidget *citizens --+ | +- GtkWidget pdialog->happiness.widget -+ |
1261  | | Citizens data          | | | Happiness                             | |
1262  | +------------------------+ | +---------------------------------------+ |
1263  +----------------------------+-------------------------------------------+
1264 ****************************************************************************/
create_and_append_happiness_page(struct city_dialog * pdialog)1265 static void create_and_append_happiness_page(struct city_dialog *pdialog)
1266 {
1267   GtkWidget *page, *label, *table, *right, *left, *frame;
1268   const char *tab_title = _("Happ_iness");
1269 
1270   /* main page */
1271   page = gtk_grid_new();
1272   gtk_grid_set_column_spacing(GTK_GRID(page), 6);
1273   gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1274   label = gtk_label_new_with_mnemonic(tab_title);
1275   gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1276 
1277   /* left: info, citizens */
1278   left = gtk_grid_new();
1279   gtk_orientable_set_orientation(GTK_ORIENTABLE(left),
1280                                  GTK_ORIENTATION_VERTICAL);
1281   gtk_container_add(GTK_CONTAINER(page), left);
1282 
1283   if (!low_citydlg) {
1284     /* upper left: info */
1285     frame = gtk_frame_new(_("Info"));
1286     gtk_container_add(GTK_CONTAINER(left), frame);
1287 
1288     table = create_city_info_table(pdialog,
1289                                    pdialog->happiness.info_ebox,
1290                                    pdialog->happiness.info_label);
1291     gtk_widget_set_halign(table, GTK_ALIGN_CENTER);
1292     gtk_container_add(GTK_CONTAINER(frame), table);
1293   }
1294 
1295   /* lower left: citizens */
1296   if (game.info.citizen_nationality) {
1297     pdialog->happiness.citizens = gtk_grid_new();
1298     gtk_orientable_set_orientation(
1299         GTK_ORIENTABLE(pdialog->happiness.citizens),
1300         GTK_ORIENTATION_VERTICAL);
1301     gtk_container_add(GTK_CONTAINER(left), pdialog->happiness.citizens);
1302     gtk_container_add(GTK_CONTAINER(pdialog->happiness.citizens),
1303                        citizens_dialog_display(pdialog->pcity));
1304   }
1305 
1306   /* right: city map, happiness */
1307   right = gtk_grid_new();
1308   gtk_orientable_set_orientation(GTK_ORIENTABLE(right),
1309                                  GTK_ORIENTATION_VERTICAL);
1310   gtk_container_add(GTK_CONTAINER(page), right);
1311 
1312   if (!low_citydlg) {
1313     /* upper right: city map */
1314     frame = gtk_frame_new(_("City map"));
1315     gtk_widget_set_size_request(frame, CITY_MAP_MIN_SIZE_X,
1316                                 CITY_MAP_MIN_SIZE_Y);
1317     gtk_container_add(GTK_CONTAINER(right), frame);
1318 
1319     city_dialog_map_create(pdialog, &pdialog->happiness.map_canvas);
1320     gtk_container_add(GTK_CONTAINER(frame), pdialog->happiness.map_canvas.sw);
1321   }
1322 
1323   /* lower right: happiness */
1324   pdialog->happiness.widget = gtk_grid_new();
1325   gtk_orientable_set_orientation(GTK_ORIENTABLE(pdialog->happiness.widget),
1326                                  GTK_ORIENTATION_VERTICAL);
1327   gtk_container_add(GTK_CONTAINER(right), pdialog->happiness.widget);
1328   gtk_container_add(GTK_CONTAINER(pdialog->happiness.widget),
1329                     get_top_happiness_display(pdialog->pcity, low_citydlg, pdialog->shell));
1330 
1331   /* show page */
1332   gtk_widget_show_all(page);
1333 }
1334 
1335 /****************************************************************
1336             **** Citizen Management Agent (CMA) Page ****
1337 *****************************************************************/
create_and_append_cma_page(struct city_dialog * pdialog)1338 static void create_and_append_cma_page(struct city_dialog *pdialog)
1339 {
1340   GtkWidget *page, *label;
1341   const char *tab_title = _("_Governor");
1342 
1343   page = gtk_grid_new();
1344 
1345   label = gtk_label_new_with_mnemonic(tab_title);
1346 
1347   gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1348 
1349   pdialog->cma_editor = create_cma_dialog(pdialog->pcity, low_citydlg);
1350   gtk_container_add(GTK_CONTAINER(page), pdialog->cma_editor->shell);
1351 
1352   gtk_widget_show(page);
1353 }
1354 
1355 /****************************************************************
1356                     **** Misc. Settings Page ****
1357 *****************************************************************/
create_and_append_settings_page(struct city_dialog * pdialog)1358 static void create_and_append_settings_page(struct city_dialog *pdialog)
1359 {
1360   int i;
1361   GtkWidget *vbox2, *page, *frame, *label, *button;
1362   GtkSizeGroup *size;
1363   GSList *group;
1364   const char *tab_title = _("_Settings");
1365 
1366   static const char *new_citizens_label[] = {
1367     N_("Entertainers"),
1368     N_("Scientists"),
1369     N_("Taxmen")
1370   };
1371 
1372   static const char *disband_label
1373     = N_("Allow unit production to disband city");
1374 
1375   static const char *misc_whichtab_label[NUM_PAGES] = {
1376     N_("Overview page"),
1377     N_("Production page"),
1378     N_("Happiness page"),
1379     N_("Governor page"),
1380     N_("This Settings page"),
1381     N_("Last active page")
1382   };
1383 
1384   static bool new_citizens_label_done;
1385   static bool misc_whichtab_label_done;
1386 
1387   /* initialize signal_blocker */
1388   pdialog->misc.block_signal = 0;
1389 
1390 
1391   page = gtk_grid_new();
1392   gtk_grid_set_column_spacing(GTK_GRID(page), 18);
1393   gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1394 
1395   size = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
1396 
1397   label = gtk_label_new_with_mnemonic(tab_title);
1398 
1399   gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1400 
1401   /* new_citizens radio */
1402   frame = gtk_frame_new(_("New citizens are"));
1403   gtk_grid_attach(GTK_GRID(page), frame, 0, 0, 1, 1);
1404   gtk_size_group_add_widget(size, frame);
1405 
1406   vbox2 = gtk_grid_new();
1407   gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2),
1408                                  GTK_ORIENTATION_VERTICAL);
1409   gtk_container_add(GTK_CONTAINER(frame), vbox2);
1410 
1411   intl_slist(ARRAY_SIZE(new_citizens_label), new_citizens_label,
1412              &new_citizens_label_done);
1413 
1414   group = NULL;
1415   for (i = 0; i < ARRAY_SIZE(new_citizens_label); i++) {
1416     button = gtk_radio_button_new_with_mnemonic(group, new_citizens_label[i]);
1417     pdialog->misc.new_citizens_radio[i] = button;
1418     gtk_container_add(GTK_CONTAINER(vbox2), button);
1419     g_signal_connect(button, "toggled",
1420 		     G_CALLBACK(cityopt_callback), pdialog);
1421     group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
1422   }
1423 
1424   /* next is the next-time-open radio group in the right column */
1425   frame = gtk_frame_new(_("Next time open"));
1426   gtk_grid_attach(GTK_GRID(page), frame, 1, 0, 1, 1);
1427   gtk_size_group_add_widget(size, frame);
1428 
1429   vbox2 = gtk_grid_new();
1430   gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2),
1431                                  GTK_ORIENTATION_VERTICAL);
1432   gtk_container_add(GTK_CONTAINER(frame), vbox2);
1433 
1434   intl_slist(ARRAY_SIZE(misc_whichtab_label), misc_whichtab_label,
1435              &misc_whichtab_label_done);
1436 
1437   group = NULL;
1438   for (i = 0; i < ARRAY_SIZE(misc_whichtab_label); i++) {
1439     button = gtk_radio_button_new_with_mnemonic(group, misc_whichtab_label[i]);
1440     pdialog->misc.whichtab_radio[i] = button;
1441     gtk_container_add(GTK_CONTAINER(vbox2), button);
1442     g_signal_connect(button, "toggled",
1443 		     G_CALLBACK(misc_whichtab_callback), GINT_TO_POINTER(i));
1444     group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
1445   }
1446 
1447   /* now we go back and fill the hbox rename */
1448   frame = gtk_frame_new(_("City"));
1449   gtk_widget_set_margin_top(frame, 12);
1450   gtk_widget_set_margin_bottom(frame, 12);
1451   gtk_grid_attach(GTK_GRID(page), frame, 0, 1, 1, 1);
1452 
1453   vbox2 = gtk_grid_new();
1454   gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2),
1455                                  GTK_ORIENTATION_VERTICAL);
1456   gtk_container_add(GTK_CONTAINER(frame), vbox2);
1457 
1458   button = gtk_button_new_with_mnemonic(_("R_ename..."));
1459   pdialog->misc.rename_command = button;
1460   gtk_container_add(GTK_CONTAINER(vbox2), button);
1461   g_signal_connect(button, "clicked",
1462 		   G_CALLBACK(rename_callback), pdialog);
1463 
1464   gtk_widget_set_sensitive(button, can_client_issue_orders());
1465 
1466   /* the disband-city-on-unit-production button */
1467   button = gtk_check_button_new_with_mnemonic(_(disband_label));
1468   pdialog->misc.disband_on_settler = button;
1469   gtk_container_add(GTK_CONTAINER(vbox2), button);
1470   g_signal_connect(button, "toggled",
1471 		   G_CALLBACK(cityopt_callback), pdialog);
1472 
1473   /* we choose which page to popup by default */
1474   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
1475 			       (pdialog->
1476 				misc.whichtab_radio[new_dialog_def_page]),
1477 			       TRUE);
1478 
1479   set_cityopt_values(pdialog);
1480 
1481   gtk_widget_show_all(page);
1482 
1483   if (new_dialog_def_page == (NUM_PAGES - 1)) {
1484     gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog->notebook),
1485 				  last_page);
1486   } else {
1487     gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog->notebook),
1488 				  new_dialog_def_page);
1489   }
1490 }
1491 
1492 
1493 
1494 
1495 /****************************************************************
1496                      **** Main City Dialog ****
1497  +----------------------------+-------------------------------+
1498  | GtkWidget *top: Citizens   | city name                     |
1499  +----------------------------+-------------------------------+
1500  | <notebook tab>                                             |
1501  +------------------------------------------------------------+
1502 *****************************************************************/
create_city_dialog(struct city * pcity)1503 static struct city_dialog *create_city_dialog(struct city *pcity)
1504 {
1505   struct city_dialog *pdialog;
1506   GtkWidget *close_command;
1507   GtkWidget *vbox, *hbox, *cbox, *ebox;
1508   int citizen_bar_width;
1509   int citizen_bar_height;
1510   GdkPixbuf *pb;
1511 
1512   if (!city_dialogs_have_been_initialised) {
1513     initialize_city_dialogs();
1514   }
1515 
1516   pdialog = fc_malloc(sizeof(struct city_dialog));
1517   pdialog->pcity = pcity;
1518   pdialog->buy_shell = NULL;
1519   pdialog->sell_shell = NULL;
1520   pdialog->rename_shell = NULL;
1521   pdialog->happiness.map_canvas.sw = NULL;      /* make sure NULL if spy */
1522   pdialog->happiness.map_canvas.ebox = NULL;    /* ditto */
1523   pdialog->happiness.map_canvas.darea = NULL;   /* ditto */
1524   pdialog->happiness.citizens = NULL;           /* ditto */
1525   pdialog->cma_editor = NULL;
1526   pdialog->map_canvas_store_unscaled
1527     = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1528             canvas_width, canvas_height);
1529 
1530   pdialog->shell = gtk_dialog_new();
1531   gtk_window_set_title(GTK_WINDOW(pdialog->shell), city_name_get(pcity));
1532   setup_dialog(pdialog->shell, toplevel);
1533   gtk_window_set_role(GTK_WINDOW(pdialog->shell), "city");
1534 
1535   g_signal_connect(pdialog->shell, "destroy",
1536 		   G_CALLBACK(city_destroy_callback), pdialog);
1537   gtk_window_set_position(GTK_WINDOW(pdialog->shell), GTK_WIN_POS_MOUSE);
1538   gtk_widget_set_name(pdialog->shell, "Freeciv");
1539 
1540   gtk_widget_realize(pdialog->shell);
1541 
1542   /* keep the icon of the executable on Windows (see PR#36491) */
1543 #ifndef FREECIV_MSWINDOWS
1544   {
1545     GdkPixbuf *pixbuf = sprite_get_pixbuf(get_icon_sprite(tileset, ICON_CITYDLG));
1546 
1547     /* Only call this after tileset_load_tiles is called. */
1548     gtk_window_set_icon(GTK_WINDOW(pdialog->shell), pixbuf);
1549     g_object_unref(pixbuf);
1550   }
1551 #endif /* FREECIV_MSWINDOWS */
1552 
1553   /* Restore size of the city dialog. */
1554   gtk_window_set_default_size(GTK_WINDOW(pdialog->shell),
1555                               gui_options.gui_gtk3_citydlg_xsize,
1556                               gui_options.gui_gtk3_citydlg_ysize);
1557 
1558   pdialog->popup_menu = gtk_menu_new();
1559 
1560   vbox = gtk_dialog_get_content_area(GTK_DIALOG(pdialog->shell));
1561   hbox = gtk_grid_new();
1562   gtk_grid_set_column_homogeneous(GTK_GRID(hbox), TRUE);
1563   gtk_container_add(GTK_CONTAINER(vbox), hbox);
1564 
1565   /**** Citizens bar here ****/
1566   cbox = gtk_grid_new();
1567   gtk_container_add(GTK_CONTAINER(hbox), cbox);
1568 
1569   ebox = gtk_event_box_new();
1570   gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
1571   gtk_container_add(GTK_CONTAINER(cbox), ebox);
1572 
1573   citizen_bar_width = tileset_small_sprite_width(tileset) * NUM_CITIZENS_SHOWN;
1574   citizen_bar_height = tileset_small_sprite_height(tileset);
1575 
1576   pdialog->citizen_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1577                                                         citizen_bar_width, citizen_bar_height);
1578   pb = surface_get_pixbuf(pdialog->citizen_surface, citizen_bar_width, citizen_bar_height);
1579   pdialog->citizen_images = gtk_image_new_from_pixbuf(pb);
1580   g_object_unref(pb);
1581 
1582   gtk_widget_add_events(pdialog->citizen_images, GDK_BUTTON_PRESS_MASK);
1583   gtk_widget_set_margin_left(pdialog->citizen_images, 2);
1584   gtk_widget_set_margin_right(pdialog->citizen_images, 2);
1585   gtk_widget_set_margin_top(pdialog->citizen_images, 2);
1586   gtk_widget_set_margin_bottom(pdialog->citizen_images, 2);
1587   gtk_widget_set_halign(pdialog->citizen_images, GTK_ALIGN_START);
1588   gtk_widget_set_valign(pdialog->citizen_images, GTK_ALIGN_CENTER);
1589   gtk_container_add(GTK_CONTAINER(ebox), pdialog->citizen_images);
1590   g_signal_connect(G_OBJECT(ebox), "button-press-event",
1591                    G_CALLBACK(citizens_callback), pdialog);
1592 
1593   /**** City name label here ****/
1594   pdialog->name_label = gtk_label_new(NULL);
1595   gtk_widget_set_hexpand(pdialog->name_label, TRUE);
1596   gtk_widget_set_halign(pdialog->name_label, GTK_ALIGN_START);
1597   gtk_widget_set_valign(pdialog->name_label, GTK_ALIGN_CENTER);
1598   gtk_container_add(GTK_CONTAINER(hbox), pdialog->name_label);
1599 
1600   /**** -Start of Notebook- ****/
1601 
1602   pdialog->notebook = gtk_notebook_new();
1603   gtk_notebook_set_tab_pos(GTK_NOTEBOOK(pdialog->notebook),
1604 			   GTK_POS_BOTTOM);
1605   gtk_container_add(GTK_CONTAINER(vbox), pdialog->notebook);
1606 
1607   create_and_append_overview_page(pdialog);
1608   create_and_append_map_page(pdialog);
1609   create_and_append_buildings_page(pdialog);
1610   create_and_append_worklist_page(pdialog);
1611 
1612   /* only create these tabs if not a spy */
1613   if (!client_has_player() || city_owner(pcity) == client_player()) {
1614     create_and_append_happiness_page(pdialog);
1615   }
1616 
1617   if (city_owner(pcity) == client_player()
1618       && !client_is_observer()) {
1619     create_and_append_cma_page(pdialog);
1620     create_and_append_settings_page(pdialog);
1621   } else {
1622     gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog->notebook),
1623                                   OVERVIEW_PAGE);
1624   }
1625 
1626   /**** End of Notebook ****/
1627 
1628   /* bottom buttons */
1629 
1630   pdialog->show_units_command =
1631     gtk_dialog_add_button(GTK_DIALOG(pdialog->shell), _("_List present units..."), CDLGR_UNITS);
1632 
1633   g_signal_connect(GTK_DIALOG(pdialog->shell), "response",
1634                    G_CALLBACK(citydlg_response_callback), pdialog);
1635 
1636   pdialog->prev_command = gtk_stockbutton_new(GTK_STOCK_GO_BACK,
1637                                               _("_Prev city"));
1638   gtk_dialog_add_action_widget(GTK_DIALOG(pdialog->shell),
1639                                pdialog->prev_command, 1);
1640 
1641   pdialog->next_command = gtk_stockbutton_new(GTK_STOCK_GO_FORWARD,
1642                                               _("_Next city"));
1643   gtk_dialog_add_action_widget(GTK_DIALOG(pdialog->shell),
1644                                pdialog->next_command, 2);
1645 
1646   if (city_owner(pcity) != client.conn.playing) {
1647     gtk_widget_set_sensitive(pdialog->prev_command, FALSE);
1648     gtk_widget_set_sensitive(pdialog->next_command, FALSE);
1649   }
1650 
1651   close_command = gtk_dialog_add_button(GTK_DIALOG(pdialog->shell),
1652 					GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
1653 
1654   gtk_dialog_set_default_response(GTK_DIALOG(pdialog->shell),
1655 	GTK_RESPONSE_CLOSE);
1656 
1657   g_signal_connect(close_command, "clicked",
1658 		   G_CALLBACK(close_callback), pdialog);
1659 
1660   g_signal_connect(pdialog->prev_command, "clicked",
1661                    G_CALLBACK(switch_city_callback), pdialog);
1662 
1663   g_signal_connect(pdialog->next_command, "clicked",
1664                    G_CALLBACK(switch_city_callback), pdialog);
1665 
1666   /* some other things we gotta do */
1667 
1668   g_signal_connect(pdialog->shell, "key_press_event",
1669 		   G_CALLBACK(keyboard_handler), pdialog);
1670 
1671   dialog_list_prepend(dialog_list, pdialog);
1672 
1673   real_city_dialog_refresh(pdialog->pcity);
1674 
1675   /* need to do this every time a new dialog is opened. */
1676   city_dialog_update_prev_next();
1677 
1678   gtk_widget_show_all(pdialog->shell);
1679 
1680   gtk_window_set_focus(GTK_WINDOW(pdialog->shell), close_command);
1681 
1682   return pdialog;
1683 }
1684 
1685 /*********** Functions to update parts of the dialog ************/
1686 /****************************************************************
1687   Update title of city dialog.
1688 *****************************************************************/
city_dialog_update_title(struct city_dialog * pdialog)1689 static void city_dialog_update_title(struct city_dialog *pdialog)
1690 {
1691   gchar *buf;
1692   const gchar *now;
1693 
1694   if (city_unhappy(pdialog->pcity)) {
1695     /* TRANS: city dialog title */
1696     buf = g_strdup_printf(_("<b>%s</b> - %s citizens - DISORDER"),
1697                           city_name_get(pdialog->pcity),
1698                           population_to_text(city_population(pdialog->pcity)));
1699   } else if (city_celebrating(pdialog->pcity)) {
1700     /* TRANS: city dialog title */
1701     buf = g_strdup_printf(_("<b>%s</b> - %s citizens - celebrating"),
1702                           city_name_get(pdialog->pcity),
1703                           population_to_text(city_population(pdialog->pcity)));
1704   } else if (city_happy(pdialog->pcity)) {
1705     /* TRANS: city dialog title */
1706     buf = g_strdup_printf(_("<b>%s</b> - %s citizens - happy"),
1707                           city_name_get(pdialog->pcity),
1708                           population_to_text(city_population(pdialog->pcity)));
1709   } else {
1710     /* TRANS: city dialog title */
1711     buf = g_strdup_printf(_("<b>%s</b> - %s citizens"),
1712                           city_name_get(pdialog->pcity),
1713                           population_to_text(city_population(pdialog->pcity)));
1714   }
1715 
1716   now = gtk_label_get_text(GTK_LABEL(pdialog->name_label));
1717   if (strcmp(now, buf) != 0) {
1718     gtk_window_set_title(GTK_WINDOW(pdialog->shell), city_name_get(pdialog->pcity));
1719     gtk_label_set_markup(GTK_LABEL(pdialog->name_label), buf);
1720   }
1721 
1722   g_free(buf);
1723 }
1724 
1725 /****************************************************************
1726   Update citizens in city dialog
1727 *****************************************************************/
city_dialog_update_citizens(struct city_dialog * pdialog)1728 static void city_dialog_update_citizens(struct city_dialog *pdialog)
1729 {
1730   enum citizen_category categories[MAX_CITY_SIZE];
1731   int i, width;
1732   int citizen_bar_width;
1733   int citizen_bar_height;
1734   struct city *pcity = pdialog->pcity;
1735   int num_citizens = get_city_citizen_types(pcity, FEELING_FINAL, categories);
1736   GdkPixbuf *pb;
1737   cairo_t *cr;
1738 
1739   /* If there is not enough space we stack the icons. We draw from left to */
1740   /* right. width is how far we go to the right for each drawn pixmap. The */
1741   /* last icon is always drawn in full, and so has reserved                */
1742   /* tileset_small_sprite_width(tileset) pixels.                           */
1743 
1744   if (num_citizens > 1) {
1745     width = MIN(tileset_small_sprite_width(tileset),
1746 		((NUM_CITIZENS_SHOWN - 1) * tileset_small_sprite_width(tileset)) /
1747 		(num_citizens - 1));
1748   } else {
1749     width = tileset_small_sprite_width(tileset);
1750   }
1751   pdialog->cwidth = width;
1752 
1753   /* overview page */
1754   citizen_bar_width = (num_citizens - 1) * width + tileset_small_sprite_width(tileset) + 2;
1755   citizen_bar_height = tileset_small_sprite_height(tileset);
1756 
1757   cr = cairo_create(pdialog->citizen_surface);
1758 
1759   for (i = 0; i < num_citizens; i++) {
1760     cairo_set_source_surface(cr,
1761                              get_citizen_sprite(tileset, categories[i], i, pcity)->surface,
1762                              i * width, 0);
1763     cairo_rectangle(cr, i * width, 0, width, citizen_bar_height);
1764     cairo_fill(cr);
1765   }
1766 
1767   cairo_destroy(cr);
1768 
1769   pb = surface_get_pixbuf(pdialog->citizen_surface, citizen_bar_width, citizen_bar_height);
1770   gtk_image_set_from_pixbuf(GTK_IMAGE(pdialog->citizen_images), pb);
1771   g_object_unref(pb);
1772 }
1773 
1774 /****************************************************************
1775   Update textual info fields in city dialog
1776 *****************************************************************/
city_dialog_update_information(GtkWidget ** info_ebox,GtkWidget ** info_label,struct city_dialog * pdialog)1777 static void city_dialog_update_information(GtkWidget **info_ebox,
1778 					   GtkWidget **info_label,
1779                                            struct city_dialog *pdialog)
1780 {
1781   int i, illness = 0;
1782   char buf[NUM_INFO_FIELDS][512];
1783   struct city *pcity = pdialog->pcity;
1784   int granaryturns;
1785   int non_workers = city_specialists(pcity);
1786   GdkRGBA red = {1.0, 0, 0, 1.0};
1787   GdkRGBA *color;
1788 
1789 
1790   /* fill the buffers with the necessary info */
1791   if (non_workers) {
1792     fc_snprintf(buf[INFO_SIZE], sizeof(buf[INFO_SIZE]), "%3d (%3d)",
1793                 pcity->size, non_workers);
1794   } else {
1795     fc_snprintf(buf[INFO_SIZE], sizeof(buf[INFO_SIZE]), "%3d", pcity->size);
1796   }
1797   fc_snprintf(buf[INFO_FOOD], sizeof(buf[INFO_FOOD]), "%3d (%+4d)",
1798               pcity->prod[O_FOOD], pcity->surplus[O_FOOD]);
1799   fc_snprintf(buf[INFO_SHIELD], sizeof(buf[INFO_SHIELD]), "%3d (%+4d)",
1800               pcity->prod[O_SHIELD] + pcity->waste[O_SHIELD],
1801               pcity->surplus[O_SHIELD]);
1802   fc_snprintf(buf[INFO_TRADE], sizeof(buf[INFO_TRADE]), "%3d (%+4d)",
1803               pcity->surplus[O_TRADE] + pcity->waste[O_TRADE],
1804               pcity->surplus[O_TRADE]);
1805   fc_snprintf(buf[INFO_GOLD], sizeof(buf[INFO_GOLD]), "%3d (%+4d)",
1806               pcity->prod[O_GOLD], pcity->surplus[O_GOLD]);
1807   fc_snprintf(buf[INFO_LUXURY], sizeof(buf[INFO_LUXURY]), "%3d",
1808               pcity->prod[O_LUXURY]);
1809   fc_snprintf(buf[INFO_SCIENCE], sizeof(buf[INFO_SCIENCE]), "%3d",
1810               pcity->prod[O_SCIENCE]);
1811   fc_snprintf(buf[INFO_GRANARY], sizeof(buf[INFO_GRANARY]), "%4d/%-4d",
1812               pcity->food_stock, city_granary_size(city_size_get(pcity)));
1813 
1814   granaryturns = city_turns_to_grow(pcity);
1815   if (granaryturns == 0) {
1816     /* TRANS: city growth is blocked.  Keep short. */
1817     fc_snprintf(buf[INFO_GROWTH], sizeof(buf[INFO_GROWTH]), _("blocked"));
1818   } else if (granaryturns == FC_INFINITY) {
1819     /* TRANS: city is not growing.  Keep short. */
1820     fc_snprintf(buf[INFO_GROWTH], sizeof(buf[INFO_GROWTH]), _("never"));
1821   } else {
1822     /* A negative value means we'll have famine in that many turns.
1823        But that's handled down below. */
1824     /* TRANS: city growth turns.  Keep short. */
1825     fc_snprintf(buf[INFO_GROWTH], sizeof(buf[INFO_GROWTH]),
1826                 PL_("%d turn", "%d turns", abs(granaryturns)),
1827                 abs(granaryturns));
1828   }
1829   fc_snprintf(buf[INFO_CORRUPTION], sizeof(buf[INFO_CORRUPTION]), "%4d",
1830               pcity->waste[O_TRADE]);
1831   fc_snprintf(buf[INFO_WASTE], sizeof(buf[INFO_WASTE]), "%4d",
1832               pcity->waste[O_SHIELD]);
1833   fc_snprintf(buf[INFO_CULTURE], sizeof(buf[INFO_CULTURE]), "%4d",
1834               pcity->client.culture);
1835   fc_snprintf(buf[INFO_POLLUTION], sizeof(buf[INFO_POLLUTION]), "%4d",
1836               pcity->pollution);
1837   if (!game.info.illness_on) {
1838     fc_snprintf(buf[INFO_ILLNESS], sizeof(buf[INFO_ILLNESS]), "  -.-");
1839   } else {
1840     illness = city_illness_calc(pcity, NULL, NULL, NULL, NULL);
1841     /* illness is in tenth of percent */
1842     fc_snprintf(buf[INFO_ILLNESS], sizeof(buf[INFO_ILLNESS]), "%5.1f%%",
1843                 (float)illness / 10.0);
1844   }
1845 
1846   get_city_dialog_airlift_value(pcity, buf[INFO_AIRLIFT],
1847                                 sizeof(buf[INFO_AIRLIFT]));
1848 
1849   /* stick 'em in the labels */
1850   for (i = 0; i < NUM_INFO_FIELDS; i++) {
1851     gtk_label_set_text(GTK_LABEL(info_label[i]), buf[i]);
1852   }
1853 
1854   /*
1855    * Special style stuff for granary, growth, pollution, and plague below.
1856    * For starvation, the "4" below is arbitrary. 3 turns should be enough
1857    * of a warning.
1858    */
1859   color = (granaryturns > -4 && granaryturns < 0) ? &red : NULL;
1860   gtk_widget_override_color(info_label[INFO_GRANARY], GTK_STATE_FLAG_NORMAL, color);
1861 
1862   color = (granaryturns == 0 || pcity->surplus[O_FOOD] < 0) ? &red : NULL;
1863   gtk_widget_override_color(info_label[INFO_GROWTH], GTK_STATE_FLAG_NORMAL, color);
1864 
1865   /* someone could add the color &orange for better granularity here */
1866 
1867   color = (pcity->pollution >= 10) ? &red : NULL;
1868   gtk_widget_override_color(info_label[INFO_POLLUTION], GTK_STATE_FLAG_NORMAL, color);
1869 
1870   /* illness is in tenth of percent, i.e 100 == 10.0% */
1871   color = (illness >= 100) ? &red : NULL;
1872   gtk_widget_override_color(info_label[INFO_ILLNESS], GTK_STATE_FLAG_NORMAL, color);
1873 }
1874 
1875 /****************************************************************
1876   Update map display of city dialog
1877 *****************************************************************/
city_dialog_update_map(struct city_dialog * pdialog)1878 static void city_dialog_update_map(struct city_dialog *pdialog)
1879 {
1880   struct canvas store = FC_STATIC_CANVAS_INIT;
1881 
1882   store.surface = pdialog->map_canvas_store_unscaled;
1883 
1884   /* The drawing is done in three steps.
1885    *   1.  First we render to a pixmap with the appropriate canvas size.
1886    *   2.  Then the pixmap is rendered into a pixbuf of equal size.
1887    *   3.  Finally this pixbuf is composited and scaled onto the GtkImage's
1888    *       target pixbuf.
1889    */
1890 
1891   city_dialog_redraw_map(pdialog->pcity, &store);
1892 
1893   /* draw to real window */
1894   draw_map_canvas(pdialog);
1895 
1896   if (cma_is_city_under_agent(pdialog->pcity, NULL)) {
1897     gtk_widget_set_sensitive(pdialog->overview.map_canvas.ebox, FALSE);
1898     if (pdialog->happiness.map_canvas.ebox) {
1899       gtk_widget_set_sensitive(pdialog->happiness.map_canvas.ebox, FALSE);
1900     }
1901   } else {
1902     gtk_widget_set_sensitive(pdialog->overview.map_canvas.ebox, TRUE);
1903     if (pdialog->happiness.map_canvas.ebox) {
1904       gtk_widget_set_sensitive(pdialog->happiness.map_canvas.ebox, TRUE);
1905     }
1906   }
1907 }
1908 
1909 /****************************************************************
1910   Update what city is building and buy cost in city dialog
1911 *****************************************************************/
city_dialog_update_building(struct city_dialog * pdialog)1912 static void city_dialog_update_building(struct city_dialog *pdialog)
1913 {
1914   char buf[32], buf2[200];
1915   gdouble pct;
1916 
1917   GtkListStore* store;
1918   GtkTreeIter iter;
1919   struct universal targets[MAX_NUM_PRODUCTION_TARGETS];
1920   struct item items[MAX_NUM_PRODUCTION_TARGETS];
1921   int targets_used, item;
1922   struct city *pcity = pdialog->pcity;
1923   gboolean sensitive = city_can_buy(pcity);
1924   const char *descr = city_production_name_translation(pcity);
1925   int cost = city_production_build_shield_cost(pcity);
1926 
1927   if (pdialog->overview.buy_command != NULL) {
1928     gtk_widget_set_sensitive(pdialog->overview.buy_command, sensitive);
1929   }
1930   if (pdialog->production.buy_command != NULL) {
1931     gtk_widget_set_sensitive(pdialog->production.buy_command, sensitive);
1932   }
1933 
1934   /* Make sure build slots info is up to date */
1935   if (pdialog->production.production_label != NULL) {
1936     int build_slots = city_build_slots(pcity);
1937 
1938     /* Only display extra info if more than one slot is available */
1939     if (build_slots > 1) {
1940       fc_snprintf(buf2, sizeof(buf2),
1941                   /* TRANS: never actually used with built_slots <= 1 */
1942                   PL_("Production (up to %d unit per turn):",
1943                       "Production (up to %d units per turn):", build_slots),
1944                   build_slots);
1945       gtk_label_set_text(
1946         GTK_LABEL(pdialog->production.production_label), buf2);
1947     } else {
1948       gtk_label_set_text(
1949         GTK_LABEL(pdialog->production.production_label), _("Production:"));
1950     }
1951   }
1952 
1953   /* Update what the city is working on */
1954   get_city_dialog_production(pcity, buf, sizeof(buf));
1955 
1956   if (cost > 0) {
1957     pct = (gdouble) pcity->shield_stock / (gdouble) cost;
1958     pct = CLAMP(pct, 0.0, 1.0);
1959   } else {
1960     pct = 1.0;
1961   }
1962 
1963   if (pdialog->overview.production_bar != NULL) {
1964     fc_snprintf(buf2, sizeof(buf2), "%s%s\n%s", descr,
1965                 worklist_is_empty(&pcity->worklist) ? "" : " (+)", buf);
1966     gtk_progress_bar_set_text(
1967       GTK_PROGRESS_BAR(pdialog->overview.production_bar), buf2);
1968     gtk_progress_bar_set_fraction(
1969       GTK_PROGRESS_BAR(pdialog->overview.production_bar), pct);
1970   }
1971 
1972   if (pdialog->production.production_bar != NULL) {
1973     fc_snprintf(buf2, sizeof(buf2), "%s%s: %s", descr,
1974                 worklist_is_empty(&pcity->worklist) ? "" : " (+)", buf);
1975     gtk_progress_bar_set_text(
1976       GTK_PROGRESS_BAR(pdialog->production.production_bar), buf2);
1977     gtk_progress_bar_set_fraction(
1978       GTK_PROGRESS_BAR(pdialog->production.production_bar), pct);
1979   }
1980 
1981   if (pdialog->overview.production_combo != NULL) {
1982     gtk_combo_box_set_active(GTK_COMBO_BOX(pdialog->overview.production_combo),
1983                              -1);
1984   }
1985 
1986   store = pdialog->overview.change_production_store;
1987   if (store != NULL) {
1988     gtk_list_store_clear(pdialog->overview.change_production_store);
1989 
1990     targets_used
1991       = collect_eventually_buildable_targets(targets, pdialog->pcity, FALSE);
1992     name_and_sort_items(targets, targets_used, items, FALSE, pcity);
1993 
1994     for (item = 0; item < targets_used; item++) {
1995       if (can_city_build_now(pcity, &items[item].item)) {
1996         const char* name;
1997         struct sprite* sprite;
1998         GdkPixbuf *pix;
1999         struct universal target = items[item].item;
2000         bool useless;
2001 
2002         if (VUT_UTYPE == target.kind) {
2003           name = utype_name_translation(target.value.utype);
2004           sprite = get_unittype_sprite(tileset, target.value.utype,
2005                                        direction8_invalid(), TRUE);
2006           useless = FALSE;
2007         } else {
2008           name = improvement_name_translation(target.value.building);
2009           sprite = get_building_sprite(tileset, target.value.building);
2010           useless = is_improvement_redundant(pcity, target.value.building);
2011         }
2012         pix = sprite_get_pixbuf(sprite);
2013         gtk_list_store_append(store, &iter);
2014         gtk_list_store_set(store, &iter, 0, pix,
2015                            1, name, 3, useless,
2016                            2, (gint)cid_encode(items[item].item),-1);
2017         g_object_unref(G_OBJECT(pix));
2018       }
2019     }
2020   }
2021 
2022   /* work around GTK+ refresh bug. */
2023   if (pdialog->overview.production_bar != NULL) {
2024     gtk_widget_queue_resize(pdialog->overview.production_bar);
2025   }
2026   if (pdialog->production.production_bar != NULL) {
2027     gtk_widget_queue_resize(pdialog->production.production_bar);
2028   }
2029 }
2030 
2031 /****************************************************************
2032   Update list of improvements in city dialog
2033 *****************************************************************/
city_dialog_update_improvement_list(struct city_dialog * pdialog)2034 static void city_dialog_update_improvement_list(struct city_dialog *pdialog)
2035 {
2036   int item, targets_used;
2037   struct universal targets[MAX_NUM_PRODUCTION_TARGETS];
2038   struct item items[MAX_NUM_PRODUCTION_TARGETS];
2039   GtkTreeModel *model;
2040   GtkListStore *store;
2041 
2042   const char *tooltip_sellable = _("Press <b>ENTER</b> or double-click to "
2043                                    "sell an improvement.");
2044   const char *tooltip_great_wonder = _("Great Wonder - cannot be sold.");
2045   const char *tooltip_small_wonder = _("Small Wonder - cannot be sold.");
2046 
2047   model =
2048     gtk_tree_view_get_model(GTK_TREE_VIEW(pdialog->overview.improvement_list));
2049   store = GTK_LIST_STORE(model);
2050 
2051   targets_used = collect_already_built_targets(targets, pdialog->pcity);
2052   name_and_sort_items(targets, targets_used, items, FALSE, pdialog->pcity);
2053 
2054   gtk_list_store_clear(store);
2055 
2056   for (item = 0; item < targets_used; item++) {
2057     GdkPixbuf *pix;
2058     GtkTreeIter it;
2059     int upkeep;
2060     struct sprite *sprite;
2061     struct universal target = items[item].item;
2062 
2063     fc_assert_action(VUT_IMPROVEMENT == target.kind, continue);
2064     /* This takes effects (like Adam Smith's) into account. */
2065     upkeep = city_improvement_upkeep(pdialog->pcity, target.value.building);
2066     sprite = get_building_sprite(tileset, target.value.building);
2067 
2068     pix = sprite_get_pixbuf(sprite);
2069     gtk_list_store_append(store, &it);
2070     gtk_list_store_set(store, &it,
2071                        0, target.value.building,
2072                        1, pix,
2073                        2, items[item].descr,
2074                        3, upkeep,
2075                        4,
2076                        is_improvement_redundant(pdialog->pcity,
2077                                                 target.value.building),
2078                        5,
2079                        is_great_wonder(target.value.building) ?
2080                          tooltip_great_wonder :
2081                            (is_small_wonder(target.value.building) ?
2082                              tooltip_small_wonder : tooltip_sellable),
2083                        -1);
2084     g_object_unref(G_OBJECT(pix));
2085   }
2086 }
2087 
2088 /****************************************************************
2089   Update list of supported units in city dialog
2090 *****************************************************************/
city_dialog_update_supported_units(struct city_dialog * pdialog)2091 static void city_dialog_update_supported_units(struct city_dialog *pdialog)
2092 {
2093   struct unit_list *units;
2094   struct unit_node_vector *nodes;
2095   int n, m, i;
2096   gchar *buf;
2097   int free_unhappy = get_city_bonus(pdialog->pcity, EFT_MAKE_CONTENT_MIL);
2098 
2099   if (NULL != client.conn.playing
2100       && city_owner(pdialog->pcity) != client.conn.playing) {
2101     units = pdialog->pcity->client.info_units_supported;
2102   } else {
2103     units = pdialog->pcity->units_supported;
2104   }
2105 
2106   nodes = &pdialog->overview.supported_units;
2107 
2108   n = unit_list_size(units);
2109   m = unit_node_vector_size(nodes);
2110 
2111   if (m > n) {
2112     i = 0;
2113     unit_node_vector_iterate(nodes, elt) {
2114       if (i++ >= n) {
2115 	gtk_widget_destroy(elt->cmd);
2116       }
2117     } unit_node_vector_iterate_end;
2118 
2119     unit_node_vector_reserve(nodes, n);
2120   } else {
2121     for (i = m; i < n; i++) {
2122       GtkWidget *cmd, *pix;
2123       struct unit_node node;
2124 
2125       cmd = gtk_button_new();
2126       node.cmd = cmd;
2127 
2128       gtk_button_set_relief(GTK_BUTTON(cmd), GTK_RELIEF_NONE);
2129       gtk_widget_add_events(cmd,
2130 	  GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2131 
2132       pix = gtk_image_new();
2133       node.pix = pix;
2134       node.height = tileset_unit_with_upkeep_height(tileset);
2135 
2136       gtk_container_add(GTK_CONTAINER(cmd), pix);
2137 
2138       gtk_grid_attach(GTK_GRID(pdialog->overview.supported_unit_table),
2139                       cmd, i, 0, 1, 1);
2140       unit_node_vector_append(nodes, node);
2141     }
2142   }
2143 
2144   i = 0;
2145   unit_list_iterate(units, punit) {
2146     struct unit_node *pnode;
2147     int happy_cost = city_unit_unhappiness(punit, &free_unhappy);
2148 
2149     pnode = unit_node_vector_get(nodes, i);
2150     if (pnode) {
2151       GtkWidget *cmd, *pix;
2152 
2153       cmd = pnode->cmd;
2154       pix = pnode->pix;
2155 
2156       put_unit_image_city_overlays(punit, GTK_IMAGE(pix), pnode->height,
2157                                    punit->upkeep, happy_cost);
2158 
2159       g_signal_handlers_disconnect_matched(cmd,
2160 	  G_SIGNAL_MATCH_FUNC,
2161 	  0, 0, NULL, supported_unit_callback, NULL);
2162 
2163       g_signal_handlers_disconnect_matched(cmd,
2164 	  G_SIGNAL_MATCH_FUNC,
2165 	  0, 0, NULL, supported_unit_middle_callback, NULL);
2166 
2167       gtk_widget_set_tooltip_text(cmd, unit_description(punit));
2168 
2169       g_signal_connect(cmd, "button_press_event",
2170 	  G_CALLBACK(supported_unit_callback),
2171 	  GINT_TO_POINTER(punit->id));
2172 
2173       g_signal_connect(cmd, "button_release_event",
2174 	  G_CALLBACK(supported_unit_middle_callback),
2175 	  GINT_TO_POINTER(punit->id));
2176 
2177       if (city_owner(pdialog->pcity) != client.conn.playing) {
2178 	gtk_widget_set_sensitive(cmd, FALSE);
2179       } else {
2180 	gtk_widget_set_sensitive(cmd, TRUE);
2181       }
2182 
2183       gtk_widget_show(pix);
2184       gtk_widget_show(cmd);
2185     }
2186     i++;
2187   } unit_list_iterate_end;
2188 
2189   buf = g_strdup_printf(_("Supported units %d"), n);
2190   gtk_frame_set_label(GTK_FRAME(pdialog->overview.supported_units_frame), buf);
2191   g_free(buf);
2192 }
2193 
2194 /****************************************************************
2195   Update list of present units in city dialog
2196 *****************************************************************/
city_dialog_update_present_units(struct city_dialog * pdialog)2197 static void city_dialog_update_present_units(struct city_dialog *pdialog)
2198 {
2199   struct unit_list *units;
2200   struct unit_node_vector *nodes;
2201   int n, m, i;
2202   gchar *buf;
2203 
2204   if (NULL != client.conn.playing
2205       && city_owner(pdialog->pcity) != client.conn.playing) {
2206     units = pdialog->pcity->client.info_units_present;
2207   } else {
2208     units = pdialog->pcity->tile->units;
2209   }
2210 
2211   nodes = &pdialog->overview.present_units;
2212 
2213   n = unit_list_size(units);
2214   m = unit_node_vector_size(nodes);
2215 
2216   if (m > n) {
2217     i = 0;
2218     unit_node_vector_iterate(nodes, elt) {
2219       if (i++ >= n) {
2220 	gtk_widget_destroy(elt->cmd);
2221       }
2222     } unit_node_vector_iterate_end;
2223 
2224     unit_node_vector_reserve(nodes, n);
2225   } else {
2226     for (i = m; i < n; i++) {
2227       GtkWidget *cmd, *pix;
2228       struct unit_node node;
2229 
2230       cmd = gtk_button_new();
2231       node.cmd = cmd;
2232 
2233       gtk_button_set_relief(GTK_BUTTON(cmd), GTK_RELIEF_NONE);
2234       gtk_widget_add_events(cmd,
2235 	  GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2236 
2237       pix = gtk_image_new();
2238       node.pix = pix;
2239       node.height = tileset_full_tile_height(tileset);
2240 
2241       gtk_container_add(GTK_CONTAINER(cmd), pix);
2242 
2243       gtk_grid_attach(GTK_GRID(pdialog->overview.present_unit_table),
2244                       cmd, i, 0, 1, 1);
2245       unit_node_vector_append(nodes, node);
2246     }
2247   }
2248 
2249   i = 0;
2250   unit_list_iterate(units, punit) {
2251     struct unit_node *pnode;
2252 
2253     pnode = unit_node_vector_get(nodes, i);
2254     if (pnode) {
2255       GtkWidget *cmd, *pix;
2256 
2257       cmd = pnode->cmd;
2258       pix = pnode->pix;
2259 
2260       put_unit_image(punit, GTK_IMAGE(pix), pnode->height);
2261 
2262       g_signal_handlers_disconnect_matched(cmd,
2263 	  G_SIGNAL_MATCH_FUNC,
2264 	  0, 0, NULL, present_unit_callback, NULL);
2265 
2266       g_signal_handlers_disconnect_matched(cmd,
2267 	  G_SIGNAL_MATCH_FUNC,
2268 	  0, 0, NULL, present_unit_middle_callback, NULL);
2269 
2270       gtk_widget_set_tooltip_text(cmd, unit_description(punit));
2271 
2272       g_signal_connect(cmd, "button_press_event",
2273 	  G_CALLBACK(present_unit_callback),
2274 	  GINT_TO_POINTER(punit->id));
2275 
2276       g_signal_connect(cmd, "button_release_event",
2277 	  G_CALLBACK(present_unit_middle_callback),
2278 	  GINT_TO_POINTER(punit->id));
2279 
2280       if (city_owner(pdialog->pcity) != client.conn.playing) {
2281 	gtk_widget_set_sensitive(cmd, FALSE);
2282       } else {
2283 	gtk_widget_set_sensitive(cmd, TRUE);
2284       }
2285 
2286       gtk_widget_show(pix);
2287       gtk_widget_show(cmd);
2288     }
2289     i++;
2290   } unit_list_iterate_end;
2291 
2292   buf = g_strdup_printf(_("Present units %d"), n);
2293   gtk_frame_set_label(GTK_FRAME(pdialog->overview.present_units_frame), buf);
2294   g_free(buf);
2295 }
2296 
2297 /****************************************************************
2298   Updates the sensitivity of the the prev and next buttons.
2299   this does not need pdialog as a parameter, since it iterates
2300   over all the open dialogs.
2301   note: we still need the sensitivity code in create_city_dialog()
2302   for the spied dialogs.
2303 *****************************************************************/
city_dialog_update_prev_next(void)2304 static void city_dialog_update_prev_next(void)
2305 {
2306   int count = 0;
2307   int city_number;
2308 
2309   if (client_is_global_observer()) {
2310     return; /* Keep them insensitive as initially set */
2311   }
2312 
2313   city_number = city_list_size(client.conn.playing->cities);
2314 
2315   /* the first time, we see if all the city dialogs are open */
2316   dialog_list_iterate(dialog_list, pdialog) {
2317     if (city_owner(pdialog->pcity) == client.conn.playing) {
2318       count++;
2319     }
2320   } dialog_list_iterate_end;
2321 
2322   if (count == city_number) {	/* all are open, shouldn't prev/next */
2323     dialog_list_iterate(dialog_list, pdialog) {
2324       gtk_widget_set_sensitive(pdialog->prev_command, FALSE);
2325       gtk_widget_set_sensitive(pdialog->next_command, FALSE);
2326     } dialog_list_iterate_end;
2327   } else {
2328     dialog_list_iterate(dialog_list, pdialog) {
2329       if (city_owner(pdialog->pcity) == client.conn.playing) {
2330 	gtk_widget_set_sensitive(pdialog->prev_command, TRUE);
2331 	gtk_widget_set_sensitive(pdialog->next_command, TRUE);
2332       }
2333     } dialog_list_iterate_end;
2334   }
2335 }
2336 
2337 /****************************************************************
2338   User clicked button from action area.
2339 *****************************************************************/
citydlg_response_callback(GtkDialog * dlg,gint response,void * data)2340 static void citydlg_response_callback(GtkDialog *dlg, gint response,
2341                                       void *data)
2342 {
2343   switch (response) {
2344   case CDLGR_UNITS:
2345     show_units_response(data);
2346     break;
2347   }
2348 }
2349 
2350 /****************************************************************
2351   User has clicked show units
2352 *****************************************************************/
show_units_response(void * data)2353 static void show_units_response(void *data)
2354 {
2355   struct city_dialog *pdialog = (struct city_dialog *) data;
2356   struct tile *ptile = pdialog->pcity->tile;
2357 
2358   if (unit_list_size(ptile->units)) {
2359     unit_select_dialog_popup(ptile);
2360   }
2361 }
2362 
2363 /****************************************************************
2364   Set city menu position
2365 *****************************************************************/
city_menu_position(GtkMenu * menu,gint * x,gint * y,gboolean * push_in,gpointer data)2366 static void city_menu_position(GtkMenu *menu, gint *x, gint *y,
2367                                gboolean *push_in, gpointer data)
2368 {
2369   GtkWidget *widget;
2370   GtkAllocation allocation;
2371   gint xpos;
2372   gint ypos;
2373 
2374   fc_assert_ret(GTK_IS_BUTTON(data));
2375 
2376   widget = GTK_WIDGET(data);
2377 
2378   gtk_widget_get_allocation(widget, &allocation);
2379 
2380   gdk_window_get_origin(gtk_widget_get_window(widget), &xpos, &ypos);
2381 
2382   xpos += allocation.x + allocation.width/2;
2383   ypos += allocation.y + allocation.height/2;
2384 
2385   *x = xpos;
2386   *y = ypos;
2387   *push_in = TRUE;
2388 }
2389 
2390 /****************************************************************
2391   Destroy widget -callback
2392 *****************************************************************/
destroy_func(GtkWidget * w,gpointer data)2393 static void destroy_func(GtkWidget *w, gpointer data)
2394 {
2395   gtk_widget_destroy(w);
2396 }
2397 
2398 /****************************************************************
2399 Pop-up menu to change attributes of supported units
2400 *****************************************************************/
supported_unit_callback(GtkWidget * w,GdkEventButton * ev,gpointer data)2401 static gboolean supported_unit_callback(GtkWidget * w, GdkEventButton * ev,
2402 				        gpointer data)
2403 {
2404   GtkWidget *menu, *item;
2405   struct city_dialog *pdialog;
2406   struct city *pcity;
2407   struct unit *punit =
2408     player_unit_by_number(client_player(), (size_t) data);
2409 
2410   if (NULL != punit
2411    && NULL != (pcity = game_city_by_number(punit->homecity))
2412    && NULL != (pdialog = get_city_dialog(pcity))) {
2413 
2414     if (ev->type != GDK_BUTTON_PRESS || ev->button == 2 || ev->button == 3
2415 	|| !can_client_issue_orders()) {
2416       return FALSE;
2417     }
2418 
2419     menu = pdialog->popup_menu;
2420 
2421     gtk_menu_popdown(GTK_MENU(menu));
2422     gtk_container_foreach(GTK_CONTAINER(menu), destroy_func, NULL);
2423 
2424     item = gtk_menu_item_new_with_mnemonic(_("Cen_ter"));
2425     g_signal_connect(item, "activate",
2426       G_CALLBACK(unit_center_callback),
2427       GINT_TO_POINTER(punit->id));
2428     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2429 
2430     item = gtk_menu_item_new_with_mnemonic(_("_Activate unit"));
2431     g_signal_connect(item, "activate",
2432       G_CALLBACK(unit_activate_callback),
2433       GINT_TO_POINTER(punit->id));
2434     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2435 
2436     item = gtk_menu_item_new_with_mnemonic(_("Activate unit, _close dialog"));
2437     g_signal_connect(item, "activate",
2438       G_CALLBACK(supported_unit_activate_close_callback),
2439       GINT_TO_POINTER(punit->id));
2440     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2441 
2442     item = gtk_menu_item_new_with_mnemonic(_("_Disband unit"));
2443     g_signal_connect(item, "activate",
2444       G_CALLBACK(unit_disband_callback),
2445       GINT_TO_POINTER(punit->id));
2446     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2447 
2448     if (unit_has_type_flag(punit, UTYF_UNDISBANDABLE)) {
2449       gtk_widget_set_sensitive(item, FALSE);
2450     }
2451 
2452     gtk_widget_show_all(menu);
2453 
2454     gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
2455       city_menu_position, w, ev->button, ev->time);
2456 
2457 
2458   }
2459   return TRUE;
2460 }
2461 
2462 /****************************************************************
2463 Pop-up menu to change attributes of units, ex. change homecity.
2464 *****************************************************************/
present_unit_callback(GtkWidget * w,GdkEventButton * ev,gpointer data)2465 static gboolean present_unit_callback(GtkWidget * w, GdkEventButton * ev,
2466 				      gpointer data)
2467 {
2468   GtkWidget *menu, *item;
2469   struct city_dialog *pdialog;
2470   struct city *pcity;
2471   struct unit *punit =
2472     player_unit_by_number(client_player(), (size_t) data);
2473 
2474   if (NULL != punit
2475    && NULL != (pcity = tile_city(unit_tile(punit)))
2476    && NULL != (pdialog = get_city_dialog(pcity))) {
2477 
2478     if (ev->type != GDK_BUTTON_PRESS || ev->button == 2 || ev->button == 3
2479 	|| !can_client_issue_orders()) {
2480       return FALSE;
2481     }
2482 
2483     menu = pdialog->popup_menu;
2484 
2485     gtk_menu_popdown(GTK_MENU(menu));
2486     gtk_container_foreach(GTK_CONTAINER(menu), destroy_func, NULL);
2487 
2488     item = gtk_menu_item_new_with_mnemonic(_("_Activate unit"));
2489     g_signal_connect(item, "activate",
2490       G_CALLBACK(unit_activate_callback),
2491       GINT_TO_POINTER(punit->id));
2492     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2493 
2494     item = gtk_menu_item_new_with_mnemonic(_("Activate unit, _close dialog"));
2495     g_signal_connect(item, "activate",
2496       G_CALLBACK(present_unit_activate_close_callback),
2497       GINT_TO_POINTER(punit->id));
2498     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2499 
2500     item = gtk_menu_item_new_with_mnemonic(_("_Load unit"));
2501     g_signal_connect(item, "activate",
2502       G_CALLBACK(unit_load_callback),
2503       GINT_TO_POINTER(punit->id));
2504     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2505 
2506     if (!unit_can_load(punit)) {
2507       gtk_widget_set_sensitive(item, FALSE);
2508     }
2509 
2510     item = gtk_menu_item_new_with_mnemonic(_("_Unload unit"));
2511     g_signal_connect(item, "activate",
2512       G_CALLBACK(unit_unload_callback),
2513       GINT_TO_POINTER(punit->id));
2514     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2515 
2516     if (!can_unit_unload(punit, unit_transport_get(punit))
2517         || !can_unit_exist_at_tile(punit, unit_tile(punit))) {
2518       gtk_widget_set_sensitive(item, FALSE);
2519     }
2520 
2521     item = gtk_menu_item_new_with_mnemonic(_("_Sentry unit"));
2522     g_signal_connect(item, "activate",
2523       G_CALLBACK(unit_sentry_callback),
2524       GINT_TO_POINTER(punit->id));
2525     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2526 
2527     if (punit->activity == ACTIVITY_SENTRY
2528 	|| !can_unit_do_activity(punit, ACTIVITY_SENTRY)) {
2529       gtk_widget_set_sensitive(item, FALSE);
2530     }
2531 
2532     item = gtk_menu_item_new_with_mnemonic(_("_Fortify unit"));
2533     g_signal_connect(item, "activate",
2534       G_CALLBACK(unit_fortify_callback),
2535       GINT_TO_POINTER(punit->id));
2536     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2537 
2538     if (punit->activity == ACTIVITY_FORTIFYING
2539 	|| !can_unit_do_activity(punit, ACTIVITY_FORTIFYING)) {
2540       gtk_widget_set_sensitive(item, FALSE);
2541     }
2542 
2543     item = gtk_menu_item_new_with_mnemonic(_("_Disband unit"));
2544     g_signal_connect(item, "activate",
2545       G_CALLBACK(unit_disband_callback),
2546       GINT_TO_POINTER(punit->id));
2547     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2548 
2549     if (unit_has_type_flag(punit, UTYF_UNDISBANDABLE)) {
2550       gtk_widget_set_sensitive(item, FALSE);
2551     }
2552 
2553     item = gtk_menu_item_new_with_mnemonic(_("Set _Home City"));
2554     g_signal_connect(item, "activate",
2555       G_CALLBACK(unit_homecity_callback),
2556       GINT_TO_POINTER(punit->id));
2557     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2558     gtk_widget_set_sensitive(item, can_unit_change_homecity_to(punit, pcity));
2559 
2560     item = gtk_menu_item_new_with_mnemonic(_("U_pgrade unit"));
2561     g_signal_connect(item, "activate",
2562       G_CALLBACK(unit_upgrade_callback),
2563       GINT_TO_POINTER(punit->id));
2564     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2565 
2566     if (!can_client_issue_orders()
2567 	|| NULL == can_upgrade_unittype(client.conn.playing,
2568                                         unit_type_get(punit))) {
2569       gtk_widget_set_sensitive(item, FALSE);
2570     }
2571 
2572     gtk_widget_show_all(menu);
2573 
2574     gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
2575       city_menu_position, w, ev->button, ev->time);
2576   }
2577   return TRUE;
2578 }
2579 
2580 /****************************************************************
2581  if user middle-clicked on a unit, activate it and close dialog
2582 *****************************************************************/
present_unit_middle_callback(GtkWidget * w,GdkEventButton * ev,gpointer data)2583 static gboolean present_unit_middle_callback(GtkWidget * w,
2584 					     GdkEventButton * ev,
2585 					     gpointer data)
2586 {
2587   struct city_dialog *pdialog;
2588   struct city *pcity;
2589   struct unit *punit =
2590     player_unit_by_number(client_player(), (size_t) data);
2591 
2592   if (NULL != punit
2593    && NULL != (pcity = tile_city(unit_tile(punit)))
2594    && NULL != (pdialog = get_city_dialog(pcity))
2595    && can_client_issue_orders()) {
2596 
2597     if (ev->button == 3) {
2598       unit_focus_set(punit);
2599     } else if (ev->button == 2) {
2600       unit_focus_set(punit);
2601       close_city_dialog(pdialog);
2602     }
2603   }
2604 
2605   return TRUE;
2606 }
2607 
2608 /****************************************************************
2609  if user middle-clicked on a unit, activate it and close dialog
2610 *****************************************************************/
supported_unit_middle_callback(GtkWidget * w,GdkEventButton * ev,gpointer data)2611 static gboolean supported_unit_middle_callback(GtkWidget * w,
2612 					       GdkEventButton * ev,
2613 					       gpointer data)
2614 {
2615   struct city_dialog *pdialog;
2616   struct city *pcity;
2617   struct unit *punit =
2618     player_unit_by_number(client_player(), (size_t) data);
2619 
2620   if (NULL != punit
2621    && NULL != (pcity = game_city_by_number(punit->homecity))
2622    && NULL != (pdialog = get_city_dialog(pcity))
2623    && can_client_issue_orders()) {
2624 
2625     if (ev->button == 3) {
2626       unit_focus_set(punit);
2627     } else if (ev->button == 2) {
2628       unit_focus_set(punit);
2629       close_city_dialog(pdialog);
2630     }
2631   }
2632 
2633   return TRUE;
2634 }
2635 
2636 /****************************************************************
2637   User has requested centering to unit
2638 *****************************************************************/
unit_center_callback(GtkWidget * w,gpointer data)2639 static void unit_center_callback(GtkWidget * w, gpointer data)
2640 {
2641   struct unit *punit =
2642     player_unit_by_number(client_player(), (size_t)data);
2643 
2644   if (NULL != punit) {
2645     center_tile_mapcanvas(unit_tile(punit));
2646   }
2647 }
2648 
2649 /****************************************************************
2650   User has requested unit activation
2651 *****************************************************************/
unit_activate_callback(GtkWidget * w,gpointer data)2652 static void unit_activate_callback(GtkWidget * w, gpointer data)
2653 {
2654   struct unit *punit =
2655     player_unit_by_number(client_player(), (size_t)data);
2656 
2657   if (NULL != punit) {
2658     unit_focus_set(punit);
2659   }
2660 }
2661 
2662 /****************************************************************
2663   User has requested some supported unit to be activated and
2664   city dialog to be closed
2665 *****************************************************************/
supported_unit_activate_close_callback(GtkWidget * w,gpointer data)2666 static void supported_unit_activate_close_callback(GtkWidget * w,
2667 						   gpointer data)
2668 {
2669   struct unit *punit =
2670     player_unit_by_number(client_player(), (size_t)data);
2671 
2672   if (NULL != punit) {
2673     struct city *pcity =
2674       player_city_by_number(client_player(), punit->homecity);
2675 
2676     unit_focus_set(punit);
2677     if (NULL != pcity) {
2678       struct city_dialog *pdialog = get_city_dialog(pcity);
2679 
2680       if (NULL != pdialog) {
2681 	close_city_dialog(pdialog);
2682       }
2683     }
2684   }
2685 }
2686 
2687 /****************************************************************
2688   User has requested some present unit to be activated and
2689   city dialog to be closed
2690 *****************************************************************/
present_unit_activate_close_callback(GtkWidget * w,gpointer data)2691 static void present_unit_activate_close_callback(GtkWidget * w,
2692 						 gpointer data)
2693 {
2694   struct unit *punit =
2695     player_unit_by_number(client_player(), (size_t)data);
2696 
2697   if (NULL != punit) {
2698     struct city *pcity = tile_city(unit_tile(punit));
2699 
2700     unit_focus_set(punit);
2701     if (NULL != pcity) {
2702       struct city_dialog *pdialog = get_city_dialog(pcity);
2703 
2704       if (NULL != pdialog) {
2705 	close_city_dialog(pdialog);
2706       }
2707     }
2708   }
2709 }
2710 
2711 /****************************************************************
2712   User has requested unit to be loaded to transport
2713 *****************************************************************/
unit_load_callback(GtkWidget * w,gpointer data)2714 static void unit_load_callback(GtkWidget * w, gpointer data)
2715 {
2716   struct unit *punit =
2717     player_unit_by_number(client_player(), (size_t)data);
2718 
2719   if (NULL != punit) {
2720     request_transport(punit, unit_tile(punit));
2721   }
2722 }
2723 
2724 /****************************************************************
2725   User has requested unit to be unloaded from transport
2726 *****************************************************************/
unit_unload_callback(GtkWidget * w,gpointer data)2727 static void unit_unload_callback(GtkWidget * w, gpointer data)
2728 {
2729   struct unit *punit =
2730     player_unit_by_number(client_player(), (size_t)data);
2731 
2732   if (NULL != punit) {
2733     request_unit_unload(punit);
2734   }
2735 }
2736 
2737 /****************************************************************
2738   User has requested unit to be sentried
2739 *****************************************************************/
unit_sentry_callback(GtkWidget * w,gpointer data)2740 static void unit_sentry_callback(GtkWidget * w, gpointer data)
2741 {
2742   struct unit *punit =
2743     player_unit_by_number(client_player(), (size_t)data);
2744 
2745   if (NULL != punit) {
2746     request_unit_sentry(punit);
2747   }
2748 }
2749 
2750 /****************************************************************
2751   User has requested unit to be fortified
2752 *****************************************************************/
unit_fortify_callback(GtkWidget * w,gpointer data)2753 static void unit_fortify_callback(GtkWidget * w, gpointer data)
2754 {
2755   struct unit *punit =
2756     player_unit_by_number(client_player(), (size_t)data);
2757 
2758   if (NULL != punit) {
2759     request_unit_fortify(punit);
2760   }
2761 }
2762 
2763 /****************************************************************
2764   User has requested unit to be disbanded
2765 *****************************************************************/
unit_disband_callback(GtkWidget * w,gpointer data)2766 static void unit_disband_callback(GtkWidget * w, gpointer data)
2767 {
2768   struct unit_list *punits;
2769   struct unit *punit =
2770     player_unit_by_number(client_player(), (size_t)data);
2771 
2772   if (NULL == punit) {
2773     return;
2774   }
2775 
2776   punits = unit_list_new();
2777   unit_list_append(punits, punit);
2778   popup_disband_dialog(punits);
2779   unit_list_destroy(punits);
2780 }
2781 
2782 /****************************************************************
2783   User has requested unit to change homecity to city where it
2784   currently is
2785 *****************************************************************/
unit_homecity_callback(GtkWidget * w,gpointer data)2786 static void unit_homecity_callback(GtkWidget * w, gpointer data)
2787 {
2788   struct unit *punit =
2789     player_unit_by_number(client_player(), (size_t)data);
2790 
2791   if (NULL != punit) {
2792     request_unit_change_homecity(punit);
2793   }
2794 }
2795 
2796 /****************************************************************
2797   User has requested unit to be upgraded
2798 *****************************************************************/
unit_upgrade_callback(GtkWidget * w,gpointer data)2799 static void unit_upgrade_callback(GtkWidget *w, gpointer data)
2800 {
2801   struct unit_list *punits;
2802   struct unit *punit =
2803     player_unit_by_number(client_player(), (size_t)data);
2804 
2805   if (NULL == punit) {
2806     return;
2807   }
2808 
2809   punits = unit_list_new();
2810   unit_list_append(punits, punit);
2811   popup_upgrade_dialog(punits);
2812   unit_list_destroy(punits);
2813 }
2814 
2815 /*** Callbacks for citizen bar, map funcs that are not update ***/
2816 /****************************************************************
2817 Somebody clicked our list of citizens. If they clicked a specialist
2818 then change the type of him, else do nothing.
2819 *****************************************************************/
citizens_callback(GtkWidget * w,GdkEventButton * ev,gpointer data)2820 static gboolean citizens_callback(GtkWidget *w, GdkEventButton *ev,
2821                                   gpointer data)
2822 {
2823   struct city_dialog *pdialog = data;
2824   struct city *pcity = pdialog->pcity;
2825   int citnum, tlen, len;
2826 
2827   if (!can_client_issue_orders()) {
2828     return FALSE;
2829   }
2830 
2831   tlen = tileset_small_sprite_width(tileset);
2832   len = (city_size_get(pcity) - 1) * pdialog->cwidth + tlen;
2833   if (ev->x > len) {
2834     /* no citizen that far to the right */
2835     return FALSE;
2836   }
2837   citnum = MIN(city_size_get(pcity) - 1, ev->x / pdialog->cwidth);
2838 
2839   city_rotate_specialist(pcity, citnum);
2840 
2841   return TRUE;
2842 }
2843 
2844 /**************************************************************************
2845   Set requested workertask
2846 **************************************************************************/
set_city_workertask(GtkWidget * w,gpointer data)2847 static void set_city_workertask(GtkWidget *w, gpointer data)
2848 {
2849   enum unit_activity act = (enum unit_activity)GPOINTER_TO_INT(data);
2850   struct city *pcity = workertask_req.owner;
2851   struct tile *ptile = workertask_req.loc;
2852   struct packet_worker_task task;
2853 
2854   task.city_id = pcity->id;
2855 
2856   if (act == ACTIVITY_LAST) {
2857     task.tgt = -1;
2858     task.want = 0;
2859   } else {
2860     enum extra_cause cause = activity_to_extra_cause(act);
2861     enum extra_rmcause rmcause = activity_to_extra_rmcause(act);
2862     struct extra_type *tgt;
2863 
2864     if (cause != EC_NONE) {
2865       tgt = next_extra_for_tile(ptile, cause, city_owner(pcity), NULL);
2866     } else if (rmcause != ERM_NONE) {
2867       tgt = prev_extra_in_tile(ptile, rmcause, city_owner(pcity), NULL);
2868     } else {
2869       tgt = NULL;
2870     }
2871 
2872     if (tgt == NULL) {
2873       struct terrain *pterr = tile_terrain(ptile);
2874 
2875       if ((act != ACTIVITY_TRANSFORM
2876            || pterr->transform_result == NULL || pterr->transform_result == pterr)
2877           && (act != ACTIVITY_IRRIGATE
2878               || pterr->irrigation_result == NULL || pterr->irrigation_result == pterr)
2879           && (act != ACTIVITY_MINE
2880               || pterr->mining_result == NULL || pterr->mining_result == pterr)) {
2881         /* No extra to order */
2882         output_window_append(ftc_client, _("There's no suitable extra to order."));
2883 
2884         return;
2885       }
2886 
2887       task.tgt = -1;
2888     } else {
2889       task.tgt = extra_index(tgt);
2890     }
2891 
2892     task.want = 100;
2893   }
2894 
2895   task.tile_id = ptile->index;
2896   task.activity = act;
2897 
2898   send_packet_worker_task(&client.conn, &task);
2899 }
2900 
2901 /****************************************************************
2902   Destroy workertask dlg
2903 *****************************************************************/
workertask_dlg_destroy(GtkWidget * w,gpointer data)2904 static void workertask_dlg_destroy(GtkWidget *w, gpointer data)
2905 {
2906   is_showing_workertask_dialog = FALSE;
2907 }
2908 
2909 /**************************************************************************
2910   Open dialog for setting worker task
2911 **************************************************************************/
popup_workertask_dlg(struct city * pcity,struct tile * ptile)2912 static void popup_workertask_dlg(struct city *pcity, struct tile *ptile)
2913 {
2914   if (!is_showing_workertask_dialog) {
2915     GtkWidget *shl;
2916     struct terrain *pterr = tile_terrain(ptile);
2917     struct universal for_terr = { .kind = VUT_TERRAIN,
2918                                   .value = { .terrain = pterr }};
2919     struct worker_task *ptask;
2920 
2921     is_showing_workertask_dialog = TRUE;
2922     workertask_req.owner = pcity;
2923     workertask_req.loc = ptile;
2924 
2925     shl = choice_dialog_start(GTK_WINDOW(toplevel),
2926 			       _("What Action to Request"),
2927 			       _("Select autosettler activity:"));
2928 
2929     ptask = worker_task_list_get(pcity->task_reqs, 0);
2930     if (ptask != NULL) {
2931       choice_dialog_add(shl, _("Clear request"),
2932                         G_CALLBACK(set_city_workertask),
2933                         GINT_TO_POINTER(ACTIVITY_LAST), FALSE, NULL);
2934     }
2935 
2936     if ((pterr->mining_result == pterr
2937          && effect_cumulative_max(EFT_MINING_POSSIBLE, &for_terr) > 0)
2938         || (pterr->mining_result != pterr && pterr->mining_result != NULL
2939             && effect_cumulative_max(EFT_MINING_TF_POSSIBLE, &for_terr) > 0)) {
2940       choice_dialog_add(shl, _("Mine"),
2941                         G_CALLBACK(set_city_workertask),
2942                         GINT_TO_POINTER(ACTIVITY_MINE), FALSE, NULL);
2943     }
2944     if ((pterr->irrigation_result == pterr
2945          && effect_cumulative_max(EFT_IRRIG_POSSIBLE, &for_terr) > 0)
2946         || (pterr->irrigation_result != pterr && pterr->irrigation_result != NULL
2947             && effect_cumulative_max(EFT_IRRIG_TF_POSSIBLE, &for_terr) > 0)) {
2948       choice_dialog_add(shl, _("Irrigate"),
2949                         G_CALLBACK(set_city_workertask),
2950                         GINT_TO_POINTER(ACTIVITY_IRRIGATE), FALSE, NULL);
2951     }
2952     if (next_extra_for_tile(ptile, EC_ROAD, city_owner(pcity), NULL) != NULL) {
2953       choice_dialog_add(shl, _("Road"),
2954                         G_CALLBACK(set_city_workertask),
2955                         GINT_TO_POINTER(ACTIVITY_GEN_ROAD), FALSE, NULL);
2956     }
2957     if (pterr->transform_result != pterr && pterr->transform_result != NULL
2958         && effect_cumulative_max(EFT_TRANSFORM_POSSIBLE, &for_terr) > 0) {
2959       choice_dialog_add(shl, _("Transform"),
2960                         G_CALLBACK(set_city_workertask),
2961                         GINT_TO_POINTER(ACTIVITY_TRANSFORM), FALSE, NULL);
2962     }
2963     if (prev_extra_in_tile(ptile, ERM_CLEANPOLLUTION,
2964                            city_owner(pcity), NULL) != NULL) {
2965       choice_dialog_add(shl, _("Clean Pollution"),
2966                         G_CALLBACK(set_city_workertask),
2967                         GINT_TO_POINTER(ACTIVITY_POLLUTION), FALSE, NULL);
2968     }
2969     if (prev_extra_in_tile(ptile, ERM_CLEANFALLOUT,
2970                            city_owner(pcity), NULL) != NULL) {
2971       choice_dialog_add(shl, _("Clean Fallout"),
2972                         G_CALLBACK(set_city_workertask),
2973                         GINT_TO_POINTER(ACTIVITY_FALLOUT), FALSE, NULL);
2974     }
2975 
2976     choice_dialog_add(shl, GTK_STOCK_CANCEL, 0, 0, FALSE, NULL);
2977     choice_dialog_end(shl);
2978 
2979     g_signal_connect(shl, "destroy", G_CALLBACK(workertask_dlg_destroy),
2980 		     NULL);
2981   }
2982 }
2983 
2984 /**************************************************************************
2985   User has pressed button on citymap
2986 **************************************************************************/
button_down_citymap(GtkWidget * w,GdkEventButton * ev,gpointer data)2987 static gboolean button_down_citymap(GtkWidget *w, GdkEventButton *ev,
2988                                     gpointer data)
2989 {
2990   struct city_dialog *pdialog = data;
2991   int canvas_x, canvas_y, city_x, city_y;
2992 
2993   if (!can_client_issue_orders()) {
2994     return FALSE;
2995   }
2996 
2997   canvas_x = ev->x * (double)canvas_width / (double)CITYMAP_WIDTH;
2998   canvas_y = ev->y * (double)canvas_height / (double)CITYMAP_HEIGHT;
2999 
3000   if (canvas_to_city_pos(&city_x, &city_y,
3001                          city_map_radius_sq_get(pdialog->pcity),
3002                          canvas_x, canvas_y)) {
3003     if (ev->button == 1) {
3004       city_toggle_worker(pdialog->pcity, city_x, city_y);
3005     } else if (ev->button == 3) {
3006       struct city *pcity = pdialog->pcity;
3007 
3008       popup_workertask_dlg(pdialog->pcity,
3009                            city_map_to_tile(pcity->tile, city_map_radius_sq_get(pcity),
3010                                             city_x, city_y));
3011     }
3012   }
3013 
3014   return TRUE;
3015 }
3016 
3017 /****************************************************************
3018   Set map canvas to be drawn
3019 *****************************************************************/
draw_map_canvas(struct city_dialog * pdialog)3020 static void draw_map_canvas(struct city_dialog *pdialog)
3021 {
3022   gtk_widget_queue_draw(pdialog->overview.map_canvas.darea);
3023   if (pdialog->happiness.map_canvas.darea) { /* in case of spy */
3024     gtk_widget_queue_draw(pdialog->happiness.map_canvas.darea);
3025   }
3026 }
3027 
3028 /********* Callbacks for Buy, Change, Sell, Worklist ************/
3029 /****************************************************************
3030   User has answered buy cost dialog
3031 *****************************************************************/
buy_callback_response(GtkWidget * w,gint response,gpointer data)3032 static void buy_callback_response(GtkWidget *w, gint response, gpointer data)
3033 {
3034   struct city_dialog *pdialog = data;
3035 
3036   if (response == GTK_RESPONSE_YES) {
3037     city_buy_production(pdialog->pcity);
3038   }
3039   gtk_widget_destroy(w);
3040 }
3041 
3042 /****************************************************************
3043   User has clicked buy-button
3044 *****************************************************************/
buy_callback(GtkWidget * w,gpointer data)3045 static void buy_callback(GtkWidget *w, gpointer data)
3046 {
3047   GtkWidget *shell;
3048   struct city_dialog *pdialog = data;
3049   const char *name = city_production_name_translation(pdialog->pcity);
3050   int value = city_production_buy_gold_cost(pdialog->pcity);
3051   char buf[1024];
3052 
3053   if (!can_client_issue_orders()) {
3054     return;
3055   }
3056 
3057   fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Treasury contains %d gold.",
3058                                         "Treasury contains %d gold.",
3059                                         client_player()->economic.gold),
3060               client_player()->economic.gold);
3061 
3062   if (value <= client_player()->economic.gold) {
3063     shell = gtk_message_dialog_new(NULL,
3064         GTK_DIALOG_DESTROY_WITH_PARENT,
3065         GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
3066         /* TRANS: Last %s is pre-pluralised "Treasury contains %d gold." */
3067         PL_("Buy %s for %d gold?\n%s",
3068             "Buy %s for %d gold?\n%s", value),
3069         name, value, buf);
3070     setup_dialog(shell, pdialog->shell);
3071     gtk_window_set_title(GTK_WINDOW(shell), _("Buy It!"));
3072     gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_NO);
3073     g_signal_connect(shell, "response", G_CALLBACK(buy_callback_response),
3074 	pdialog);
3075     gtk_window_present(GTK_WINDOW(shell));
3076   } else {
3077     shell = gtk_message_dialog_new(NULL,
3078         GTK_DIALOG_DESTROY_WITH_PARENT,
3079         GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
3080         /* TRANS: Last %s is pre-pluralised "Treasury contains %d gold." */
3081         PL_("%s costs %d gold.\n%s",
3082             "%s costs %d gold.\n%s", value),
3083         name, value, buf);
3084     setup_dialog(shell, pdialog->shell);
3085     gtk_window_set_title(GTK_WINDOW(shell), _("Buy It!"));
3086     g_signal_connect(shell, "response", G_CALLBACK(gtk_widget_destroy),
3087       NULL);
3088     gtk_window_present(GTK_WINDOW(shell));
3089   }
3090 }
3091 
3092 /****************************************************************************
3093   Callback for the dropdown production menu.
3094 ****************************************************************************/
change_production_callback(GtkComboBox * combo,struct city_dialog * pdialog)3095 static void change_production_callback(GtkComboBox *combo,
3096                                        struct city_dialog *pdialog)
3097 {
3098   GtkTreeIter iter;
3099 
3100   if (can_client_issue_orders()
3101       && gtk_combo_box_get_active_iter(combo, &iter)) {
3102     cid id;
3103     struct universal univ;
3104 
3105     gtk_tree_model_get(gtk_combo_box_get_model(combo), &iter, 2, &id, -1);
3106     univ = cid_production(id);
3107     city_change_production(pdialog->pcity, &univ);
3108   }
3109 }
3110 
3111 /****************************************************************
3112   User has clicked sell-button
3113 *****************************************************************/
sell_callback(struct impr_type * pimprove,gpointer data)3114 static void sell_callback(struct impr_type *pimprove, gpointer data)
3115 {
3116   GtkWidget *shl;
3117   struct city_dialog *pdialog = (struct city_dialog *) data;
3118   pdialog->sell_id = improvement_number(pimprove);
3119   int price;
3120 
3121   if (!can_client_issue_orders()) {
3122     return;
3123   }
3124 
3125   if (test_player_sell_building_now(client.conn.playing, pdialog->pcity,
3126                                     pimprove) != TR_SUCCESS) {
3127     return;
3128   }
3129 
3130   price = impr_sell_gold(pimprove);
3131   shl = gtk_message_dialog_new(NULL,
3132     GTK_DIALOG_DESTROY_WITH_PARENT,
3133     GTK_MESSAGE_QUESTION,
3134     GTK_BUTTONS_YES_NO,
3135     PL_("Sell %s for %d gold?",
3136         "Sell %s for %d gold?", price),
3137     city_improvement_name_translation(pdialog->pcity, pimprove), price);
3138   setup_dialog(shl, pdialog->shell);
3139   pdialog->sell_shell = shl;
3140 
3141   gtk_window_set_title(GTK_WINDOW(shl), _("Sell It!"));
3142   gtk_window_set_position(GTK_WINDOW(shl), GTK_WIN_POS_CENTER_ON_PARENT);
3143 
3144   g_signal_connect(shl, "response",
3145 		   G_CALLBACK(sell_callback_response), pdialog);
3146 
3147   gtk_window_present(GTK_WINDOW(shl));
3148 }
3149 
3150 /****************************************************************
3151   User has responded to sell price dialog
3152 *****************************************************************/
sell_callback_response(GtkWidget * w,gint response,gpointer data)3153 static void sell_callback_response(GtkWidget *w, gint response, gpointer data)
3154 {
3155   struct city_dialog *pdialog = data;
3156 
3157   if (response == GTK_RESPONSE_YES) {
3158     city_sell_improvement(pdialog->pcity, pdialog->sell_id);
3159   }
3160   gtk_widget_destroy(w);
3161 
3162   pdialog->sell_shell = NULL;
3163 }
3164 
3165 /****************************************************************
3166  this is here because it's closely related to the sell stuff
3167 *****************************************************************/
impr_callback(GtkTreeView * view,GtkTreePath * path,GtkTreeViewColumn * col,gpointer data)3168 static void impr_callback(GtkTreeView *view, GtkTreePath *path,
3169 			  GtkTreeViewColumn *col, gpointer data)
3170 {
3171   GtkTreeModel *model;
3172   GtkTreeIter it;
3173   GdkWindow *win;
3174   GdkDeviceManager *manager;
3175   GdkModifierType mask;
3176   struct impr_type *pimprove;
3177 
3178   model = gtk_tree_view_get_model(view);
3179 
3180   if (!gtk_tree_model_get_iter(model, &it, path)) {
3181     return;
3182   }
3183 
3184   gtk_tree_model_get(model, &it, 0, &pimprove, -1);
3185 
3186   win = gdk_get_default_root_window();
3187   manager = gdk_display_get_device_manager(gdk_window_get_display(win));
3188 
3189   gdk_window_get_device_position(win,
3190                                  gdk_device_manager_get_client_pointer(manager),
3191                                  NULL, NULL, &mask);
3192 
3193   if (!(mask & GDK_CONTROL_MASK)) {
3194     sell_callback(pimprove, data);
3195   } else {
3196     if (is_great_wonder(pimprove)) {
3197       popup_help_dialog_typed(improvement_name_translation(pimprove), HELP_WONDER);
3198     } else {
3199       popup_help_dialog_typed(improvement_name_translation(pimprove), HELP_IMPROVEMENT);
3200     }
3201   }
3202 }
3203 
3204 /******* Callbacks for stuff on the Misc. Settings page *********/
3205 /****************************************************************
3206   Called when Rename button pressed
3207 *****************************************************************/
rename_callback(GtkWidget * w,gpointer data)3208 static void rename_callback(GtkWidget *w, gpointer data)
3209 {
3210   struct city_dialog *pdialog;
3211 
3212   pdialog = (struct city_dialog *) data;
3213 
3214   pdialog->rename_shell = input_dialog_create(GTK_WINDOW(pdialog->shell),
3215                                               /* "shellrenamecity" */
3216                                               _("Rename City"),
3217                                               _("What should we rename the city to?"),
3218                                               city_name_get(pdialog->pcity),
3219                                               rename_popup_callback, pdialog);
3220 }
3221 
3222 /****************************************************************
3223   Called when user has finished with "Rename City" popup
3224 *****************************************************************/
rename_popup_callback(gpointer data,gint response,const char * input)3225 static void rename_popup_callback(gpointer data, gint response,
3226                                   const char *input)
3227 {
3228   struct city_dialog *pdialog = data;
3229 
3230   if (pdialog) {
3231     if (response == GTK_RESPONSE_OK) {
3232       city_rename(pdialog->pcity, input);
3233     } /* else CANCEL or DELETE_EVENT */
3234 
3235     pdialog->rename_shell = NULL;
3236   }
3237 }
3238 
3239 /****************************************************************
3240  Sets which page will be set on reopen of dialog
3241 *****************************************************************/
misc_whichtab_callback(GtkWidget * w,gpointer data)3242 static void misc_whichtab_callback(GtkWidget * w, gpointer data)
3243 {
3244   new_dialog_def_page = GPOINTER_TO_INT(data);
3245 }
3246 
3247 /**************************************************************************
3248 City options callbacks
3249 **************************************************************************/
cityopt_callback(GtkWidget * w,gpointer data)3250 static void cityopt_callback(GtkWidget * w, gpointer data)
3251 {
3252   struct city_dialog *pdialog = (struct city_dialog *) data;
3253 
3254   if (!can_client_issue_orders()) {
3255     return;
3256   }
3257 
3258   if(!pdialog->misc.block_signal){
3259     struct city *pcity = pdialog->pcity;
3260     bv_city_options new_options;
3261 
3262     fc_assert(CITYO_LAST == 3);
3263 
3264     BV_CLR_ALL(new_options);
3265     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pdialog->misc.disband_on_settler))) {
3266       BV_SET(new_options, CITYO_DISBAND);
3267     }
3268     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pdialog->misc.new_citizens_radio[1]))) {
3269       BV_SET(new_options, CITYO_NEW_EINSTEIN);
3270     }
3271     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pdialog->misc.new_citizens_radio[2]))) {
3272       BV_SET(new_options, CITYO_NEW_TAXMAN);
3273     }
3274 
3275     dsend_packet_city_options_req(&client.conn, pcity->id,new_options);
3276   }
3277 }
3278 
3279 /**************************************************************************
3280  refresh the city options (auto_[land, air, sea, helicopter] and
3281  disband-is-size-1) in the misc page.
3282 **************************************************************************/
set_cityopt_values(struct city_dialog * pdialog)3283 static void set_cityopt_values(struct city_dialog *pdialog)
3284 {
3285   struct city *pcity = pdialog->pcity;
3286 
3287   pdialog->misc.block_signal = 1;
3288 
3289   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pdialog->misc.disband_on_settler),
3290 			       is_city_option_set(pcity, CITYO_DISBAND));
3291 
3292   if (is_city_option_set(pcity, CITYO_NEW_EINSTEIN)) {
3293     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
3294 				 (pdialog->misc.new_citizens_radio[1]), TRUE);
3295   } else if (is_city_option_set(pcity, CITYO_NEW_TAXMAN)) {
3296     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
3297 				 (pdialog->misc.new_citizens_radio[2]), TRUE);
3298   } else {
3299     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
3300 				 (pdialog->misc.new_citizens_radio[0]), TRUE);
3301   }
3302   pdialog->misc.block_signal = 0;
3303 }
3304 
3305 /*************** Callbacks for: Close, Prev, Next. **************/
3306 /****************************************************************
3307   User has clicked rename city-button
3308 *****************************************************************/
close_callback(GtkWidget * w,gpointer data)3309 static void close_callback(GtkWidget *w, gpointer data)
3310 {
3311   close_city_dialog((struct city_dialog *) data);
3312 }
3313 
3314 /****************************************************************
3315   User has closed rename city dialog
3316 *****************************************************************/
city_destroy_callback(GtkWidget * w,gpointer data)3317 static void city_destroy_callback(GtkWidget *w, gpointer data)
3318 {
3319   struct city_dialog *pdialog;
3320 
3321   pdialog = (struct city_dialog *) data;
3322 
3323   gtk_widget_hide(pdialog->shell);
3324 
3325   if (game.info.citizen_nationality) {
3326     citizens_dialog_close(pdialog->pcity);
3327   }
3328   close_happiness_dialog(pdialog->pcity);
3329   close_cma_dialog(pdialog->pcity);
3330 
3331   /* Save size of the city dialog. */
3332   gui_options.gui_gtk3_citydlg_xsize
3333     = CLIP(GUI_GTK3_CITYDLG_MIN_XSIZE,
3334            gtk_widget_get_allocated_width(pdialog->shell),
3335            GUI_GTK3_CITYDLG_MAX_XSIZE);
3336   gui_options.gui_gtk3_citydlg_ysize
3337     = CLIP(GUI_GTK3_CITYDLG_MIN_XSIZE,
3338            gtk_widget_get_allocated_height(pdialog->shell),
3339            GUI_GTK3_CITYDLG_MAX_XSIZE);
3340 
3341   last_page
3342     = gtk_notebook_get_current_page(GTK_NOTEBOOK(pdialog->notebook));
3343 
3344   if (pdialog->popup_menu) {
3345     gtk_widget_destroy(pdialog->popup_menu);
3346   }
3347 
3348   dialog_list_remove(dialog_list, pdialog);
3349 
3350   unit_node_vector_free(&pdialog->overview.supported_units);
3351   unit_node_vector_free(&pdialog->overview.present_units);
3352 
3353   if (pdialog->buy_shell) {
3354     gtk_widget_destroy(pdialog->buy_shell);
3355   }
3356   if (pdialog->sell_shell) {
3357     gtk_widget_destroy(pdialog->sell_shell);
3358   }
3359   if (pdialog->rename_shell) {
3360     gtk_widget_destroy(pdialog->rename_shell);
3361   }
3362 
3363   cairo_surface_destroy(pdialog->map_canvas_store_unscaled);
3364   cairo_surface_destroy(pdialog->citizen_surface);
3365 
3366   free(pdialog);
3367 
3368   /* need to do this every time a new dialog is closed. */
3369   city_dialog_update_prev_next();
3370 }
3371 
3372 /************************************************************************
3373   Close city dialog
3374 *************************************************************************/
close_city_dialog(struct city_dialog * pdialog)3375 static void close_city_dialog(struct city_dialog *pdialog)
3376 {
3377   gtk_widget_destroy(pdialog->shell);
3378 }
3379 
3380 /************************************************************************
3381   Callback for the prev/next buttons. Switches to the previous/next
3382   city.
3383 *************************************************************************/
switch_city_callback(GtkWidget * w,gpointer data)3384 static void switch_city_callback(GtkWidget *w, gpointer data)
3385 {
3386   struct city_dialog *pdialog = (struct city_dialog *) data;
3387   int i, j, dir, size;
3388   struct city *new_pcity = NULL;
3389 
3390   if (client_is_global_observer()) {
3391     return;
3392   }
3393 
3394   size = city_list_size(client.conn.playing->cities);
3395 
3396   fc_assert_ret(city_dialogs_have_been_initialised);
3397   fc_assert_ret(size >= 1);
3398   fc_assert_ret(city_owner(pdialog->pcity) == client.conn.playing);
3399 
3400   if (size == 1) {
3401     return;
3402   }
3403 
3404   /* dir = 1 will advance to the city, dir = -1 will get previous */
3405   if (w == pdialog->next_command) {
3406     dir = 1;
3407   } else if (w == pdialog->prev_command) {
3408     dir = -1;
3409   } else {
3410     /* Always fails. */
3411     fc_assert_ret(w == pdialog->next_command
3412                   || w == pdialog->prev_command);
3413     dir = 1;
3414   }
3415 
3416   for (i = 0; i < size; i++) {
3417     if (pdialog->pcity == city_list_get(client.conn.playing->cities, i)) {
3418       break;
3419     }
3420   }
3421 
3422   fc_assert_ret(i < size);
3423 
3424   for (j = 1; j < size; j++) {
3425     struct city *other_pcity = city_list_get(client.conn.playing->cities,
3426 					     (i + dir * j + size) % size);
3427     struct city_dialog *other_pdialog = get_city_dialog(other_pcity);
3428 
3429     fc_assert_ret(other_pdialog != pdialog);
3430     if (!other_pdialog) {
3431       new_pcity = other_pcity;
3432       break;
3433     }
3434   }
3435 
3436   if (!new_pcity) {
3437     /* Every other city has an open city dialog. */
3438     return;
3439   }
3440 
3441   /* cleanup happiness dialog */
3442   if (game.info.citizen_nationality) {
3443     citizens_dialog_close(pdialog->pcity);
3444   }
3445   close_happiness_dialog(pdialog->pcity);
3446 
3447   pdialog->pcity = new_pcity;
3448 
3449   /* reinitialize happiness, and cma dialogs */
3450   if (game.info.citizen_nationality) {
3451     gtk_container_add(GTK_CONTAINER(pdialog->happiness.citizens),
3452                       citizens_dialog_display(pdialog->pcity));
3453   }
3454   gtk_container_add(GTK_CONTAINER(pdialog->happiness.widget),
3455                     get_top_happiness_display(pdialog->pcity, low_citydlg, pdialog->shell));
3456   if (!client_is_observer()) {
3457     fc_assert(pdialog->cma_editor != NULL);
3458     pdialog->cma_editor->pcity = new_pcity;
3459   }
3460 
3461   reset_city_worklist(pdialog->production.worklist, pdialog->pcity);
3462 
3463   can_slide = FALSE;
3464   center_tile_mapcanvas(pdialog->pcity->tile);
3465   can_slide = TRUE;
3466   if (!client_is_observer()) {
3467     set_cityopt_values(pdialog);  /* need not be in real_city_dialog_refresh */
3468   }
3469 
3470   real_city_dialog_refresh(pdialog->pcity);
3471 
3472   /* recenter the city map(s) */
3473   city_dialog_map_recenter(pdialog->overview.map_canvas.sw);
3474   if (pdialog->happiness.map_canvas.sw) {
3475     city_dialog_map_recenter(pdialog->happiness.map_canvas.sw);
3476   }
3477 }
3478 
3479 /**************************************************************************
3480   City is about to disappear from client
3481 **************************************************************************/
city_to_disappear(struct city * pcity)3482 void city_to_disappear(struct city *pcity)
3483 {
3484 }
3485