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