Qt client - remove right click menu from end turn sidebar
[freeciv.git] / client / gui-gtk-3.0 / citydlg.c
blob01d6f4e86803ee127cf4edbf5c47b4867f8dcc75
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.
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 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
22 #include <gtk/gtk.h>
23 #include <gdk/gdkkeysyms.h>
25 /* utility */
26 #include "bitvector.h"
27 #include "fcintl.h"
28 #include "log.h"
29 #include "mem.h"
30 #include "shared.h"
31 #include "support.h"
33 /* common */
34 #include "city.h"
35 #include "game.h"
36 #include "map.h"
37 #include "movement.h"
38 #include "packets.h"
39 #include "player.h"
40 #include "unitlist.h"
42 /* client */
43 #include "chatline_common.h"
44 #include "client_main.h"
45 #include "colors.h"
46 #include "control.h"
47 #include "climap.h"
48 #include "options.h"
49 #include "text.h"
50 #include "tilespec.h"
52 /* client/agents */
53 #include "cma_fec.h"
55 /* client/gui-gtk-3.0 */
56 #include "choice_dialog.h"
57 #include "citizensinfo.h"
58 #include "cityrep.h"
59 #include "cma_fe.h"
60 #include "dialogs.h"
61 #include "graphics.h"
62 #include "gui_main.h"
63 #include "gui_stuff.h"
64 #include "happiness.h"
65 #include "helpdlg.h"
66 #include "inputdlg.h"
67 #include "mapview.h"
68 #include "repodlgs.h"
69 #include "wldlg.h"
71 #include "citydlg.h"
73 #define CITYMAP_WIDTH MIN(512, canvas_width)
74 #define CITYMAP_HEIGHT (CITYMAP_WIDTH * canvas_height / canvas_width)
75 #define CITYMAP_SCALE ((double)CITYMAP_WIDTH / (double)canvas_width)
77 #define TINYSCREEN_MAX_HEIGHT (500 - 1)
79 /* Only CDLGR_UNITS button currently uses these, others have
80 * direct callback. */
81 enum citydlg_response { CDLGR_UNITS, CDLGR_PREV, CDLGR_NEXT };
83 struct city_dialog;
85 /* get 'struct dialog_list' and related function */
86 #define SPECLIST_TAG dialog
87 #define SPECLIST_TYPE struct city_dialog
88 #include "speclist.h"
90 #define dialog_list_iterate(dialoglist, pdialog) \
91 TYPED_LIST_ITERATE(struct city_dialog, dialoglist, pdialog)
92 #define dialog_list_iterate_end LIST_ITERATE_END
94 struct unit_node {
95 GtkWidget *cmd;
96 GtkWidget *pix;
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"
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
108 enum { OVERVIEW_PAGE, MAP_PAGE, BUILDINGS_PAGE, WORKLIST_PAGE,
109 HAPPINESS_PAGE, CMA_PAGE, TRADE_PAGE, MISC_PAGE
112 #define NUM_CITIZENS_SHOWN 30
113 #define NUM_INFO_FIELDS 13 /* number of fields in city_info */
114 #define NUM_PAGES 6 /* the number of pages in city dialog notebook
115 * (+1) if you change this, you must add an
116 * entry to misc_whichtab_label[] */
118 /* minimal size for the city map scrolling windows*/
119 #define CITY_MAP_MIN_SIZE_X 200
120 #define CITY_MAP_MIN_SIZE_Y 150
122 struct city_map_canvas {
123 GtkWidget *sw;
124 GtkWidget *ebox;
125 GtkWidget *darea;
128 struct city_dialog {
129 struct city *pcity;
131 GtkWidget *shell;
132 GtkWidget *name_label;
133 cairo_surface_t *map_canvas_store_unscaled;
134 GtkWidget *notebook;
136 GtkWidget *popup_menu;
137 GtkWidget *citizen_pixmap;
139 struct {
140 struct city_map_canvas map_canvas;
142 GtkWidget *production_bar;
143 GtkWidget *production_combo;
144 GtkWidget *buy_command;
145 GtkWidget *improvement_list;
147 GtkWidget *supported_units_frame;
148 GtkWidget *supported_unit_table;
150 GtkWidget *present_units_frame;
151 GtkWidget *present_unit_table;
153 struct unit_node_vector supported_units;
154 struct unit_node_vector present_units;
156 GtkWidget *info_ebox[NUM_INFO_FIELDS];
157 GtkWidget *info_label[NUM_INFO_FIELDS];
159 GtkListStore* change_production_store;
160 } overview;
162 struct {
163 GtkWidget *production_label;
164 GtkWidget *production_bar;
165 GtkWidget *buy_command;
166 GtkWidget *worklist;
167 } production;
169 struct {
170 struct city_map_canvas map_canvas;
172 GtkWidget *widget;
173 GtkWidget *info_ebox[NUM_INFO_FIELDS];
174 GtkWidget *info_label[NUM_INFO_FIELDS];
175 GtkWidget *citizens;
176 } happiness;
178 struct cma_dialog *cma_editor;
180 struct {
181 GtkWidget *rename_command;
182 GtkWidget *new_citizens_radio[3];
183 GtkWidget *disband_on_settler;
184 GtkWidget *whichtab_radio[NUM_PAGES];
185 short block_signal;
186 } misc;
188 GtkWidget *buy_shell, *sell_shell;
189 GtkTreeSelection *change_selection;
190 GtkWidget *rename_shell, *rename_input;
192 GtkWidget *show_units_command;
193 GtkWidget *prev_command, *next_command;
195 Impr_type_id sell_id;
197 int cwidth;
200 static struct dialog_list *dialog_list;
201 static bool city_dialogs_have_been_initialised = FALSE;
202 static int canvas_width, canvas_height;
203 static int new_dialog_def_page = OVERVIEW_PAGE;
204 static int last_page = OVERVIEW_PAGE;
206 static bool is_showing_workertask_dialog = FALSE;
208 static struct
210 struct city *owner;
211 struct tile *loc;
212 } workertask_req;
214 static bool low_citydlg;
216 /****************************************/
218 static void initialize_city_dialogs(void);
219 static void city_dialog_map_create(struct city_dialog *pdialog,
220 struct city_map_canvas *cmap_canvas);
221 static void city_dialog_map_recenter(GtkWidget *map_canvas_sw);
223 static struct city_dialog *get_city_dialog(struct city *pcity);
224 static gboolean keyboard_handler(GtkWidget * widget, GdkEventKey * event,
225 struct city_dialog *pdialog);
227 static GtkWidget *create_city_info_table(struct city_dialog *pdialog,
228 GtkWidget **info_ebox,
229 GtkWidget **info_label);
230 static void create_and_append_overview_page(struct city_dialog *pdialog);
231 static void create_and_append_map_page(struct city_dialog *pdialog);
232 static void create_and_append_buildings_page(struct city_dialog *pdialog);
233 static void create_and_append_worklist_page(struct city_dialog *pdialog);
234 static void create_and_append_happiness_page(struct city_dialog *pdialog);
235 static void create_and_append_cma_page(struct city_dialog *pdialog);
236 static void create_and_append_settings_page(struct city_dialog *pdialog);
238 static struct city_dialog *create_city_dialog(struct city *pcity);
240 static void city_dialog_update_title(struct city_dialog *pdialog);
241 static void city_dialog_update_citizens(struct city_dialog *pdialog);
242 static void city_dialog_update_information(GtkWidget **info_ebox,
243 GtkWidget **info_label,
244 struct city_dialog *pdialog);
245 static void city_dialog_update_map(struct city_dialog *pdialog);
246 static void city_dialog_update_building(struct city_dialog *pdialog);
247 static void city_dialog_update_improvement_list(struct city_dialog
248 *pdialog);
249 static void city_dialog_update_supported_units(struct city_dialog
250 *pdialog);
251 static void city_dialog_update_present_units(struct city_dialog *pdialog);
252 static void city_dialog_update_prev_next(void);
254 static void show_units_response(void *data);
256 static gboolean supported_unit_callback(GtkWidget * w, GdkEventButton * ev,
257 gpointer data);
258 static gboolean present_unit_callback(GtkWidget * w, GdkEventButton * ev,
259 gpointer data);
260 static gboolean supported_unit_middle_callback(GtkWidget * w,
261 GdkEventButton * ev,
262 gpointer data);
263 static gboolean present_unit_middle_callback(GtkWidget * w,
264 GdkEventButton * ev,
265 gpointer data);
267 static void unit_center_callback(GtkWidget * w, gpointer data);
268 static void unit_activate_callback(GtkWidget * w, gpointer data);
269 static void supported_unit_activate_close_callback(GtkWidget * w,
270 gpointer data);
271 static void present_unit_activate_close_callback(GtkWidget * w,
272 gpointer data);
273 static void unit_load_callback(GtkWidget * w, gpointer data);
274 static void unit_unload_callback(GtkWidget * w, gpointer data);
275 static void unit_sentry_callback(GtkWidget * w, gpointer data);
276 static void unit_fortify_callback(GtkWidget * w, gpointer data);
277 static void unit_disband_callback(GtkWidget * w, gpointer data);
278 static void unit_homecity_callback(GtkWidget * w, gpointer data);
279 static void unit_upgrade_callback(GtkWidget * w, gpointer data);
281 static gboolean citizens_callback(GtkWidget * w, GdkEventButton * ev,
282 gpointer data);
283 static gboolean button_down_citymap(GtkWidget * w, GdkEventButton * ev,
284 gpointer data);
285 static void draw_map_canvas(struct city_dialog *pdialog);
287 static void buy_callback(GtkWidget * w, gpointer data);
288 static void change_production_callback(GtkComboBox *combo,
289 struct city_dialog *pdialog);
291 static void sell_callback(struct impr_type *pimprove, gpointer data);
292 static void sell_callback_response(GtkWidget *w, gint response, gpointer data);
294 static void impr_callback(GtkTreeView *view, GtkTreePath *path,
295 GtkTreeViewColumn *col, gpointer data);
297 static void rename_callback(GtkWidget * w, gpointer data);
298 static void rename_popup_callback(gpointer data, gint response,
299 const char *input);
300 static void set_cityopt_values(struct city_dialog *pdialog);
301 static void cityopt_callback(GtkWidget * w, gpointer data);
302 static void misc_whichtab_callback(GtkWidget * w, gpointer data);
304 static void city_destroy_callback(GtkWidget *w, gpointer data);
305 static void close_city_dialog(struct city_dialog *pdialog);
306 static void citydlg_response_callback(GtkDialog *dlg, gint response,
307 void *data);
308 static void close_callback(GtkWidget *w, gpointer data);
309 static void switch_city_callback(GtkWidget *w, gpointer data);
311 /****************************************************************
312 Called to set the dimensions of the city dialog, both on
313 startup and if the tileset is changed.
314 *****************************************************************/
315 static void init_citydlg_dimensions(void)
317 canvas_width = get_citydlg_canvas_width();
318 canvas_height = get_citydlg_canvas_height();
321 /****************************************************************
322 Initialize stuff needed for city dialogs
323 *****************************************************************/
324 static void initialize_city_dialogs(void)
326 int height;
328 fc_assert_ret(!city_dialogs_have_been_initialised);
330 dialog_list = dialog_list_new();
331 init_citydlg_dimensions();
332 height = screen_height();
334 /* Use default layout when height cannot be determined
335 * (when height == 0) */
336 if (height > 0 && height <= TINYSCREEN_MAX_HEIGHT) {
337 low_citydlg = TRUE;
338 } else {
339 low_citydlg = FALSE;
342 city_dialogs_have_been_initialised = TRUE;
345 /****************************************************************
346 Called when the tileset changes.
347 *****************************************************************/
348 void reset_city_dialogs(void)
350 if (!city_dialogs_have_been_initialised) {
351 return;
354 init_citydlg_dimensions();
356 dialog_list_iterate(dialog_list, pdialog) {
357 /* There's no reasonable way to resize a GtkPixcomm, so we don't try.
358 Instead we just redraw the overview within the existing area. The
359 player has to close and reopen the dialog to fix this. */
360 city_dialog_update_map(pdialog);
361 } dialog_list_iterate_end;
363 popdown_all_city_dialogs();
366 /****************************************************************
367 Return city dialog of the given city, or NULL is it doesn't
368 already exist
369 *****************************************************************/
370 static struct city_dialog *get_city_dialog(struct city *pcity)
372 if (!city_dialogs_have_been_initialised) {
373 initialize_city_dialogs();
376 dialog_list_iterate(dialog_list, pdialog) {
377 if (pdialog->pcity == pcity)
378 return pdialog;
380 dialog_list_iterate_end;
381 return NULL;
384 /***************************************************************************
385 Redraw map canvas on expose.
386 ****************************************************************************/
387 static gboolean canvas_exposed_cb(GtkWidget *w, cairo_t *cr,
388 gpointer data)
390 struct city_dialog *pdialog = data;
392 cairo_scale(cr, CITYMAP_SCALE, CITYMAP_SCALE);
393 cairo_set_source_surface(cr, pdialog->map_canvas_store_unscaled, 0, 0);
394 if (!gtk_widget_get_sensitive(pdialog->overview.map_canvas.ebox)) {
395 cairo_paint_with_alpha(cr, 0.5);
396 } else {
397 cairo_paint(cr);
400 return TRUE;
403 /***************************************************************************
404 Create a city map widget; used in the overview and in the happiness page.
405 ****************************************************************************/
406 static void city_dialog_map_create(struct city_dialog *pdialog,
407 struct city_map_canvas *cmap_canvas)
409 GtkWidget *sw, *ebox, *darea;
411 sw = gtk_scrolled_window_new(NULL, NULL);
412 gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(sw),
413 CITYMAP_WIDTH);
414 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw),
415 CITYMAP_HEIGHT);
416 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
417 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
418 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
419 GTK_SHADOW_NONE);
421 ebox = gtk_event_box_new();
422 gtk_widget_set_halign(ebox, GTK_ALIGN_CENTER);
423 gtk_widget_set_valign(ebox, GTK_ALIGN_CENTER);
424 gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
425 gtk_container_add(GTK_CONTAINER(sw), ebox);
427 darea = gtk_drawing_area_new();
428 gtk_widget_add_events(darea, GDK_BUTTON_PRESS_MASK);
429 gtk_widget_set_size_request(darea, CITYMAP_WIDTH, CITYMAP_HEIGHT);
430 g_signal_connect(ebox, "button-press-event",
431 G_CALLBACK(button_down_citymap), pdialog);
432 g_signal_connect(darea, "draw",
433 G_CALLBACK(canvas_exposed_cb), pdialog);
434 gtk_container_add(GTK_CONTAINER(ebox), darea);
436 /* save all widgets for the city map */
437 cmap_canvas->sw = sw;
438 cmap_canvas->ebox = ebox;
439 cmap_canvas->darea = darea;
442 /****************************************************************
443 Center city dialog map.
444 *****************************************************************/
445 static void city_dialog_map_recenter(GtkWidget *map_canvas_sw) {
446 GtkAdjustment *adjust = NULL;
447 gdouble value;
449 fc_assert_ret(map_canvas_sw != NULL);
451 adjust = gtk_scrolled_window_get_hadjustment(
452 GTK_SCROLLED_WINDOW(map_canvas_sw));
453 value = (gtk_adjustment_get_lower(adjust)
454 + gtk_adjustment_get_upper(adjust)
455 - gtk_adjustment_get_page_size(adjust)) / 2;
456 gtk_adjustment_set_value(adjust, value);
457 gtk_adjustment_value_changed(adjust);
459 adjust = gtk_scrolled_window_get_vadjustment(
460 GTK_SCROLLED_WINDOW(map_canvas_sw));
461 value = (gtk_adjustment_get_lower(adjust)
462 + gtk_adjustment_get_upper(adjust)
463 - gtk_adjustment_get_page_size(adjust)) / 2;
464 gtk_adjustment_set_value(adjust, value);
465 gtk_adjustment_value_changed(adjust);
468 /****************************************************************
469 Refresh city dialog of the given city
470 *****************************************************************/
471 void real_city_dialog_refresh(struct city *pcity)
473 struct city_dialog *pdialog = get_city_dialog(pcity);
475 log_debug("CITYMAP_WIDTH: %d", CITYMAP_WIDTH);
476 log_debug("CITYMAP_HEIGHT: %d", CITYMAP_HEIGHT);
477 log_debug("CITYMAP_SCALE: %.3f", CITYMAP_SCALE);
479 if (city_owner(pcity) == client.conn.playing) {
480 city_report_dialog_update_city(pcity);
481 economy_report_dialog_update();
484 if (!pdialog)
485 return;
487 city_dialog_update_title(pdialog);
488 city_dialog_update_citizens(pdialog);
489 city_dialog_update_information(pdialog->overview.info_ebox,
490 pdialog->overview.info_label, pdialog);
491 city_dialog_update_map(pdialog);
492 city_dialog_update_building(pdialog);
493 city_dialog_update_improvement_list(pdialog);
494 city_dialog_update_supported_units(pdialog);
495 city_dialog_update_present_units(pdialog);
497 if (!client_has_player() || city_owner(pcity) == client_player()) {
498 bool have_present_units = (unit_list_size(pcity->tile->units) > 0);
500 refresh_worklist(pdialog->production.worklist);
502 if (!low_citydlg) {
503 city_dialog_update_information(pdialog->happiness.info_ebox,
504 pdialog->happiness.info_label, pdialog);
506 refresh_happiness_dialog(pdialog->pcity);
507 if (game.info.citizen_nationality) {
508 citizens_dialog_refresh(pdialog->pcity);
511 if (!client_is_observer()) {
512 refresh_cma_dialog(pdialog->pcity, REFRESH_ALL);
515 gtk_widget_set_sensitive(pdialog->show_units_command,
516 can_client_issue_orders() &&
517 have_present_units);
518 } else {
519 /* Set the buttons we do not want live while a Diplomat investigates */
520 gtk_widget_set_sensitive(pdialog->show_units_command, FALSE);
524 /****************************************************************
525 Refresh city dialogs of unit's homecity and city where unit
526 currently is.
527 *****************************************************************/
528 void refresh_unit_city_dialogs(struct unit *punit)
530 struct city *pcity_sup, *pcity_pre;
531 struct city_dialog *pdialog;
533 pcity_sup = game_city_by_number(punit->homecity);
534 pcity_pre = tile_city(unit_tile(punit));
536 if (pcity_sup && (pdialog = get_city_dialog(pcity_sup))) {
537 city_dialog_update_supported_units(pdialog);
540 if (pcity_pre && (pdialog = get_city_dialog(pcity_pre))) {
541 city_dialog_update_present_units(pdialog);
545 /****************************************************************
546 popup the dialog 10% inside the main-window
547 *****************************************************************/
548 void real_city_dialog_popup(struct city *pcity)
550 struct city_dialog *pdialog;
552 if (!(pdialog = get_city_dialog(pcity))) {
553 pdialog = create_city_dialog(pcity);
556 gtk_window_present(GTK_WINDOW(pdialog->shell));
558 /* center the city map(s); this must be *after* the city dialog was drawn
559 * else the size information is missing! */
560 city_dialog_map_recenter(pdialog->overview.map_canvas.sw);
561 if (pdialog->happiness.map_canvas.sw) {
562 city_dialog_map_recenter(pdialog->happiness.map_canvas.sw);
566 /****************************************************************
567 Return whether city dialog for given city is open
568 *****************************************************************/
569 bool city_dialog_is_open(struct city *pcity)
571 return get_city_dialog(pcity) != NULL;
574 /****************************************************************
575 popdown the dialog
576 *****************************************************************/
577 void popdown_city_dialog(struct city *pcity)
579 struct city_dialog *pdialog = get_city_dialog(pcity);
581 if (pdialog) {
582 close_city_dialog(pdialog);
586 /****************************************************************
587 popdown all dialogs
588 *****************************************************************/
589 void popdown_all_city_dialogs(void)
591 if (!city_dialogs_have_been_initialised) {
592 return;
595 while (dialog_list_size(dialog_list)) {
596 close_city_dialog(dialog_list_get(dialog_list, 0));
598 dialog_list_destroy(dialog_list);
600 city_dialogs_have_been_initialised = FALSE;
603 /**************************************************************************
604 Keyboard handler for city dialog
605 **************************************************************************/
606 static gboolean keyboard_handler(GtkWidget * widget, GdkEventKey * event,
607 struct city_dialog *pdialog)
609 if (event->state & GDK_CONTROL_MASK) {
610 switch (event->keyval) {
611 case GDK_KEY_Left:
612 gtk_notebook_prev_page(GTK_NOTEBOOK(pdialog->notebook));
613 return TRUE;
615 case GDK_KEY_Right:
616 gtk_notebook_next_page(GTK_NOTEBOOK(pdialog->notebook));
617 return TRUE;
619 default:
620 break;
624 return FALSE;
627 /**************************************************************************
628 Destroy info popup dialog when button released
629 **************************************************************************/
630 static gboolean show_info_button_release(GtkWidget *w, GdkEventButton *ev,
631 gpointer data)
633 gtk_grab_remove(w);
634 gdk_device_ungrab(ev->device, ev->time);
635 gtk_widget_destroy(w);
636 return FALSE;
639 enum { FIELD_FOOD, FIELD_SHIELD, FIELD_TRADE, FIELD_GOLD, FIELD_LUXURY,
640 FIELD_SCIENCE, FIELD_GRANARY, FIELD_GROWTH, FIELD_CORRUPTION,
641 FIELD_WASTE, FIELD_CULTURE, FIELD_POLLUTION, FIELD_ILLNESS
644 /****************************************************************
645 Popup info dialog
646 *****************************************************************/
647 static gboolean show_info_popup(GtkWidget *w, GdkEventButton *ev,
648 gpointer data)
650 struct city_dialog *pdialog = g_object_get_data(G_OBJECT(w), "pdialog");
652 if (ev->button == 1) {
653 GtkWidget *p, *label, *frame;
654 char buf[1024];
656 switch (GPOINTER_TO_UINT(data)) {
657 case FIELD_FOOD:
658 get_city_dialog_output_text(pdialog->pcity, O_FOOD, buf, sizeof(buf));
659 break;
660 case FIELD_SHIELD:
661 get_city_dialog_output_text(pdialog->pcity, O_SHIELD,
662 buf, sizeof(buf));
663 break;
664 case FIELD_TRADE:
665 get_city_dialog_output_text(pdialog->pcity, O_TRADE, buf, sizeof(buf));
666 break;
667 case FIELD_GOLD:
668 get_city_dialog_output_text(pdialog->pcity, O_GOLD, buf, sizeof(buf));
669 break;
670 case FIELD_SCIENCE:
671 get_city_dialog_output_text(pdialog->pcity, O_SCIENCE,
672 buf, sizeof(buf));
673 break;
674 case FIELD_LUXURY:
675 get_city_dialog_output_text(pdialog->pcity, O_LUXURY,
676 buf, sizeof(buf));
677 break;
678 case FIELD_CULTURE:
679 get_city_dialog_culture_text(pdialog->pcity, buf, sizeof(buf));
680 break;
681 case FIELD_POLLUTION:
682 get_city_dialog_pollution_text(pdialog->pcity, buf, sizeof(buf));
683 break;
684 case FIELD_ILLNESS:
685 get_city_dialog_illness_text(pdialog->pcity, buf, sizeof(buf));
686 break;
687 default:
688 return TRUE;
691 p = gtk_window_new(GTK_WINDOW_POPUP);
692 gtk_widget_set_name(p, "Freeciv");
693 gtk_container_set_border_width(GTK_CONTAINER(p), 2);
694 gtk_window_set_position(GTK_WINDOW(p), GTK_WIN_POS_MOUSE);
696 frame = gtk_frame_new(NULL);
697 gtk_container_add(GTK_CONTAINER(p), frame);
699 label = gtk_label_new(buf);
700 gtk_widget_set_name(label, "city_info_label");
701 gtk_widget_set_margin_left(label, 4);
702 gtk_widget_set_margin_right(label, 4);
703 gtk_widget_set_margin_top(label, 4);
704 gtk_widget_set_margin_bottom(label, 4);
705 gtk_container_add(GTK_CONTAINER(frame), label);
706 gtk_widget_show_all(p);
708 gdk_device_grab(ev->device, gtk_widget_get_window(p),
709 GDK_OWNERSHIP_NONE, TRUE, GDK_BUTTON_RELEASE_MASK, NULL,
710 ev->time);
711 gtk_grab_add(p);
713 g_signal_connect_after(p, "button_release_event",
714 G_CALLBACK(show_info_button_release), NULL);
716 return TRUE;
719 /****************************************************************
720 used once in the overview page and once in the happiness page
721 **info_label points to the info_label in the respective struct
722 ****************************************************************/
723 static GtkWidget *create_city_info_table(struct city_dialog *pdialog,
724 GtkWidget **info_ebox,
725 GtkWidget **info_label)
727 int i;
728 GtkWidget *table, *label, *ebox;
730 static const char *output_label[NUM_INFO_FIELDS] = { N_("Food:"),
731 N_("Prod:"),
732 N_("Trade:"),
733 N_("Gold:"),
734 N_("Luxury:"),
735 N_("Science:"),
736 N_("Granary:"),
737 N_("Change in:"),
738 N_("Corruption:"),
739 N_("Waste:"),
740 N_("Culture:"),
741 N_("Pollution:"),
742 N_("Plague Risk:")
744 static bool output_label_done;
746 table = gtk_grid_new();
747 g_object_set(table, "margin", 4, NULL);
749 intl_slist(ARRAY_SIZE(output_label), output_label, &output_label_done);
751 for (i = 0; i < NUM_INFO_FIELDS; i++) {
752 label = gtk_label_new(output_label[i]);
753 switch (i) {
754 case 2:
755 case 5:
756 case 7:
757 gtk_widget_set_margin_bottom(label, 5);
758 break;
759 case 3:
760 case 6:
761 case 8:
762 gtk_widget_set_margin_top(label, 5);
763 break;
764 default:
765 break;
767 gtk_widget_set_margin_right(label, 5);
768 gtk_widget_set_name(label, "city_label"); /* for font style? */
769 gtk_widget_set_halign(label, GTK_ALIGN_START);
770 gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
771 gtk_grid_attach(GTK_GRID(table), label, 0, i, 1, 1);
773 ebox = gtk_event_box_new();
774 switch (i) {
775 case 2:
776 case 5:
777 case 7:
778 gtk_widget_set_margin_bottom(ebox, 5);
779 break;
780 case 3:
781 case 6:
782 case 8:
783 gtk_widget_set_margin_top(ebox, 5);
784 break;
785 default:
786 break;
788 gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
789 g_object_set_data(G_OBJECT(ebox), "pdialog", pdialog);
790 g_signal_connect(ebox, "button_press_event",
791 G_CALLBACK(show_info_popup), GUINT_TO_POINTER(i));
792 info_ebox[i] = ebox;
794 label = gtk_label_new("");
795 info_label[i] = label;
796 gtk_widget_set_name(label, "city_label"); /* ditto */
797 gtk_widget_set_halign(label, GTK_ALIGN_START);
798 gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
800 gtk_container_add(GTK_CONTAINER(ebox), label);
802 gtk_grid_attach(GTK_GRID(table), ebox, 1, i, 1, 1);
805 gtk_widget_show_all(table);
807 return table;
810 /****************************************************************
811 Create main citydlg map
812 *****************************************************************/
813 static void create_citydlg_main_map(struct city_dialog *pdialog,
814 GtkWidget *container)
816 GtkWidget *frame;
818 frame = gtk_frame_new(_("City map"));
819 gtk_widget_set_size_request(frame, CITY_MAP_MIN_SIZE_X,
820 CITY_MAP_MIN_SIZE_Y);
821 gtk_container_add(GTK_CONTAINER(container), frame);
823 city_dialog_map_create(pdialog, &pdialog->overview.map_canvas);
824 gtk_container_add(GTK_CONTAINER(frame), pdialog->overview.map_canvas.sw);
827 /****************************************************************
828 Create improvements list
829 *****************************************************************/
830 static GtkWidget *create_citydlg_improvement_list(struct city_dialog *pdialog,
831 GtkWidget *vbox)
833 GtkWidget *view;
834 GtkListStore *store;
835 GtkCellRenderer *rend;
837 /* improvements */
838 store = gtk_list_store_new(5, G_TYPE_POINTER, GDK_TYPE_PIXBUF,
839 G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
841 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
842 gtk_widget_set_hexpand(view, TRUE);
843 gtk_widget_set_vexpand(view, TRUE);
844 g_object_unref(store);
845 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
846 gtk_widget_set_name(view, "small_font");
847 pdialog->overview.improvement_list = view;
849 gtk_widget_set_tooltip_markup(view,
850 _("Press <b>ENTER</b> or double-click to sell an improvement."));
852 rend = gtk_cell_renderer_pixbuf_new();
853 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL,
854 rend, "pixbuf", 1, NULL);
855 rend = gtk_cell_renderer_text_new();
856 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL,
857 rend, "text", 2,
858 "strikethrough", 4, NULL);
859 rend = gtk_cell_renderer_text_new();
860 g_object_set(rend, "xalign", 1.0, NULL);
861 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL,
862 rend, "text", 3,
863 "strikethrough", 4, NULL);
865 g_signal_connect(view, "row_activated", G_CALLBACK(impr_callback),
866 pdialog);
868 return view;
871 /****************************************************************
872 **** Overview page ****
873 +- GtkWidget *page ------------------------------------------+
874 | +- GtkWidget *middle -----------+------------------------+ |
875 | | City map | Production | |
876 | +-------------------------------+------------------------+ |
877 +------------------------------------------------------------+
878 | +- GtkWidget *bottom -------+----------------------------+ |
879 | | Info | +- GtkWidget *right -----+ | |
880 | | | | supported units | | |
881 | | | +------------------------+ | |
882 | | | | present units | | |
883 | | | +------------------------+ | |
884 | +---------------------------+----------------------------+ |
885 +------------------------------------------------------------+
886 *****************************************************************/
887 static void create_and_append_overview_page(struct city_dialog *pdialog)
889 GtkWidget *page, *bottom;
890 GtkWidget *hbox, *right, *vbox, *frame, *table;
891 GtkWidget *label, *sw, *view, *bar, *production_combo;
892 GtkCellRenderer *rend;
893 GtkListStore *production_store;
894 /* TRANS: Overview tab in city dialog */
895 const char *tab_title = _("_Overview");
896 int unit_height = tileset_unit_with_upkeep_height(tileset);
898 /* main page */
899 page = gtk_grid_new();
900 gtk_orientable_set_orientation(GTK_ORIENTABLE(page),
901 GTK_ORIENTATION_VERTICAL);
902 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
903 label = gtk_label_new_with_mnemonic(tab_title);
904 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
906 if (!low_citydlg) {
907 GtkWidget *middle;
909 /* middle: city map, improvements */
910 middle = gtk_grid_new();
911 gtk_grid_set_column_spacing(GTK_GRID(middle), 6);
912 gtk_container_add(GTK_CONTAINER(page), middle);
914 /* city map */
915 create_citydlg_main_map(pdialog, middle);
917 /* improvements */
918 vbox = gtk_grid_new();
919 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox),
920 GTK_ORIENTATION_VERTICAL);
921 gtk_container_add(GTK_CONTAINER(middle), vbox);
923 view = create_citydlg_improvement_list(pdialog, middle);
925 label = g_object_new(GTK_TYPE_LABEL, "label", _("Production:"),
926 "xalign", 0.0, "yalign", 0.5, NULL);
927 gtk_container_add(GTK_CONTAINER(vbox), label);
929 hbox = gtk_grid_new();
930 gtk_grid_set_column_spacing(GTK_GRID(hbox), 10);
931 gtk_container_add(GTK_CONTAINER(vbox), hbox);
933 production_store = gtk_list_store_new(4, GDK_TYPE_PIXBUF, G_TYPE_STRING,
934 G_TYPE_INT, G_TYPE_BOOLEAN);
935 pdialog->overview.change_production_store = production_store;
937 production_combo =
938 gtk_combo_box_new_with_model(GTK_TREE_MODEL(production_store));
939 gtk_widget_set_hexpand(production_combo, TRUE);
940 pdialog->overview.production_combo = production_combo;
941 gtk_container_add(GTK_CONTAINER(hbox), production_combo);
942 g_object_unref(production_store);
943 g_signal_connect(production_combo, "changed",
944 G_CALLBACK(change_production_callback), pdialog);
946 gtk_cell_layout_clear(GTK_CELL_LAYOUT(production_combo));
947 rend = gtk_cell_renderer_pixbuf_new();
948 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(production_combo), rend, TRUE);
949 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(production_combo),
950 rend, "pixbuf", 0, NULL);
951 g_object_set(rend, "xalign", 0.0, NULL);
953 rend = gtk_cell_renderer_text_new();
954 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(production_combo), rend, TRUE);
955 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(production_combo),
956 rend, "text", 1, "strikethrough", 3, NULL);
958 bar = gtk_progress_bar_new();
959 gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(bar), TRUE);
960 pdialog->overview.production_bar = bar;
961 gtk_container_add(GTK_CONTAINER(production_combo), bar);
962 gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(production_combo), 3);
964 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(bar), _("%d/%d %d turns"));
966 pdialog->overview.buy_command = gtk_stockbutton_new(GTK_STOCK_EXECUTE,
967 _("_Buy"));
968 gtk_container_add(GTK_CONTAINER(hbox), pdialog->overview.buy_command);
969 g_signal_connect(pdialog->overview.buy_command, "clicked",
970 G_CALLBACK(buy_callback), pdialog);
972 label = g_object_new(GTK_TYPE_LABEL, "use-underline", TRUE,
973 "mnemonic-widget", view,
974 "label", _("I_mprovements:"),
975 "xalign", 0.0, "yalign", 0.5, NULL);
976 gtk_container_add(GTK_CONTAINER(vbox), label);
978 sw = gtk_scrolled_window_new(NULL, NULL);
979 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
980 GTK_SHADOW_ETCHED_IN);
981 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
982 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
983 gtk_container_add(GTK_CONTAINER(vbox), sw);
985 gtk_container_add(GTK_CONTAINER(sw), view);
986 } else {
987 pdialog->overview.buy_command = NULL;
988 pdialog->overview.production_bar = NULL;
989 pdialog->overview.production_combo = NULL;
990 pdialog->overview.change_production_store = NULL;
993 /* bottom: info, units */
994 bottom = gtk_grid_new();
995 gtk_grid_set_column_spacing(GTK_GRID(bottom), 6);
996 gtk_container_add(GTK_CONTAINER(page), bottom);
998 /* info */
999 frame = gtk_frame_new(_("Info"));
1000 gtk_container_add(GTK_CONTAINER(bottom), frame);
1002 table = create_city_info_table(pdialog,
1003 pdialog->overview.info_ebox,
1004 pdialog->overview.info_label);
1005 gtk_widget_set_halign(table, GTK_ALIGN_CENTER);
1006 gtk_widget_set_valign(table, GTK_ALIGN_CENTER);
1007 gtk_container_add(GTK_CONTAINER(frame), table);
1009 /* right: present and supported units (overview page) */
1010 right = gtk_grid_new();
1011 gtk_orientable_set_orientation(GTK_ORIENTABLE(right),
1012 GTK_ORIENTATION_VERTICAL);
1013 gtk_container_add(GTK_CONTAINER(bottom), right);
1015 pdialog->overview.supported_units_frame = gtk_frame_new("");
1016 gtk_container_add(GTK_CONTAINER(right),
1017 pdialog->overview.supported_units_frame);
1018 pdialog->overview.present_units_frame = gtk_frame_new("");
1019 gtk_container_add(GTK_CONTAINER(right),
1020 pdialog->overview.present_units_frame);
1022 /* supported units */
1023 sw = gtk_scrolled_window_new(NULL, NULL);
1024 gtk_widget_set_hexpand(sw, TRUE);
1025 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1026 GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
1027 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1028 GTK_SHADOW_NONE);
1029 gtk_container_add(GTK_CONTAINER(pdialog->overview.supported_units_frame),
1030 sw);
1033 table = gtk_grid_new();
1034 gtk_grid_set_column_spacing(GTK_GRID(table), 2);
1035 gtk_widget_set_size_request(table, -1, unit_height);
1036 gtk_container_add(GTK_CONTAINER(sw), table);
1038 gtk_container_set_focus_hadjustment(GTK_CONTAINER(table),
1039 gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(sw)));
1040 gtk_container_set_focus_vadjustment(GTK_CONTAINER(table),
1041 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw)));
1043 pdialog->overview.supported_unit_table = table;
1044 unit_node_vector_init(&pdialog->overview.supported_units);
1046 /* present units */
1047 sw = gtk_scrolled_window_new(NULL, NULL);
1048 gtk_widget_set_hexpand(sw, TRUE);
1049 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1050 GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
1051 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1052 GTK_SHADOW_NONE);
1053 gtk_container_add(GTK_CONTAINER(pdialog->overview.present_units_frame), sw);
1055 table = gtk_grid_new();
1056 gtk_grid_set_column_spacing(GTK_GRID(table), 2);
1057 gtk_widget_set_size_request(table, -1, unit_height);
1058 gtk_container_add(GTK_CONTAINER(sw), table);
1060 gtk_container_set_focus_hadjustment(GTK_CONTAINER(table),
1061 gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(sw)));
1062 gtk_container_set_focus_vadjustment(GTK_CONTAINER(table),
1063 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw)));
1065 pdialog->overview.present_unit_table = table;
1066 unit_node_vector_init(&pdialog->overview.present_units);
1068 /* show page */
1069 gtk_widget_show_all(page);
1072 /****************************************************************
1073 Create map page for small screens
1074 *****************************************************************/
1075 static void create_and_append_map_page(struct city_dialog *pdialog)
1077 if (low_citydlg) {
1078 GtkWidget *page;
1079 GtkWidget *label;
1080 const char *tab_title = _("Citymap");
1082 page = gtk_grid_new();
1083 gtk_orientable_set_orientation(GTK_ORIENTABLE(page),
1084 GTK_ORIENTATION_VERTICAL);
1085 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1086 label = gtk_label_new_with_mnemonic(tab_title);
1087 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1089 create_citydlg_main_map(pdialog, page);
1091 gtk_widget_show_all(page);
1095 /****************************************************************
1096 Something dragged to worklist dialog.
1097 *****************************************************************/
1098 static void target_drag_data_received(GtkWidget *w,
1099 GdkDragContext *context,
1100 gint x, gint y,
1101 GtkSelectionData *data,
1102 guint info, guint time,
1103 gpointer user_data)
1105 struct city_dialog *pdialog = (struct city_dialog *) user_data;
1106 GtkTreeModel *model;
1107 GtkTreePath *path;
1109 if (NULL != client.conn.playing
1110 && city_owner(pdialog->pcity) != client.conn.playing) {
1111 gtk_drag_finish(context, FALSE, FALSE, time);
1114 if (gtk_tree_get_row_drag_data(data, &model, &path)) {
1115 GtkTreeIter it;
1117 if (gtk_tree_model_get_iter(model, &it, path)) {
1118 cid id;
1119 struct universal univ;
1121 gtk_tree_model_get(model, &it, 0, &id, -1);
1122 univ = cid_production(id);
1123 city_change_production(pdialog->pcity, &univ);
1124 gtk_drag_finish(context, TRUE, FALSE, time);
1126 gtk_tree_path_free(path);
1129 gtk_drag_finish(context, FALSE, FALSE, time);
1132 /****************************************************************
1133 Create production page header - what tab this actually is,
1134 depends on screen size and layout.
1135 *****************************************************************/
1136 static void create_production_header(struct city_dialog *pdialog, GtkContainer *contain)
1138 GtkWidget *hbox, *bar;
1140 hbox = gtk_grid_new();
1141 g_object_set(hbox, "margin", 2, NULL);
1142 gtk_grid_set_column_spacing(GTK_GRID(hbox), 10);
1143 gtk_container_add(contain, hbox);
1145 /* The label is set in city_dialog_update_building() */
1146 bar = gtk_progress_bar_new();
1147 gtk_widget_set_hexpand(bar, TRUE);
1148 gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(bar), TRUE);
1149 pdialog->production.production_bar = bar;
1150 gtk_container_add(GTK_CONTAINER(hbox), bar);
1151 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(bar), _("%d/%d %d turns"));
1153 add_worklist_dnd_target(bar);
1155 g_signal_connect(bar, "drag_data_received",
1156 G_CALLBACK(target_drag_data_received), pdialog);
1158 pdialog->production.buy_command = gtk_stockbutton_new(GTK_STOCK_EXECUTE,
1159 _("_Buy"));
1160 gtk_container_add(GTK_CONTAINER(hbox), pdialog->production.buy_command);
1162 g_signal_connect(pdialog->production.buy_command, "clicked",
1163 G_CALLBACK(buy_callback), pdialog);
1166 /****************************************************************
1167 Create buildings list page for small screens
1168 *****************************************************************/
1169 static void create_and_append_buildings_page(struct city_dialog *pdialog)
1171 if (low_citydlg) {
1172 GtkWidget *page;
1173 GtkWidget *label;
1174 GtkWidget *vbox;
1175 GtkWidget *view;
1176 const char *tab_title = _("Buildings");
1178 page = gtk_grid_new();
1179 gtk_orientable_set_orientation(GTK_ORIENTABLE(page),
1180 GTK_ORIENTATION_VERTICAL);
1181 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1182 label = gtk_label_new_with_mnemonic(tab_title);
1184 create_production_header(pdialog, GTK_CONTAINER(page));
1185 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1187 vbox = gtk_grid_new();
1188 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox),
1189 GTK_ORIENTATION_VERTICAL);
1190 gtk_container_add(GTK_CONTAINER(page), vbox);
1192 view = create_citydlg_improvement_list(pdialog, vbox);
1194 gtk_container_add(GTK_CONTAINER(vbox), view);
1196 gtk_widget_show_all(page);
1200 /****************************************************************
1201 **** Production Page ****
1202 *****************************************************************/
1203 static void create_and_append_worklist_page(struct city_dialog *pdialog)
1205 const char *tab_title = _("P_roduction");
1206 GtkWidget *label = gtk_label_new_with_mnemonic(tab_title);
1207 GtkWidget *page, *editor;
1209 page = gtk_grid_new();
1210 gtk_orientable_set_orientation(GTK_ORIENTABLE(page),
1211 GTK_ORIENTATION_VERTICAL);
1212 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1213 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1215 /* stuff that's being currently built */
1216 if (!low_citydlg) {
1217 label = g_object_new(GTK_TYPE_LABEL,
1218 "label", _("Production:"),
1219 "xalign", 0.0, "yalign", 0.5, NULL);
1220 pdialog->production.production_label = label;
1221 gtk_container_add(GTK_CONTAINER(page), label);
1223 create_production_header(pdialog, GTK_CONTAINER(page));
1224 } else {
1225 pdialog->production.production_label = NULL;
1228 editor = create_worklist();
1229 g_object_set(editor, "margin", 6, NULL);
1230 reset_city_worklist(editor, pdialog->pcity);
1231 gtk_container_add(GTK_CONTAINER(page), editor);
1232 pdialog->production.worklist = editor;
1234 gtk_widget_show_all(page);
1237 /***************************************************************************
1238 **** Happiness Page ****
1239 +- GtkWidget *page ----------+-------------------------------------------+
1240 | +- GtkWidget *left ------+ | +- GtkWidget *right --------------------+ |
1241 | | Info | | | City map | |
1242 | +- GtkWidget *citizens --+ | +- GtkWidget pdialog->happiness.widget -+ |
1243 | | Citizens data | | | Happiness | |
1244 | +------------------------+ | +---------------------------------------+ |
1245 +----------------------------+-------------------------------------------+
1246 ****************************************************************************/
1247 static void create_and_append_happiness_page(struct city_dialog *pdialog)
1249 GtkWidget *page, *label, *table, *right, *left, *frame;
1250 const char *tab_title = _("Happ_iness");
1252 /* main page */
1253 page = gtk_grid_new();
1254 gtk_grid_set_column_spacing(GTK_GRID(page), 6);
1255 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1256 label = gtk_label_new_with_mnemonic(tab_title);
1257 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1259 /* left: info, citizens */
1260 left = gtk_grid_new();
1261 gtk_orientable_set_orientation(GTK_ORIENTABLE(left),
1262 GTK_ORIENTATION_VERTICAL);
1263 gtk_container_add(GTK_CONTAINER(page), left);
1265 if (!low_citydlg) {
1266 /* upper left: info */
1267 frame = gtk_frame_new(_("Info"));
1268 gtk_container_add(GTK_CONTAINER(left), frame);
1270 table = create_city_info_table(pdialog,
1271 pdialog->happiness.info_ebox,
1272 pdialog->happiness.info_label);
1273 gtk_widget_set_halign(table, GTK_ALIGN_CENTER);
1274 gtk_container_add(GTK_CONTAINER(frame), table);
1277 /* lower left: citizens */
1278 if (game.info.citizen_nationality) {
1279 pdialog->happiness.citizens = gtk_grid_new();
1280 gtk_orientable_set_orientation(
1281 GTK_ORIENTABLE(pdialog->happiness.citizens),
1282 GTK_ORIENTATION_VERTICAL);
1283 gtk_container_add(GTK_CONTAINER(left), pdialog->happiness.citizens);
1284 gtk_container_add(GTK_CONTAINER(pdialog->happiness.citizens),
1285 citizens_dialog_display(pdialog->pcity));
1288 /* right: city map, happiness */
1289 right = gtk_grid_new();
1290 gtk_orientable_set_orientation(GTK_ORIENTABLE(right),
1291 GTK_ORIENTATION_VERTICAL);
1292 gtk_container_add(GTK_CONTAINER(page), right);
1294 if (!low_citydlg) {
1295 /* upper right: city map */
1296 frame = gtk_frame_new(_("City map"));
1297 gtk_widget_set_size_request(frame, CITY_MAP_MIN_SIZE_X,
1298 CITY_MAP_MIN_SIZE_Y);
1299 gtk_container_add(GTK_CONTAINER(right), frame);
1301 city_dialog_map_create(pdialog, &pdialog->happiness.map_canvas);
1302 gtk_container_add(GTK_CONTAINER(frame), pdialog->happiness.map_canvas.sw);
1305 /* lower right: happiness */
1306 pdialog->happiness.widget = gtk_grid_new();
1307 gtk_orientable_set_orientation(GTK_ORIENTABLE(pdialog->happiness.widget),
1308 GTK_ORIENTATION_VERTICAL);
1309 gtk_container_add(GTK_CONTAINER(right), pdialog->happiness.widget);
1310 gtk_container_add(GTK_CONTAINER(pdialog->happiness.widget),
1311 get_top_happiness_display(pdialog->pcity, low_citydlg));
1313 /* show page */
1314 gtk_widget_show_all(page);
1317 /****************************************************************
1318 **** Citizen Management Agent (CMA) Page ****
1319 *****************************************************************/
1320 static void create_and_append_cma_page(struct city_dialog *pdialog)
1322 GtkWidget *page, *label;
1323 const char *tab_title = _("_Governor");
1325 page = gtk_grid_new();
1327 label = gtk_label_new_with_mnemonic(tab_title);
1329 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1331 pdialog->cma_editor = create_cma_dialog(pdialog->pcity, low_citydlg);
1332 gtk_container_add(GTK_CONTAINER(page), pdialog->cma_editor->shell);
1334 gtk_widget_show(page);
1337 /****************************************************************
1338 **** Misc. Settings Page ****
1339 *****************************************************************/
1340 static void create_and_append_settings_page(struct city_dialog *pdialog)
1342 int i;
1343 GtkWidget *vbox2, *page, *frame, *label, *button;
1344 GtkSizeGroup *size;
1345 GSList *group;
1346 const char *tab_title = _("_Settings");
1348 static const char *new_citizens_label[] = {
1349 N_("Entertainers"),
1350 N_("Scientists"),
1351 N_("Taxmen")
1354 static const char *disband_label = N_("Disband if build settler at size 1");
1356 static const char *misc_whichtab_label[NUM_PAGES] = {
1357 N_("Overview page"),
1358 N_("Production page"),
1359 N_("Happiness page"),
1360 N_("Governor page"),
1361 N_("This Settings page"),
1362 N_("Last active page")
1365 static bool new_citizens_label_done;
1366 static bool misc_whichtab_label_done;
1368 /* initialize signal_blocker */
1369 pdialog->misc.block_signal = 0;
1372 page = gtk_grid_new();
1373 gtk_grid_set_column_spacing(GTK_GRID(page), 18);
1374 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1376 size = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
1378 label = gtk_label_new_with_mnemonic(tab_title);
1380 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1382 /* new_citizens radio */
1383 frame = gtk_frame_new(_("New citizens are"));
1384 gtk_grid_attach(GTK_GRID(page), frame, 0, 0, 1, 1);
1385 gtk_size_group_add_widget(size, frame);
1387 vbox2 = gtk_grid_new();
1388 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2),
1389 GTK_ORIENTATION_VERTICAL);
1390 gtk_container_add(GTK_CONTAINER(frame), vbox2);
1392 intl_slist(ARRAY_SIZE(new_citizens_label), new_citizens_label,
1393 &new_citizens_label_done);
1395 group = NULL;
1396 for (i = 0; i < ARRAY_SIZE(new_citizens_label); i++) {
1397 button = gtk_radio_button_new_with_mnemonic(group, new_citizens_label[i]);
1398 pdialog->misc.new_citizens_radio[i] = button;
1399 gtk_container_add(GTK_CONTAINER(vbox2), button);
1400 g_signal_connect(button, "toggled",
1401 G_CALLBACK(cityopt_callback), pdialog);
1402 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
1405 /* next is the next-time-open radio group in the right column */
1406 frame = gtk_frame_new(_("Next time open"));
1407 gtk_grid_attach(GTK_GRID(page), frame, 1, 0, 1, 1);
1408 gtk_size_group_add_widget(size, frame);
1410 vbox2 = gtk_grid_new();
1411 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2),
1412 GTK_ORIENTATION_VERTICAL);
1413 gtk_container_add(GTK_CONTAINER(frame), vbox2);
1415 intl_slist(ARRAY_SIZE(misc_whichtab_label), misc_whichtab_label,
1416 &misc_whichtab_label_done);
1418 group = NULL;
1419 for (i = 0; i < ARRAY_SIZE(misc_whichtab_label); i++) {
1420 button = gtk_radio_button_new_with_mnemonic(group, misc_whichtab_label[i]);
1421 pdialog->misc.whichtab_radio[i] = button;
1422 gtk_container_add(GTK_CONTAINER(vbox2), button);
1423 g_signal_connect(button, "toggled",
1424 G_CALLBACK(misc_whichtab_callback), GINT_TO_POINTER(i));
1425 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
1428 /* now we go back and fill the hbox rename */
1429 frame = gtk_frame_new(_("City"));
1430 gtk_widget_set_margin_top(frame, 12);
1431 gtk_widget_set_margin_bottom(frame, 12);
1432 gtk_grid_attach(GTK_GRID(page), frame, 0, 1, 1, 1);
1434 vbox2 = gtk_grid_new();
1435 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2),
1436 GTK_ORIENTATION_VERTICAL);
1437 gtk_container_add(GTK_CONTAINER(frame), vbox2);
1439 button = gtk_button_new_with_mnemonic(_("R_ename..."));
1440 pdialog->misc.rename_command = button;
1441 gtk_container_add(GTK_CONTAINER(vbox2), button);
1442 g_signal_connect(button, "clicked",
1443 G_CALLBACK(rename_callback), pdialog);
1445 gtk_widget_set_sensitive(button, can_client_issue_orders());
1447 /* the disband-if-size-1 button */
1448 button = gtk_check_button_new_with_mnemonic(_(disband_label));
1449 pdialog->misc.disband_on_settler = button;
1450 gtk_container_add(GTK_CONTAINER(vbox2), button);
1451 g_signal_connect(button, "toggled",
1452 G_CALLBACK(cityopt_callback), pdialog);
1454 /* we choose which page to popup by default */
1455 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
1456 (pdialog->
1457 misc.whichtab_radio[new_dialog_def_page]),
1458 TRUE);
1460 set_cityopt_values(pdialog);
1462 gtk_widget_show_all(page);
1464 if (new_dialog_def_page == (NUM_PAGES - 1)) {
1465 gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog->notebook),
1466 last_page);
1467 } else {
1468 gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog->notebook),
1469 new_dialog_def_page);
1476 /****************************************************************
1477 **** Main City Dialog ****
1478 +----------------------------+-------------------------------+
1479 | GtkWidget *top: Citizens | city name |
1480 +----------------------------+-------------------------------+
1481 | <notebook tab> |
1482 +------------------------------------------------------------+
1483 *****************************************************************/
1484 static struct city_dialog *create_city_dialog(struct city *pcity)
1486 struct city_dialog *pdialog;
1488 GtkWidget *close_command;
1489 GtkWidget *vbox, *hbox, *cbox, *ebox;
1491 if (!city_dialogs_have_been_initialised) {
1492 initialize_city_dialogs();
1495 pdialog = fc_malloc(sizeof(struct city_dialog));
1496 pdialog->pcity = pcity;
1497 pdialog->buy_shell = NULL;
1498 pdialog->sell_shell = NULL;
1499 pdialog->rename_shell = NULL;
1500 pdialog->happiness.map_canvas.sw = NULL; /* make sure NULL if spy */
1501 pdialog->happiness.map_canvas.ebox = NULL; /* ditto */
1502 pdialog->happiness.map_canvas.darea = NULL; /* ditto */
1503 pdialog->happiness.citizens = NULL; /* ditto */
1504 pdialog->cma_editor = NULL;
1505 pdialog->map_canvas_store_unscaled
1506 = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1507 canvas_width, canvas_height);
1509 pdialog->shell = gtk_dialog_new();
1510 gtk_window_set_title(GTK_WINDOW(pdialog->shell), city_name_get(pcity));
1511 setup_dialog(pdialog->shell, toplevel);
1512 gtk_window_set_role(GTK_WINDOW(pdialog->shell), "city");
1514 g_signal_connect(pdialog->shell, "destroy",
1515 G_CALLBACK(city_destroy_callback), pdialog);
1516 gtk_window_set_position(GTK_WINDOW(pdialog->shell), GTK_WIN_POS_MOUSE);
1517 gtk_widget_set_name(pdialog->shell, "Freeciv");
1519 gtk_widget_realize(pdialog->shell);
1521 /* keep the icon of the executable on Windows (see PR#36491) */
1522 #ifndef WIN32_NATIVE
1524 GdkPixbuf *pixbuf = sprite_get_pixbuf(get_icon_sprite(tileset, ICON_CITYDLG));
1526 /* Only call this after tileset_load_tiles is called. */
1527 gtk_window_set_icon(GTK_WINDOW(pdialog->shell), pixbuf);
1528 g_object_unref(pixbuf);
1530 #endif /* WIN32_NATIVE */
1532 /* Restore size of the city dialog. */
1533 gtk_window_set_default_size(GTK_WINDOW(pdialog->shell),
1534 gui_options.gui_gtk3_citydlg_xsize,
1535 gui_options.gui_gtk3_citydlg_ysize);
1537 pdialog->popup_menu = gtk_menu_new();
1539 vbox = gtk_dialog_get_content_area(GTK_DIALOG(pdialog->shell));
1540 hbox = gtk_grid_new();
1541 gtk_grid_set_column_homogeneous(GTK_GRID(hbox), TRUE);
1542 gtk_container_add(GTK_CONTAINER(vbox), hbox);
1544 /**** Citizens bar here ****/
1545 cbox = gtk_grid_new();
1546 gtk_container_add(GTK_CONTAINER(hbox), cbox);
1548 ebox = gtk_event_box_new();
1549 gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
1550 gtk_container_add(GTK_CONTAINER(cbox), ebox);
1551 pdialog->citizen_pixmap =
1552 gtk_pixcomm_new(tileset_small_sprite_width(tileset)
1553 * NUM_CITIZENS_SHOWN,
1554 tileset_small_sprite_height(tileset));
1555 gtk_widget_add_events(pdialog->citizen_pixmap, GDK_BUTTON_PRESS_MASK);
1556 gtk_widget_set_margin_left(pdialog->citizen_pixmap, 2);
1557 gtk_widget_set_margin_right(pdialog->citizen_pixmap, 2);
1558 gtk_widget_set_margin_top(pdialog->citizen_pixmap, 2);
1559 gtk_widget_set_margin_bottom(pdialog->citizen_pixmap, 2);
1560 gtk_widget_set_halign(pdialog->citizen_pixmap, GTK_ALIGN_START);
1561 gtk_widget_set_valign(pdialog->citizen_pixmap, GTK_ALIGN_CENTER);
1562 gtk_container_add(GTK_CONTAINER(ebox), pdialog->citizen_pixmap);
1563 g_signal_connect(G_OBJECT(ebox), "button-press-event",
1564 G_CALLBACK(citizens_callback), pdialog);
1566 /**** City name label here ****/
1567 pdialog->name_label = gtk_label_new(NULL);
1568 gtk_widget_set_hexpand(pdialog->name_label, TRUE);
1569 gtk_widget_set_halign(pdialog->name_label, GTK_ALIGN_START);
1570 gtk_widget_set_valign(pdialog->name_label, GTK_ALIGN_CENTER);
1571 gtk_container_add(GTK_CONTAINER(hbox), pdialog->name_label);
1573 /**** -Start of Notebook- ****/
1575 pdialog->notebook = gtk_notebook_new();
1576 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(pdialog->notebook),
1577 GTK_POS_BOTTOM);
1578 gtk_container_add(GTK_CONTAINER(vbox), pdialog->notebook);
1580 create_and_append_overview_page(pdialog);
1581 create_and_append_map_page(pdialog);
1582 create_and_append_buildings_page(pdialog);
1583 create_and_append_worklist_page(pdialog);
1585 /* only create these tabs if not a spy */
1586 if (!client_has_player() || city_owner(pcity) == client_player()) {
1587 create_and_append_happiness_page(pdialog);
1590 if (city_owner(pcity) == client_player()
1591 && !client_is_observer()) {
1592 create_and_append_cma_page(pdialog);
1593 create_and_append_settings_page(pdialog);
1594 } else {
1595 gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog->notebook),
1596 OVERVIEW_PAGE);
1599 /**** End of Notebook ****/
1601 /* bottom buttons */
1603 pdialog->show_units_command =
1604 gtk_dialog_add_button(GTK_DIALOG(pdialog->shell), _("_List present units..."), CDLGR_UNITS);
1606 g_signal_connect(GTK_DIALOG(pdialog->shell), "response",
1607 G_CALLBACK(citydlg_response_callback), pdialog);
1609 pdialog->prev_command = gtk_stockbutton_new(GTK_STOCK_GO_BACK,
1610 _("_Prev city"));
1611 gtk_dialog_add_action_widget(GTK_DIALOG(pdialog->shell),
1612 pdialog->prev_command, 1);
1614 pdialog->next_command = gtk_stockbutton_new(GTK_STOCK_GO_FORWARD,
1615 _("_Next city"));
1616 gtk_dialog_add_action_widget(GTK_DIALOG(pdialog->shell),
1617 pdialog->next_command, 2);
1619 if (city_owner(pcity) != client.conn.playing) {
1620 gtk_widget_set_sensitive(pdialog->prev_command, FALSE);
1621 gtk_widget_set_sensitive(pdialog->next_command, FALSE);
1624 close_command = gtk_dialog_add_button(GTK_DIALOG(pdialog->shell),
1625 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
1627 gtk_dialog_set_default_response(GTK_DIALOG(pdialog->shell),
1628 GTK_RESPONSE_CLOSE);
1630 g_signal_connect(close_command, "clicked",
1631 G_CALLBACK(close_callback), pdialog);
1633 g_signal_connect(pdialog->prev_command, "clicked",
1634 G_CALLBACK(switch_city_callback), pdialog);
1636 g_signal_connect(pdialog->next_command, "clicked",
1637 G_CALLBACK(switch_city_callback), pdialog);
1639 /* some other things we gotta do */
1641 g_signal_connect(pdialog->shell, "key_press_event",
1642 G_CALLBACK(keyboard_handler), pdialog);
1644 dialog_list_prepend(dialog_list, pdialog);
1646 real_city_dialog_refresh(pdialog->pcity);
1648 /* need to do this every time a new dialog is opened. */
1649 city_dialog_update_prev_next();
1651 gtk_widget_show_all(pdialog->shell);
1653 gtk_window_set_focus(GTK_WINDOW(pdialog->shell), close_command);
1655 return pdialog;
1658 /*********** Functions to update parts of the dialog ************/
1659 /****************************************************************
1660 Update title of city dialog.
1661 *****************************************************************/
1662 static void city_dialog_update_title(struct city_dialog *pdialog)
1664 gchar *buf;
1665 const gchar *now;
1667 if (city_unhappy(pdialog->pcity)) {
1668 /* TRANS: city dialog title */
1669 buf = g_strdup_printf(_("<b>%s</b> - %s citizens - DISORDER"),
1670 city_name_get(pdialog->pcity),
1671 population_to_text(city_population(pdialog->pcity)));
1672 } else if (city_celebrating(pdialog->pcity)) {
1673 /* TRANS: city dialog title */
1674 buf = g_strdup_printf(_("<b>%s</b> - %s citizens - celebrating"),
1675 city_name_get(pdialog->pcity),
1676 population_to_text(city_population(pdialog->pcity)));
1677 } else if (city_happy(pdialog->pcity)) {
1678 /* TRANS: city dialog title */
1679 buf = g_strdup_printf(_("<b>%s</b> - %s citizens - happy"),
1680 city_name_get(pdialog->pcity),
1681 population_to_text(city_population(pdialog->pcity)));
1682 } else {
1683 /* TRANS: city dialog title */
1684 buf = g_strdup_printf(_("<b>%s</b> - %s citizens"),
1685 city_name_get(pdialog->pcity),
1686 population_to_text(city_population(pdialog->pcity)));
1689 now = gtk_label_get_text(GTK_LABEL(pdialog->name_label));
1690 if (strcmp(now, buf) != 0) {
1691 gtk_window_set_title(GTK_WINDOW(pdialog->shell), city_name_get(pdialog->pcity));
1692 gtk_label_set_markup(GTK_LABEL(pdialog->name_label), buf);
1695 g_free(buf);
1698 /****************************************************************
1699 Update citizens in city dialog
1700 *****************************************************************/
1701 static void city_dialog_update_citizens(struct city_dialog *pdialog)
1703 enum citizen_category categories[MAX_CITY_SIZE];
1704 int i, width, size;
1705 int start_margin;
1706 int end_margin;
1707 struct city *pcity = pdialog->pcity;
1708 int num_citizens = get_city_citizen_types(pcity, FEELING_FINAL, categories);
1710 /* If there is not enough space we stack the icons. We draw from left to */
1711 /* right. width is how far we go to the right for each drawn pixmap. The */
1712 /* last icon is always drawn in full, and so has reserved */
1713 /* tileset_small_sprite_width(tileset) pixels. */
1715 if (num_citizens > 1) {
1716 width = MIN(tileset_small_sprite_width(tileset),
1717 ((NUM_CITIZENS_SHOWN - 1) * tileset_small_sprite_width(tileset)) /
1718 (num_citizens - 1));
1719 } else {
1720 width = tileset_small_sprite_width(tileset);
1722 pdialog->cwidth = width;
1724 /* overview page */
1725 start_margin = gtk_widget_get_margin_left(pdialog->citizen_pixmap);
1726 end_margin = gtk_widget_get_margin_right(pdialog->citizen_pixmap);
1727 gtk_pixcomm_clear(GTK_PIXCOMM(pdialog->citizen_pixmap));
1729 size = (num_citizens - 1) * width + tileset_small_sprite_width(tileset) +
1730 2 * (start_margin + end_margin);
1731 gtk_widget_set_size_request(GTK_WIDGET(pdialog->citizen_pixmap), size, -1);
1733 for (i = 0; i < num_citizens; i++) {
1734 gtk_pixcomm_copyto(GTK_PIXCOMM(pdialog->citizen_pixmap),
1735 get_citizen_sprite(tileset, categories[i], i, pcity),
1736 i * width, 0);
1740 /****************************************************************
1741 Update textual info fields in city dialog
1742 *****************************************************************/
1743 static void city_dialog_update_information(GtkWidget **info_ebox,
1744 GtkWidget **info_label,
1745 struct city_dialog *pdialog)
1747 int i, illness = 0;
1748 char buf[NUM_INFO_FIELDS][512];
1749 struct city *pcity = pdialog->pcity;
1750 int granaryturns;
1751 GdkRGBA red = {1.0, 0, 0, 1.0};
1752 GdkRGBA *color;
1754 enum { FOOD, SHIELD, TRADE, GOLD, LUXURY, SCIENCE,
1755 GRANARY, GROWTH, CORRUPTION, WASTE, CULTURE,
1756 POLLUTION, ILLNESS
1759 /* fill the buffers with the necessary info */
1760 fc_snprintf(buf[FOOD], sizeof(buf[FOOD]), "%3d (%+4d)",
1761 pcity->prod[O_FOOD], pcity->surplus[O_FOOD]);
1762 fc_snprintf(buf[SHIELD], sizeof(buf[SHIELD]), "%3d (%+4d)",
1763 pcity->prod[O_SHIELD] + pcity->waste[O_SHIELD],
1764 pcity->surplus[O_SHIELD]);
1765 fc_snprintf(buf[TRADE], sizeof(buf[TRADE]), "%3d (%+4d)",
1766 pcity->surplus[O_TRADE] + pcity->waste[O_TRADE],
1767 pcity->surplus[O_TRADE]);
1768 fc_snprintf(buf[GOLD], sizeof(buf[GOLD]), "%3d (%+4d)",
1769 pcity->prod[O_GOLD], pcity->surplus[O_GOLD]);
1770 fc_snprintf(buf[LUXURY], sizeof(buf[LUXURY]), "%3d",
1771 pcity->prod[O_LUXURY]);
1772 fc_snprintf(buf[SCIENCE], sizeof(buf[SCIENCE]), "%3d",
1773 pcity->prod[O_SCIENCE]);
1774 fc_snprintf(buf[GRANARY], sizeof(buf[GRANARY]), "%4d/%-4d",
1775 pcity->food_stock, city_granary_size(city_size_get(pcity)));
1777 granaryturns = city_turns_to_grow(pcity);
1778 if (granaryturns == 0) {
1779 /* TRANS: city growth is blocked. Keep short. */
1780 fc_snprintf(buf[GROWTH], sizeof(buf[GROWTH]), _("blocked"));
1781 } else if (granaryturns == FC_INFINITY) {
1782 /* TRANS: city is not growing. Keep short. */
1783 fc_snprintf(buf[GROWTH], sizeof(buf[GROWTH]), _("never"));
1784 } else {
1785 /* A negative value means we'll have famine in that many turns.
1786 But that's handled down below. */
1787 /* TRANS: city growth turns. Keep short. */
1788 fc_snprintf(buf[GROWTH], sizeof(buf[GROWTH]),
1789 PL_("%d turn", "%d turns", abs(granaryturns)),
1790 abs(granaryturns));
1792 fc_snprintf(buf[CORRUPTION], sizeof(buf[CORRUPTION]), "%4d",
1793 pcity->waste[O_TRADE]);
1794 fc_snprintf(buf[WASTE], sizeof(buf[WASTE]), "%4d",
1795 pcity->waste[O_SHIELD]);
1796 fc_snprintf(buf[CULTURE], sizeof(buf[CULTURE]), "%4d",
1797 pcity->client.culture);
1798 fc_snprintf(buf[POLLUTION], sizeof(buf[POLLUTION]), "%4d",
1799 pcity->pollution);
1800 if (!game.info.illness_on) {
1801 fc_snprintf(buf[ILLNESS], sizeof(buf[ILLNESS]), " -.-");
1802 } else {
1803 illness = city_illness_calc(pcity, NULL, NULL, NULL, NULL);
1804 /* illness is in tenth of percent */
1805 fc_snprintf(buf[ILLNESS], sizeof(buf[ILLNESS]), "%4.1f",
1806 (float)illness / 10.0);
1809 /* stick 'em in the labels */
1810 for (i = 0; i < NUM_INFO_FIELDS; i++) {
1811 gtk_label_set_text(GTK_LABEL(info_label[i]), buf[i]);
1815 * Special style stuff for granary, growth and pollution below. The
1816 * "4" below is arbitrary. 3 turns should be enough of a warning.
1818 color = (granaryturns > -4 && granaryturns < 0) ? &red : NULL;
1819 gtk_widget_override_color(info_label[GRANARY], GTK_STATE_FLAG_NORMAL, color);
1821 color = (granaryturns == 0 || pcity->surplus[O_FOOD] < 0) ? &red : NULL;
1822 gtk_widget_override_color(info_label[GROWTH], GTK_STATE_FLAG_NORMAL, color);
1824 /* someone could add the color &orange for better granularity here */
1826 color = (pcity->pollution >= 10) ? &red : NULL;
1827 gtk_widget_override_color(info_label[POLLUTION], GTK_STATE_FLAG_NORMAL, color);
1829 /* illness is in tenth of percent, i.e 100 != 10.0% */
1830 color = (illness >= 100) ? &red : NULL;
1831 gtk_widget_override_color(info_label[ILLNESS], GTK_STATE_FLAG_NORMAL, color);
1834 /****************************************************************
1835 Update map display of city dialog
1836 *****************************************************************/
1837 static void city_dialog_update_map(struct city_dialog *pdialog)
1839 struct canvas store = FC_STATIC_CANVAS_INIT;
1841 store.surface = pdialog->map_canvas_store_unscaled;
1843 /* The drawing is done in three steps.
1844 * 1. First we render to a pixmap with the appropriate canvas size.
1845 * 2. Then the pixmap is rendered into a pixbuf of equal size.
1846 * 3. Finally this pixbuf is composited and scaled onto the GtkImage's
1847 * target pixbuf.
1850 city_dialog_redraw_map(pdialog->pcity, &store);
1852 /* draw to real window */
1853 draw_map_canvas(pdialog);
1855 if (cma_is_city_under_agent(pdialog->pcity, NULL)) {
1856 gtk_widget_set_sensitive(pdialog->overview.map_canvas.ebox, FALSE);
1857 if (pdialog->happiness.map_canvas.ebox) {
1858 gtk_widget_set_sensitive(pdialog->happiness.map_canvas.ebox, FALSE);
1860 } else {
1861 gtk_widget_set_sensitive(pdialog->overview.map_canvas.ebox, TRUE);
1862 if (pdialog->happiness.map_canvas.ebox) {
1863 gtk_widget_set_sensitive(pdialog->happiness.map_canvas.ebox, TRUE);
1868 /****************************************************************
1869 Update what city is building and buy cost in city dialog
1870 *****************************************************************/
1871 static void city_dialog_update_building(struct city_dialog *pdialog)
1873 char buf[32], buf2[200];
1874 gdouble pct;
1876 GtkListStore* store;
1877 GtkTreeIter iter;
1878 struct universal targets[MAX_NUM_PRODUCTION_TARGETS];
1879 struct item items[MAX_NUM_PRODUCTION_TARGETS];
1880 int targets_used, item;
1881 struct city *pcity = pdialog->pcity;
1882 gboolean sensitive = city_can_buy(pcity);
1883 const char *descr = city_production_name_translation(pcity);
1884 int cost = city_production_build_shield_cost(pcity);
1886 if (pdialog->overview.buy_command != NULL) {
1887 gtk_widget_set_sensitive(pdialog->overview.buy_command, sensitive);
1889 if (pdialog->production.buy_command != NULL) {
1890 gtk_widget_set_sensitive(pdialog->production.buy_command, sensitive);
1893 /* Make sure build slots info is up to date */
1894 if (pdialog->production.production_label != NULL) {
1895 int build_slots = city_build_slots(pcity);
1897 /* Only display extra info if more than one slot is available */
1898 if (build_slots > 1) {
1899 fc_snprintf(buf2, sizeof(buf2),
1900 /* TRANS: never actually used with built_slots <= 1 */
1901 PL_("Production (up to %d unit per turn):",
1902 "Production (up to %d units per turn):", build_slots),
1903 build_slots);
1904 gtk_label_set_text(
1905 GTK_LABEL(pdialog->production.production_label), buf2);
1906 } else {
1907 gtk_label_set_text(
1908 GTK_LABEL(pdialog->production.production_label), _("Production:"));
1912 /* Update what the city is working on */
1913 get_city_dialog_production(pcity, buf, sizeof(buf));
1915 if (cost > 0) {
1916 pct = (gdouble) pcity->shield_stock / (gdouble) cost;
1917 pct = CLAMP(pct, 0.0, 1.0);
1918 } else {
1919 pct = 1.0;
1922 if (pdialog->overview.production_bar != NULL) {
1923 fc_snprintf(buf2, sizeof(buf2), "%s%s\n%s", descr,
1924 worklist_is_empty(&pcity->worklist) ? "" : " (+)", buf);
1925 gtk_progress_bar_set_text(
1926 GTK_PROGRESS_BAR(pdialog->overview.production_bar), buf2);
1927 gtk_progress_bar_set_fraction(
1928 GTK_PROGRESS_BAR(pdialog->overview.production_bar), pct);
1931 if (pdialog->production.production_bar != NULL) {
1932 fc_snprintf(buf2, sizeof(buf2), "%s%s: %s", descr,
1933 worklist_is_empty(&pcity->worklist) ? "" : " (+)", buf);
1934 gtk_progress_bar_set_text(
1935 GTK_PROGRESS_BAR(pdialog->production.production_bar), buf2);
1936 gtk_progress_bar_set_fraction(
1937 GTK_PROGRESS_BAR(pdialog->production.production_bar), pct);
1940 if (pdialog->overview.production_combo != NULL) {
1941 gtk_combo_box_set_active(GTK_COMBO_BOX(pdialog->overview.production_combo),
1942 -1);
1945 store = pdialog->overview.change_production_store;
1946 if (store != NULL) {
1947 gtk_list_store_clear(pdialog->overview.change_production_store);
1949 targets_used
1950 = collect_eventually_buildable_targets(targets, pdialog->pcity, FALSE);
1951 name_and_sort_items(targets, targets_used, items, FALSE, pcity);
1953 for (item = 0; item < targets_used; item++) {
1954 if (can_city_build_now(pcity, &items[item].item)) {
1955 const char* name;
1956 struct sprite* sprite;
1957 GdkPixbuf *pix;
1958 struct universal target = items[item].item;
1959 bool useless;
1961 if (VUT_UTYPE == target.kind) {
1962 name = utype_name_translation(target.value.utype);
1963 sprite = get_unittype_sprite(tileset, target.value.utype,
1964 direction8_invalid(), TRUE);
1965 useless = FALSE;
1966 } else {
1967 name = improvement_name_translation(target.value.building);
1968 sprite = get_building_sprite(tileset, target.value.building);
1969 useless = is_improvement_redundant(pcity, target.value.building);
1971 pix = sprite_get_pixbuf(sprite);
1972 gtk_list_store_append(store, &iter);
1973 gtk_list_store_set(store, &iter, 0, pix,
1974 1, name, 3, useless,
1975 2, (gint)cid_encode(items[item].item),-1);
1976 g_object_unref(G_OBJECT(pix));
1981 /* work around GTK+ refresh bug. */
1982 if (pdialog->overview.production_bar != NULL) {
1983 gtk_widget_queue_resize(pdialog->overview.production_bar);
1985 if (pdialog->production.production_bar != NULL) {
1986 gtk_widget_queue_resize(pdialog->production.production_bar);
1990 /****************************************************************
1991 Update list of improvements in city dialog
1992 *****************************************************************/
1993 static void city_dialog_update_improvement_list(struct city_dialog *pdialog)
1995 int total, item, targets_used;
1996 struct universal targets[MAX_NUM_PRODUCTION_TARGETS];
1997 struct item items[MAX_NUM_PRODUCTION_TARGETS];
1998 GtkTreeModel *model;
1999 GtkListStore *store;
2001 model =
2002 gtk_tree_view_get_model(GTK_TREE_VIEW(pdialog->overview.improvement_list));
2003 store = GTK_LIST_STORE(model);
2005 targets_used = collect_already_built_targets(targets, pdialog->pcity);
2006 name_and_sort_items(targets, targets_used, items, FALSE, pdialog->pcity);
2008 gtk_list_store_clear(store);
2010 total = 0;
2011 for (item = 0; item < targets_used; item++) {
2012 GdkPixbuf *pix;
2013 GtkTreeIter it;
2014 int upkeep;
2015 struct sprite *sprite;
2016 struct universal target = items[item].item;
2018 fc_assert_action(VUT_IMPROVEMENT == target.kind, continue);
2019 /* This takes effects (like Adam Smith's) into account. */
2020 upkeep = city_improvement_upkeep(pdialog->pcity, target.value.building);
2021 sprite = get_building_sprite(tileset, target.value.building);
2023 pix = sprite_get_pixbuf(sprite);
2024 gtk_list_store_append(store, &it);
2025 gtk_list_store_set(store, &it,
2026 0, target.value.building,
2027 1, pix,
2028 2, items[item].descr,
2029 3, upkeep,
2030 4, is_improvement_redundant(pdialog->pcity, target.value.building),
2031 -1);
2032 g_object_unref(G_OBJECT(pix));
2034 total += upkeep;
2038 /****************************************************************
2039 Update list of supported units in city dialog
2040 *****************************************************************/
2041 static void city_dialog_update_supported_units(struct city_dialog *pdialog)
2043 struct unit_list *units;
2044 struct unit_node_vector *nodes;
2045 int n, m, i;
2046 gchar *buf;
2047 int free_unhappy = get_city_bonus(pdialog->pcity, EFT_MAKE_CONTENT_MIL);
2049 if (NULL != client.conn.playing
2050 && city_owner(pdialog->pcity) != client.conn.playing) {
2051 units = pdialog->pcity->client.info_units_supported;
2052 } else {
2053 units = pdialog->pcity->units_supported;
2056 nodes = &pdialog->overview.supported_units;
2058 n = unit_list_size(units);
2059 m = unit_node_vector_size(nodes);
2061 if (m > n) {
2062 i = 0;
2063 unit_node_vector_iterate(nodes, elt) {
2064 if (i++ >= n) {
2065 gtk_widget_destroy(elt->cmd);
2067 } unit_node_vector_iterate_end;
2069 unit_node_vector_reserve(nodes, n);
2070 } else {
2071 for (i = m; i < n; i++) {
2072 GtkWidget *cmd, *pix;
2073 struct unit_node node;
2074 int unit_height = tileset_unit_with_upkeep_height(tileset);
2076 cmd = gtk_button_new();
2077 node.cmd = cmd;
2079 gtk_button_set_relief(GTK_BUTTON(cmd), GTK_RELIEF_NONE);
2080 gtk_widget_add_events(cmd,
2081 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2083 pix = gtk_pixcomm_new(tileset_full_tile_width(tileset), unit_height);
2084 node.pix = pix;
2086 gtk_container_add(GTK_CONTAINER(cmd), pix);
2088 gtk_grid_attach(GTK_GRID(pdialog->overview.supported_unit_table),
2089 cmd, i, 0, 1, 1);
2090 unit_node_vector_append(nodes, node);
2094 i = 0;
2095 unit_list_iterate(units, punit) {
2096 struct unit_node *pnode;
2097 int happy_cost = city_unit_unhappiness(punit, &free_unhappy);
2099 pnode = unit_node_vector_get(nodes, i);
2100 if (pnode) {
2101 GtkWidget *cmd, *pix;
2103 cmd = pnode->cmd;
2104 pix = pnode->pix;
2106 put_unit_gpixmap(punit, GTK_PIXCOMM(pix));
2107 put_unit_gpixmap_city_overlays(punit, GTK_PIXCOMM(pix), punit->upkeep,
2108 happy_cost);
2110 g_signal_handlers_disconnect_matched(cmd,
2111 G_SIGNAL_MATCH_FUNC,
2112 0, 0, NULL, supported_unit_callback, NULL);
2114 g_signal_handlers_disconnect_matched(cmd,
2115 G_SIGNAL_MATCH_FUNC,
2116 0, 0, NULL, supported_unit_middle_callback, NULL);
2118 gtk_widget_set_tooltip_text(cmd, unit_description(punit));
2120 g_signal_connect(cmd, "button_press_event",
2121 G_CALLBACK(supported_unit_callback),
2122 GINT_TO_POINTER(punit->id));
2124 g_signal_connect(cmd, "button_release_event",
2125 G_CALLBACK(supported_unit_middle_callback),
2126 GINT_TO_POINTER(punit->id));
2128 if (city_owner(pdialog->pcity) != client.conn.playing) {
2129 gtk_widget_set_sensitive(cmd, FALSE);
2130 } else {
2131 gtk_widget_set_sensitive(cmd, TRUE);
2134 gtk_widget_show(pix);
2135 gtk_widget_show(cmd);
2137 i++;
2138 } unit_list_iterate_end;
2140 buf = g_strdup_printf(_("Supported units %d"), n);
2141 gtk_frame_set_label(GTK_FRAME(pdialog->overview.supported_units_frame), buf);
2142 g_free(buf);
2145 /****************************************************************
2146 Update list of present units in city dialog
2147 *****************************************************************/
2148 static void city_dialog_update_present_units(struct city_dialog *pdialog)
2150 struct unit_list *units;
2151 struct unit_node_vector *nodes;
2152 int n, m, i;
2153 gchar *buf;
2155 if (NULL != client.conn.playing
2156 && city_owner(pdialog->pcity) != client.conn.playing) {
2157 units = pdialog->pcity->client.info_units_present;
2158 } else {
2159 units = pdialog->pcity->tile->units;
2162 nodes = &pdialog->overview.present_units;
2164 n = unit_list_size(units);
2165 m = unit_node_vector_size(nodes);
2167 if (m > n) {
2168 i = 0;
2169 unit_node_vector_iterate(nodes, elt) {
2170 if (i++ >= n) {
2171 gtk_widget_destroy(elt->cmd);
2173 } unit_node_vector_iterate_end;
2175 unit_node_vector_reserve(nodes, n);
2176 } else {
2177 for (i = m; i < n; i++) {
2178 GtkWidget *cmd, *pix;
2179 struct unit_node node;
2181 cmd = gtk_button_new();
2182 node.cmd = cmd;
2184 gtk_button_set_relief(GTK_BUTTON(cmd), GTK_RELIEF_NONE);
2185 gtk_widget_add_events(cmd,
2186 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2188 pix = gtk_pixcomm_new(tileset_full_tile_width(tileset),
2189 tileset_full_tile_height(tileset));
2190 node.pix = pix;
2192 gtk_container_add(GTK_CONTAINER(cmd), pix);
2194 gtk_grid_attach(GTK_GRID(pdialog->overview.present_unit_table),
2195 cmd, i, 0, 1, 1);
2196 unit_node_vector_append(nodes, node);
2200 i = 0;
2201 unit_list_iterate(units, punit) {
2202 struct unit_node *pnode;
2204 pnode = unit_node_vector_get(nodes, i);
2205 if (pnode) {
2206 GtkWidget *cmd, *pix;
2208 cmd = pnode->cmd;
2209 pix = pnode->pix;
2211 put_unit_gpixmap(punit, GTK_PIXCOMM(pix));
2213 g_signal_handlers_disconnect_matched(cmd,
2214 G_SIGNAL_MATCH_FUNC,
2215 0, 0, NULL, present_unit_callback, NULL);
2217 g_signal_handlers_disconnect_matched(cmd,
2218 G_SIGNAL_MATCH_FUNC,
2219 0, 0, NULL, present_unit_middle_callback, NULL);
2221 gtk_widget_set_tooltip_text(cmd, unit_description(punit));
2223 g_signal_connect(cmd, "button_press_event",
2224 G_CALLBACK(present_unit_callback),
2225 GINT_TO_POINTER(punit->id));
2227 g_signal_connect(cmd, "button_release_event",
2228 G_CALLBACK(present_unit_middle_callback),
2229 GINT_TO_POINTER(punit->id));
2231 if (city_owner(pdialog->pcity) != client.conn.playing) {
2232 gtk_widget_set_sensitive(cmd, FALSE);
2233 } else {
2234 gtk_widget_set_sensitive(cmd, TRUE);
2237 gtk_widget_show(pix);
2238 gtk_widget_show(cmd);
2240 i++;
2241 } unit_list_iterate_end;
2243 buf = g_strdup_printf(_("Present units %d"), n);
2244 gtk_frame_set_label(GTK_FRAME(pdialog->overview.present_units_frame), buf);
2245 g_free(buf);
2248 /****************************************************************
2249 Updates the sensitivity of the the prev and next buttons.
2250 this does not need pdialog as a parameter, since it iterates
2251 over all the open dialogs.
2252 note: we still need the sensitivity code in create_city_dialog()
2253 for the spied dialogs.
2254 *****************************************************************/
2255 static void city_dialog_update_prev_next(void)
2257 int count = 0;
2258 int city_number;
2260 if (client_is_global_observer()) {
2261 return; /* Keep them insensitive as initially set */
2264 city_number = city_list_size(client.conn.playing->cities);
2266 /* the first time, we see if all the city dialogs are open */
2267 dialog_list_iterate(dialog_list, pdialog) {
2268 if (city_owner(pdialog->pcity) == client.conn.playing) {
2269 count++;
2271 } dialog_list_iterate_end;
2273 if (count == city_number) { /* all are open, shouldn't prev/next */
2274 dialog_list_iterate(dialog_list, pdialog) {
2275 gtk_widget_set_sensitive(pdialog->prev_command, FALSE);
2276 gtk_widget_set_sensitive(pdialog->next_command, FALSE);
2277 } dialog_list_iterate_end;
2278 } else {
2279 dialog_list_iterate(dialog_list, pdialog) {
2280 if (city_owner(pdialog->pcity) == client.conn.playing) {
2281 gtk_widget_set_sensitive(pdialog->prev_command, TRUE);
2282 gtk_widget_set_sensitive(pdialog->next_command, TRUE);
2284 } dialog_list_iterate_end;
2288 /****************************************************************
2289 User clicked button from action area.
2290 *****************************************************************/
2291 static void citydlg_response_callback(GtkDialog *dlg, gint response,
2292 void *data)
2294 switch (response) {
2295 case CDLGR_UNITS:
2296 show_units_response(data);
2297 break;
2301 /****************************************************************
2302 User has clicked show units
2303 *****************************************************************/
2304 static void show_units_response(void *data)
2306 struct city_dialog *pdialog = (struct city_dialog *) data;
2307 struct tile *ptile = pdialog->pcity->tile;
2309 if (unit_list_size(ptile->units)) {
2310 unit_select_dialog_popup(ptile);
2314 /****************************************************************
2315 Set city menu position
2316 *****************************************************************/
2317 static void city_menu_position(GtkMenu *menu, gint *x, gint *y,
2318 gboolean *push_in, gpointer data)
2320 GtkWidget *widget;
2321 GtkAllocation allocation;
2322 gint xpos;
2323 gint ypos;
2325 fc_assert_ret(GTK_IS_BUTTON(data));
2327 widget = GTK_WIDGET(data);
2329 gtk_widget_get_allocation(widget, &allocation);
2331 gdk_window_get_origin(gtk_widget_get_window(widget), &xpos, &ypos);
2333 xpos += allocation.x + allocation.width/2;
2334 ypos += allocation.y + allocation.height/2;
2336 *x = xpos;
2337 *y = ypos;
2338 *push_in = TRUE;
2341 /****************************************************************
2342 Destroy widget -callback
2343 *****************************************************************/
2344 static void destroy_func(GtkWidget *w, gpointer data)
2346 gtk_widget_destroy(w);
2349 /****************************************************************
2350 Pop-up menu to change attributes of supported units
2351 *****************************************************************/
2352 static gboolean supported_unit_callback(GtkWidget * w, GdkEventButton * ev,
2353 gpointer data)
2355 GtkWidget *menu, *item;
2356 struct city_dialog *pdialog;
2357 struct city *pcity;
2358 struct unit *punit =
2359 player_unit_by_number(client_player(), (size_t) data);
2361 if (NULL != punit
2362 && NULL != (pcity = game_city_by_number(punit->homecity))
2363 && NULL != (pdialog = get_city_dialog(pcity))) {
2365 if (ev->type != GDK_BUTTON_PRESS || ev->button == 2 || ev->button == 3
2366 || !can_client_issue_orders()) {
2367 return FALSE;
2370 menu = pdialog->popup_menu;
2372 gtk_menu_popdown(GTK_MENU(menu));
2373 gtk_container_foreach(GTK_CONTAINER(menu), destroy_func, NULL);
2375 item = gtk_menu_item_new_with_mnemonic(_("Cen_ter"));
2376 g_signal_connect(item, "activate",
2377 G_CALLBACK(unit_center_callback),
2378 GINT_TO_POINTER(punit->id));
2379 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2381 item = gtk_menu_item_new_with_mnemonic(_("_Activate unit"));
2382 g_signal_connect(item, "activate",
2383 G_CALLBACK(unit_activate_callback),
2384 GINT_TO_POINTER(punit->id));
2385 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2387 item = gtk_menu_item_new_with_mnemonic(_("Activate unit, _close dialog"));
2388 g_signal_connect(item, "activate",
2389 G_CALLBACK(supported_unit_activate_close_callback),
2390 GINT_TO_POINTER(punit->id));
2391 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2393 item = gtk_menu_item_new_with_mnemonic(_("_Disband unit"));
2394 g_signal_connect(item, "activate",
2395 G_CALLBACK(unit_disband_callback),
2396 GINT_TO_POINTER(punit->id));
2397 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2399 if (unit_has_type_flag(punit, UTYF_UNDISBANDABLE)) {
2400 gtk_widget_set_sensitive(item, FALSE);
2403 gtk_widget_show_all(menu);
2405 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
2406 city_menu_position, w, ev->button, ev->time);
2410 return TRUE;
2413 /****************************************************************
2414 Pop-up menu to change attributes of units, ex. change homecity.
2415 *****************************************************************/
2416 static gboolean present_unit_callback(GtkWidget * w, GdkEventButton * ev,
2417 gpointer data)
2419 GtkWidget *menu, *item;
2420 struct city_dialog *pdialog;
2421 struct city *pcity;
2422 struct unit *punit =
2423 player_unit_by_number(client_player(), (size_t) data);
2425 if (NULL != punit
2426 && NULL != (pcity = tile_city(unit_tile(punit)))
2427 && NULL != (pdialog = get_city_dialog(pcity))) {
2429 if (ev->type != GDK_BUTTON_PRESS || ev->button == 2 || ev->button == 3
2430 || !can_client_issue_orders()) {
2431 return FALSE;
2434 menu = pdialog->popup_menu;
2436 gtk_menu_popdown(GTK_MENU(menu));
2437 gtk_container_foreach(GTK_CONTAINER(menu), destroy_func, NULL);
2439 item = gtk_menu_item_new_with_mnemonic(_("_Activate unit"));
2440 g_signal_connect(item, "activate",
2441 G_CALLBACK(unit_activate_callback),
2442 GINT_TO_POINTER(punit->id));
2443 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2445 item = gtk_menu_item_new_with_mnemonic(_("Activate unit, _close dialog"));
2446 g_signal_connect(item, "activate",
2447 G_CALLBACK(present_unit_activate_close_callback),
2448 GINT_TO_POINTER(punit->id));
2449 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2451 item = gtk_menu_item_new_with_mnemonic(_("_Load unit"));
2452 g_signal_connect(item, "activate",
2453 G_CALLBACK(unit_load_callback),
2454 GINT_TO_POINTER(punit->id));
2455 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2457 if (!unit_can_load(punit)) {
2458 gtk_widget_set_sensitive(item, FALSE);
2461 item = gtk_menu_item_new_with_mnemonic(_("_Unload unit"));
2462 g_signal_connect(item, "activate",
2463 G_CALLBACK(unit_unload_callback),
2464 GINT_TO_POINTER(punit->id));
2465 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2467 if (!can_unit_unload(punit, unit_transport_get(punit))
2468 || !can_unit_exist_at_tile(punit, unit_tile(punit))) {
2469 gtk_widget_set_sensitive(item, FALSE);
2472 item = gtk_menu_item_new_with_mnemonic(_("_Sentry unit"));
2473 g_signal_connect(item, "activate",
2474 G_CALLBACK(unit_sentry_callback),
2475 GINT_TO_POINTER(punit->id));
2476 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2478 if (punit->activity == ACTIVITY_SENTRY
2479 || !can_unit_do_activity(punit, ACTIVITY_SENTRY)) {
2480 gtk_widget_set_sensitive(item, FALSE);
2483 item = gtk_menu_item_new_with_mnemonic(_("_Fortify unit"));
2484 g_signal_connect(item, "activate",
2485 G_CALLBACK(unit_fortify_callback),
2486 GINT_TO_POINTER(punit->id));
2487 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2489 if (punit->activity == ACTIVITY_FORTIFYING
2490 || !can_unit_do_activity(punit, ACTIVITY_FORTIFYING)) {
2491 gtk_widget_set_sensitive(item, FALSE);
2494 item = gtk_menu_item_new_with_mnemonic(_("_Disband unit"));
2495 g_signal_connect(item, "activate",
2496 G_CALLBACK(unit_disband_callback),
2497 GINT_TO_POINTER(punit->id));
2498 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2500 if (unit_has_type_flag(punit, UTYF_UNDISBANDABLE)) {
2501 gtk_widget_set_sensitive(item, FALSE);
2504 item = gtk_menu_item_new_with_mnemonic(_("Set _Home City"));
2505 g_signal_connect(item, "activate",
2506 G_CALLBACK(unit_homecity_callback),
2507 GINT_TO_POINTER(punit->id));
2508 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2509 gtk_widget_set_sensitive(item, can_unit_change_homecity_to(punit, pcity));
2511 item = gtk_menu_item_new_with_mnemonic(_("U_pgrade unit"));
2512 g_signal_connect(item, "activate",
2513 G_CALLBACK(unit_upgrade_callback),
2514 GINT_TO_POINTER(punit->id));
2515 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2517 if (!can_client_issue_orders()
2518 || NULL == can_upgrade_unittype(client.conn.playing,
2519 unit_type_get(punit))) {
2520 gtk_widget_set_sensitive(item, FALSE);
2523 gtk_widget_show_all(menu);
2525 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
2526 city_menu_position, w, ev->button, ev->time);
2528 return TRUE;
2531 /****************************************************************
2532 if user middle-clicked on a unit, activate it and close dialog
2533 *****************************************************************/
2534 static gboolean present_unit_middle_callback(GtkWidget * w,
2535 GdkEventButton * ev,
2536 gpointer data)
2538 struct city_dialog *pdialog;
2539 struct city *pcity;
2540 struct unit *punit =
2541 player_unit_by_number(client_player(), (size_t) data);
2543 if (NULL != punit
2544 && NULL != (pcity = tile_city(unit_tile(punit)))
2545 && NULL != (pdialog = get_city_dialog(pcity))
2546 && can_client_issue_orders()) {
2548 if (ev->button == 3) {
2549 unit_focus_set(punit);
2550 } else if (ev->button == 2) {
2551 unit_focus_set(punit);
2552 close_city_dialog(pdialog);
2556 return TRUE;
2559 /****************************************************************
2560 if user middle-clicked on a unit, activate it and close dialog
2561 *****************************************************************/
2562 static gboolean supported_unit_middle_callback(GtkWidget * w,
2563 GdkEventButton * ev,
2564 gpointer data)
2566 struct city_dialog *pdialog;
2567 struct city *pcity;
2568 struct unit *punit =
2569 player_unit_by_number(client_player(), (size_t) data);
2571 if (NULL != punit
2572 && NULL != (pcity = game_city_by_number(punit->homecity))
2573 && NULL != (pdialog = get_city_dialog(pcity))
2574 && can_client_issue_orders()) {
2576 if (ev->button == 3) {
2577 unit_focus_set(punit);
2578 } else if (ev->button == 2) {
2579 unit_focus_set(punit);
2580 close_city_dialog(pdialog);
2584 return TRUE;
2587 /****************************************************************
2588 User has requested centering to unit
2589 *****************************************************************/
2590 static void unit_center_callback(GtkWidget * w, gpointer data)
2592 struct unit *punit =
2593 player_unit_by_number(client_player(), (size_t)data);
2595 if (NULL != punit) {
2596 center_tile_mapcanvas(unit_tile(punit));
2600 /****************************************************************
2601 User has requested unit activation
2602 *****************************************************************/
2603 static void unit_activate_callback(GtkWidget * w, gpointer data)
2605 struct unit *punit =
2606 player_unit_by_number(client_player(), (size_t)data);
2608 if (NULL != punit) {
2609 unit_focus_set(punit);
2613 /****************************************************************
2614 User has requested some supported unit to be activated and
2615 city dialog to be closed
2616 *****************************************************************/
2617 static void supported_unit_activate_close_callback(GtkWidget * w,
2618 gpointer data)
2620 struct unit *punit =
2621 player_unit_by_number(client_player(), (size_t)data);
2623 if (NULL != punit) {
2624 struct city *pcity =
2625 player_city_by_number(client_player(), punit->homecity);
2627 unit_focus_set(punit);
2628 if (NULL != pcity) {
2629 struct city_dialog *pdialog = get_city_dialog(pcity);
2631 if (NULL != pdialog) {
2632 close_city_dialog(pdialog);
2638 /****************************************************************
2639 User has requested some present unit to be activated and
2640 city dialog to be closed
2641 *****************************************************************/
2642 static void present_unit_activate_close_callback(GtkWidget * w,
2643 gpointer data)
2645 struct unit *punit =
2646 player_unit_by_number(client_player(), (size_t)data);
2648 if (NULL != punit) {
2649 struct city *pcity = tile_city(unit_tile(punit));
2651 unit_focus_set(punit);
2652 if (NULL != pcity) {
2653 struct city_dialog *pdialog = get_city_dialog(pcity);
2655 if (NULL != pdialog) {
2656 close_city_dialog(pdialog);
2662 /****************************************************************
2663 User has requested unit to be loaded to transport
2664 *****************************************************************/
2665 static void unit_load_callback(GtkWidget * w, gpointer data)
2667 struct unit *punit =
2668 player_unit_by_number(client_player(), (size_t)data);
2670 if (NULL != punit) {
2671 request_transport(punit, unit_tile(punit));
2675 /****************************************************************
2676 User has requested unit to be unloaded from transport
2677 *****************************************************************/
2678 static void unit_unload_callback(GtkWidget * w, gpointer data)
2680 struct unit *punit =
2681 player_unit_by_number(client_player(), (size_t)data);
2683 if (NULL != punit) {
2684 request_unit_unload(punit);
2688 /****************************************************************
2689 User has requested unit to be sentried
2690 *****************************************************************/
2691 static void unit_sentry_callback(GtkWidget * w, gpointer data)
2693 struct unit *punit =
2694 player_unit_by_number(client_player(), (size_t)data);
2696 if (NULL != punit) {
2697 request_unit_sentry(punit);
2701 /****************************************************************
2702 User has requested unit to be fortified
2703 *****************************************************************/
2704 static void unit_fortify_callback(GtkWidget * w, gpointer data)
2706 struct unit *punit =
2707 player_unit_by_number(client_player(), (size_t)data);
2709 if (NULL != punit) {
2710 request_unit_fortify(punit);
2714 /****************************************************************
2715 User has requested unit to be disbanded
2716 *****************************************************************/
2717 static void unit_disband_callback(GtkWidget * w, gpointer data)
2719 struct unit_list *punits;
2720 struct unit *punit =
2721 player_unit_by_number(client_player(), (size_t)data);
2723 if (NULL == punit) {
2724 return;
2727 punits = unit_list_new();
2728 unit_list_append(punits, punit);
2729 popup_disband_dialog(punits);
2730 unit_list_destroy(punits);
2733 /****************************************************************
2734 User has requested unit to change homecity to city where it
2735 currently is
2736 *****************************************************************/
2737 static void unit_homecity_callback(GtkWidget * w, gpointer data)
2739 struct unit *punit =
2740 player_unit_by_number(client_player(), (size_t)data);
2742 if (NULL != punit) {
2743 request_unit_change_homecity(punit);
2747 /****************************************************************
2748 User has requested unit to be upgraded
2749 *****************************************************************/
2750 static void unit_upgrade_callback(GtkWidget *w, gpointer data)
2752 struct unit_list *punits;
2753 struct unit *punit =
2754 player_unit_by_number(client_player(), (size_t)data);
2756 if (NULL == punit) {
2757 return;
2760 punits = unit_list_new();
2761 unit_list_append(punits, punit);
2762 popup_upgrade_dialog(punits);
2763 unit_list_destroy(punits);
2766 /*** Callbacks for citizen bar, map funcs that are not update ***/
2767 /****************************************************************
2768 Somebody clicked our list of citizens. If they clicked a specialist
2769 then change the type of him, else do nothing.
2770 *****************************************************************/
2771 static gboolean citizens_callback(GtkWidget *w, GdkEventButton *ev,
2772 gpointer data)
2774 struct city_dialog *pdialog = data;
2775 struct city *pcity = pdialog->pcity;
2776 int citnum, tlen, len;
2778 if (!can_client_issue_orders()) {
2779 return FALSE;
2782 tlen = tileset_small_sprite_width(tileset);
2783 len = (city_size_get(pcity) - 1) * pdialog->cwidth + tlen;
2784 if (ev->x > len) {
2785 /* no citizen that far to the right */
2786 return FALSE;
2788 citnum = MIN(city_size_get(pcity) - 1, ev->x / pdialog->cwidth);
2790 city_rotate_specialist(pcity, citnum);
2792 return TRUE;
2795 /**************************************************************************
2796 Set requested workertask
2797 **************************************************************************/
2798 static void set_city_workertask(GtkWidget *w, gpointer data)
2800 enum unit_activity act = (enum unit_activity)GPOINTER_TO_INT(data);
2801 struct city *pcity = workertask_req.owner;
2802 struct tile *ptile = workertask_req.loc;
2803 struct packet_worker_task task;
2805 task.city_id = pcity->id;
2807 if (act == ACTIVITY_LAST) {
2808 task.tgt = -1;
2809 task.want = 0;
2810 } else {
2811 enum extra_cause cause = activity_to_extra_cause(act);
2812 struct extra_type *tgt;
2814 if (cause != EC_NONE) {
2815 tgt = next_extra_for_tile(ptile, cause, city_owner(pcity), NULL);
2816 } else {
2817 tgt = NULL;
2820 if (tgt == NULL) {
2821 struct terrain *pterr = tile_terrain(ptile);
2823 if ((act != ACTIVITY_TRANSFORM
2824 || pterr->transform_result == NULL || pterr->transform_result == pterr)
2825 && (act != ACTIVITY_IRRIGATE
2826 || pterr->irrigation_result == NULL || pterr->irrigation_result == pterr)
2827 && (act != ACTIVITY_MINE
2828 || pterr->mining_result == NULL || pterr->mining_result == pterr)) {
2829 /* No extra to order */
2830 output_window_append(ftc_client, _("There's no suitable extra to order."));
2832 return;
2835 task.tgt = -1;
2836 } else {
2837 task.tgt = extra_index(tgt);
2840 task.want = 100;
2843 task.tile_id = ptile->index;
2844 task.activity = act;
2846 send_packet_worker_task(&client.conn, &task);
2849 /****************************************************************
2850 Destroy workertask dlg
2851 *****************************************************************/
2852 static void workertask_dlg_destroy(GtkWidget *w, gpointer data)
2854 is_showing_workertask_dialog = FALSE;
2857 /**************************************************************************
2858 Open dialog for setting worker task
2859 **************************************************************************/
2860 static void popup_workertask_dlg(struct city *pcity, struct tile *ptile)
2862 if (!is_showing_workertask_dialog) {
2863 GtkWidget *shl;
2864 struct terrain *pterr = tile_terrain(ptile);
2865 struct universal for_terr = { .kind = VUT_TERRAIN,
2866 .value = { .terrain = pterr }};
2867 struct worker_task *ptask;
2869 is_showing_workertask_dialog = TRUE;
2870 workertask_req.owner = pcity;
2871 workertask_req.loc = ptile;
2873 shl = choice_dialog_start(GTK_WINDOW(toplevel),
2874 _("What Action to Request"),
2875 _("Select autosettler activity:"));
2877 ptask = worker_task_list_get(pcity->task_reqs, 0);
2878 if (ptask != NULL) {
2879 choice_dialog_add(shl, _("Clear request"),
2880 G_CALLBACK(set_city_workertask),
2881 GINT_TO_POINTER(ACTIVITY_LAST), FALSE, NULL);
2884 if ((pterr->mining_result == pterr
2885 && effect_cumulative_max(EFT_MINING_POSSIBLE, &for_terr) > 0)
2886 || (pterr->mining_result != pterr && pterr->mining_result != NULL
2887 && effect_cumulative_max(EFT_MINING_TF_POSSIBLE, &for_terr) > 0)) {
2888 choice_dialog_add(shl, _("Mine"),
2889 G_CALLBACK(set_city_workertask),
2890 GINT_TO_POINTER(ACTIVITY_MINE), FALSE, NULL);
2892 if ((pterr->irrigation_result == pterr
2893 && effect_cumulative_max(EFT_IRRIG_POSSIBLE, &for_terr) > 0)
2894 || (pterr->irrigation_result != pterr && pterr->irrigation_result != NULL
2895 && effect_cumulative_max(EFT_IRRIG_TF_POSSIBLE, &for_terr) > 0)) {
2896 choice_dialog_add(shl, _("Irrigate"),
2897 G_CALLBACK(set_city_workertask),
2898 GINT_TO_POINTER(ACTIVITY_IRRIGATE), FALSE, NULL);
2900 if (next_extra_for_tile(ptile, EC_ROAD, city_owner(pcity), NULL) != NULL) {
2901 choice_dialog_add(shl, _("Road"),
2902 G_CALLBACK(set_city_workertask),
2903 GINT_TO_POINTER(ACTIVITY_GEN_ROAD), FALSE, NULL);
2905 if (pterr->transform_result != pterr && pterr->transform_result != NULL
2906 && effect_cumulative_max(EFT_TRANSFORM_POSSIBLE, &for_terr) > 0) {
2907 choice_dialog_add(shl, _("Transform"),
2908 G_CALLBACK(set_city_workertask),
2909 GINT_TO_POINTER(ACTIVITY_TRANSFORM), FALSE, NULL);
2912 choice_dialog_add(shl, GTK_STOCK_CANCEL, 0, 0, FALSE, NULL);
2913 choice_dialog_end(shl);
2915 g_signal_connect(shl, "destroy", G_CALLBACK(workertask_dlg_destroy),
2916 NULL);
2920 /**************************************************************************
2921 User has pressed button on citymap
2922 **************************************************************************/
2923 static gboolean button_down_citymap(GtkWidget *w, GdkEventButton *ev,
2924 gpointer data)
2926 struct city_dialog *pdialog = data;
2927 int canvas_x, canvas_y, city_x, city_y;
2929 if (!can_client_issue_orders()) {
2930 return FALSE;
2933 canvas_x = ev->x * (double)canvas_width / (double)CITYMAP_WIDTH;
2934 canvas_y = ev->y * (double)canvas_height / (double)CITYMAP_HEIGHT;
2936 if (canvas_to_city_pos(&city_x, &city_y,
2937 city_map_radius_sq_get(pdialog->pcity),
2938 canvas_x, canvas_y)) {
2939 if (ev->button == 1) {
2940 city_toggle_worker(pdialog->pcity, city_x, city_y);
2941 } else if (ev->button == 3) {
2942 struct city *pcity = pdialog->pcity;
2944 popup_workertask_dlg(pdialog->pcity,
2945 city_map_to_tile(pcity->tile, city_map_radius_sq_get(pcity),
2946 city_x, city_y));
2950 return TRUE;
2953 /****************************************************************
2954 Set map canvas to be drawn
2955 *****************************************************************/
2956 static void draw_map_canvas(struct city_dialog *pdialog)
2958 gtk_widget_queue_draw(pdialog->overview.map_canvas.darea);
2959 if (pdialog->happiness.map_canvas.darea) { /* in case of spy */
2960 gtk_widget_queue_draw(pdialog->happiness.map_canvas.darea);
2964 /********* Callbacks for Buy, Change, Sell, Worklist ************/
2965 /****************************************************************
2966 User has answered buy cost dialog
2967 *****************************************************************/
2968 static void buy_callback_response(GtkWidget *w, gint response, gpointer data)
2970 struct city_dialog *pdialog = data;
2972 if (response == GTK_RESPONSE_YES) {
2973 city_buy_production(pdialog->pcity);
2975 gtk_widget_destroy(w);
2978 /****************************************************************
2979 User has clicked buy-button
2980 *****************************************************************/
2981 static void buy_callback(GtkWidget *w, gpointer data)
2983 GtkWidget *shell;
2984 struct city_dialog *pdialog = data;
2985 const char *name = city_production_name_translation(pdialog->pcity);
2986 int value = city_production_buy_gold_cost(pdialog->pcity);
2987 char buf[1024];
2989 if (!can_client_issue_orders()) {
2990 return;
2993 fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Treasury contains %d gold.",
2994 "Treasury contains %d gold.",
2995 client_player()->economic.gold),
2996 client_player()->economic.gold);
2998 if (value <= client_player()->economic.gold) {
2999 shell = gtk_message_dialog_new(NULL,
3000 GTK_DIALOG_DESTROY_WITH_PARENT,
3001 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
3002 /* TRANS: Last %s is pre-pluralised "Treasury contains %d gold." */
3003 PL_("Buy %s for %d gold?\n%s",
3004 "Buy %s for %d gold?\n%s", value),
3005 name, value, buf);
3006 setup_dialog(shell, pdialog->shell);
3007 gtk_window_set_title(GTK_WINDOW(shell), _("Buy It!"));
3008 gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_NO);
3009 g_signal_connect(shell, "response", G_CALLBACK(buy_callback_response),
3010 pdialog);
3011 gtk_window_present(GTK_WINDOW(shell));
3012 } else {
3013 shell = gtk_message_dialog_new(NULL,
3014 GTK_DIALOG_DESTROY_WITH_PARENT,
3015 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
3016 /* TRANS: Last %s is pre-pluralised "Treasury contains %d gold." */
3017 PL_("%s costs %d gold.\n%s",
3018 "%s costs %d gold.\n%s", value),
3019 name, value, buf);
3020 setup_dialog(shell, pdialog->shell);
3021 gtk_window_set_title(GTK_WINDOW(shell), _("Buy It!"));
3022 g_signal_connect(shell, "response", G_CALLBACK(gtk_widget_destroy),
3023 NULL);
3024 gtk_window_present(GTK_WINDOW(shell));
3028 /****************************************************************************
3029 Callback for the dropdown production menu.
3030 ****************************************************************************/
3031 static void change_production_callback(GtkComboBox *combo,
3032 struct city_dialog *pdialog)
3034 GtkTreeIter iter;
3036 if (can_client_issue_orders()
3037 && gtk_combo_box_get_active_iter(combo, &iter)) {
3038 cid id;
3039 struct universal univ;
3041 gtk_tree_model_get(gtk_combo_box_get_model(combo), &iter, 2, &id, -1);
3042 univ = cid_production(id);
3043 city_change_production(pdialog->pcity, &univ);
3047 /****************************************************************
3048 User has clicked sell-button
3049 *****************************************************************/
3050 static void sell_callback(struct impr_type *pimprove, gpointer data)
3052 GtkWidget *shl;
3053 struct city_dialog *pdialog = (struct city_dialog *) data;
3054 pdialog->sell_id = improvement_number(pimprove);
3055 int price;
3057 if (!can_client_issue_orders()) {
3058 return;
3061 if (test_player_sell_building_now(client.conn.playing, pdialog->pcity,
3062 pimprove) != TR_SUCCESS) {
3063 return;
3066 price = impr_sell_gold(pimprove);
3067 shl = gtk_message_dialog_new(NULL,
3068 GTK_DIALOG_DESTROY_WITH_PARENT,
3069 GTK_MESSAGE_QUESTION,
3070 GTK_BUTTONS_YES_NO,
3071 PL_("Sell %s for %d gold?",
3072 "Sell %s for %d gold?", price),
3073 city_improvement_name_translation(pdialog->pcity, pimprove), price);
3074 setup_dialog(shl, pdialog->shell);
3075 pdialog->sell_shell = shl;
3077 gtk_window_set_title(GTK_WINDOW(shl), _("Sell It!"));
3078 gtk_window_set_position(GTK_WINDOW(shl), GTK_WIN_POS_CENTER_ON_PARENT);
3080 g_signal_connect(shl, "response",
3081 G_CALLBACK(sell_callback_response), pdialog);
3083 gtk_window_present(GTK_WINDOW(shl));
3086 /****************************************************************
3087 User has responded to sell price dialog
3088 *****************************************************************/
3089 static void sell_callback_response(GtkWidget *w, gint response, gpointer data)
3091 struct city_dialog *pdialog = data;
3093 if (response == GTK_RESPONSE_YES) {
3094 city_sell_improvement(pdialog->pcity, pdialog->sell_id);
3096 gtk_widget_destroy(w);
3098 pdialog->sell_shell = NULL;
3101 /****************************************************************
3102 this is here because it's closely related to the sell stuff
3103 *****************************************************************/
3104 static void impr_callback(GtkTreeView *view, GtkTreePath *path,
3105 GtkTreeViewColumn *col, gpointer data)
3107 GtkTreeModel *model;
3108 GtkTreeIter it;
3109 GdkWindow *win;
3110 GdkDeviceManager *manager;
3111 GdkModifierType mask;
3112 struct impr_type *pimprove;
3114 model = gtk_tree_view_get_model(view);
3116 if (!gtk_tree_model_get_iter(model, &it, path)) {
3117 return;
3120 gtk_tree_model_get(model, &it, 0, &pimprove, -1);
3122 win = gdk_get_default_root_window();
3123 manager = gdk_display_get_device_manager(gdk_window_get_display(win));
3125 gdk_window_get_device_position(win,
3126 gdk_device_manager_get_client_pointer(manager),
3127 NULL, NULL, &mask);
3129 if (!(mask & GDK_CONTROL_MASK)) {
3130 sell_callback(pimprove, data);
3131 } else {
3132 if (is_great_wonder(pimprove)) {
3133 popup_help_dialog_typed(improvement_name_translation(pimprove), HELP_WONDER);
3134 } else {
3135 popup_help_dialog_typed(improvement_name_translation(pimprove), HELP_IMPROVEMENT);
3140 /******* Callbacks for stuff on the Misc. Settings page *********/
3141 /****************************************************************
3142 Called when Rename button pressed
3143 *****************************************************************/
3144 static void rename_callback(GtkWidget *w, gpointer data)
3146 struct city_dialog *pdialog;
3148 pdialog = (struct city_dialog *) data;
3150 pdialog->rename_shell = input_dialog_create(GTK_WINDOW(pdialog->shell),
3151 /* "shellrenamecity" */
3152 _("Rename City"),
3153 _("What should we rename the city to?"),
3154 city_name_get(pdialog->pcity),
3155 rename_popup_callback, pdialog);
3158 /****************************************************************
3159 Called when user has finished with "Rename City" popup
3160 *****************************************************************/
3161 static void rename_popup_callback(gpointer data, gint response,
3162 const char *input)
3164 struct city_dialog *pdialog = data;
3166 if (pdialog) {
3167 if (response == GTK_RESPONSE_OK) {
3168 city_rename(pdialog->pcity, input);
3169 } /* else CANCEL or DELETE_EVENT */
3171 pdialog->rename_shell = NULL;
3175 /****************************************************************
3176 Sets which page will be set on reopen of dialog
3177 *****************************************************************/
3178 static void misc_whichtab_callback(GtkWidget * w, gpointer data)
3180 new_dialog_def_page = GPOINTER_TO_INT(data);
3183 /**************************************************************************
3184 City options callbacks
3185 **************************************************************************/
3186 static void cityopt_callback(GtkWidget * w, gpointer data)
3188 struct city_dialog *pdialog = (struct city_dialog *) data;
3190 if (!can_client_issue_orders()) {
3191 return;
3194 if(!pdialog->misc.block_signal){
3195 struct city *pcity = pdialog->pcity;
3196 bv_city_options new_options;
3198 fc_assert(CITYO_LAST == 3);
3200 BV_CLR_ALL(new_options);
3201 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pdialog->misc.disband_on_settler))) {
3202 BV_SET(new_options, CITYO_DISBAND);
3204 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pdialog->misc.new_citizens_radio[1]))) {
3205 BV_SET(new_options, CITYO_NEW_EINSTEIN);
3207 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pdialog->misc.new_citizens_radio[2]))) {
3208 BV_SET(new_options, CITYO_NEW_TAXMAN);
3211 dsend_packet_city_options_req(&client.conn, pcity->id,new_options);
3215 /**************************************************************************
3216 refresh the city options (auto_[land, air, sea, helicopter] and
3217 disband-is-size-1) in the misc page.
3218 **************************************************************************/
3219 static void set_cityopt_values(struct city_dialog *pdialog)
3221 struct city *pcity = pdialog->pcity;
3223 pdialog->misc.block_signal = 1;
3225 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pdialog->misc.disband_on_settler),
3226 is_city_option_set(pcity, CITYO_DISBAND));
3228 if (is_city_option_set(pcity, CITYO_NEW_EINSTEIN)) {
3229 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
3230 (pdialog->misc.new_citizens_radio[1]), TRUE);
3231 } else if (is_city_option_set(pcity, CITYO_NEW_TAXMAN)) {
3232 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
3233 (pdialog->misc.new_citizens_radio[2]), TRUE);
3234 } else {
3235 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
3236 (pdialog->misc.new_citizens_radio[0]), TRUE);
3238 pdialog->misc.block_signal = 0;
3241 /*************** Callbacks for: Close, Prev, Next. **************/
3242 /****************************************************************
3243 User has clicked rename city-button
3244 *****************************************************************/
3245 static void close_callback(GtkWidget *w, gpointer data)
3247 close_city_dialog((struct city_dialog *) data);
3250 /****************************************************************
3251 User has closed rename city dialog
3252 *****************************************************************/
3253 static void city_destroy_callback(GtkWidget *w, gpointer data)
3255 struct city_dialog *pdialog;
3257 pdialog = (struct city_dialog *) data;
3259 gtk_widget_hide(pdialog->shell);
3261 if (game.info.citizen_nationality) {
3262 citizens_dialog_close(pdialog->pcity);
3264 close_happiness_dialog(pdialog->pcity);
3265 close_cma_dialog(pdialog->pcity);
3267 /* Save size of the city dialog. */
3268 gui_options.gui_gtk3_citydlg_xsize
3269 = CLIP(GUI_GTK3_CITYDLG_MIN_XSIZE,
3270 gtk_widget_get_allocated_width(pdialog->shell),
3271 GUI_GTK3_CITYDLG_MAX_XSIZE);
3272 gui_options.gui_gtk3_citydlg_ysize
3273 = CLIP(GUI_GTK3_CITYDLG_MIN_XSIZE,
3274 gtk_widget_get_allocated_height(pdialog->shell),
3275 GUI_GTK3_CITYDLG_MAX_XSIZE);
3277 last_page
3278 = gtk_notebook_get_current_page(GTK_NOTEBOOK(pdialog->notebook));
3280 if (pdialog->popup_menu) {
3281 gtk_widget_destroy(pdialog->popup_menu);
3284 dialog_list_remove(dialog_list, pdialog);
3286 unit_node_vector_free(&pdialog->overview.supported_units);
3287 unit_node_vector_free(&pdialog->overview.present_units);
3289 if (pdialog->buy_shell) {
3290 gtk_widget_destroy(pdialog->buy_shell);
3292 if (pdialog->sell_shell) {
3293 gtk_widget_destroy(pdialog->sell_shell);
3295 if (pdialog->rename_shell) {
3296 gtk_widget_destroy(pdialog->rename_shell);
3299 cairo_surface_destroy(pdialog->map_canvas_store_unscaled);
3301 free(pdialog);
3303 /* need to do this every time a new dialog is closed. */
3304 city_dialog_update_prev_next();
3307 /************************************************************************
3308 Close city dialog
3309 *************************************************************************/
3310 static void close_city_dialog(struct city_dialog *pdialog)
3312 gtk_widget_destroy(pdialog->shell);
3315 /************************************************************************
3316 Callback for the prev/next buttons. Switches to the previous/next
3317 city.
3318 *************************************************************************/
3319 static void switch_city_callback(GtkWidget *w, gpointer data)
3321 struct city_dialog *pdialog = (struct city_dialog *) data;
3322 int i, j, dir, size;
3323 struct city *new_pcity = NULL;
3325 if (client_is_global_observer()) {
3326 return;
3329 size = city_list_size(client.conn.playing->cities);
3331 fc_assert_ret(city_dialogs_have_been_initialised);
3332 fc_assert_ret(size >= 1);
3333 fc_assert_ret(city_owner(pdialog->pcity) == client.conn.playing);
3335 if (size == 1) {
3336 return;
3339 /* dir = 1 will advance to the city, dir = -1 will get previous */
3340 if (w == pdialog->next_command) {
3341 dir = 1;
3342 } else if (w == pdialog->prev_command) {
3343 dir = -1;
3344 } else {
3345 /* Always fails. */
3346 fc_assert_ret(w == pdialog->next_command
3347 || w == pdialog->prev_command);
3348 dir = 1;
3351 for (i = 0; i < size; i++) {
3352 if (pdialog->pcity == city_list_get(client.conn.playing->cities, i)) {
3353 break;
3357 fc_assert_ret(i < size);
3359 for (j = 1; j < size; j++) {
3360 struct city *other_pcity = city_list_get(client.conn.playing->cities,
3361 (i + dir * j + size) % size);
3362 struct city_dialog *other_pdialog = get_city_dialog(other_pcity);
3364 fc_assert_ret(other_pdialog != pdialog);
3365 if (!other_pdialog) {
3366 new_pcity = other_pcity;
3367 break;
3371 if (!new_pcity) {
3372 /* Every other city has an open city dialog. */
3373 return;
3376 /* cleanup happiness dialog */
3377 if (game.info.citizen_nationality) {
3378 citizens_dialog_close(pdialog->pcity);
3380 close_happiness_dialog(pdialog->pcity);
3382 pdialog->pcity = new_pcity;
3384 /* reinitialize happiness, and cma dialogs */
3385 if (game.info.citizen_nationality) {
3386 gtk_container_add(GTK_CONTAINER(pdialog->happiness.citizens),
3387 citizens_dialog_display(pdialog->pcity));
3389 gtk_container_add(GTK_CONTAINER(pdialog->happiness.widget),
3390 get_top_happiness_display(pdialog->pcity, low_citydlg));
3391 if (!client_is_observer()) {
3392 fc_assert(pdialog->cma_editor != NULL);
3393 pdialog->cma_editor->pcity = new_pcity;
3396 reset_city_worklist(pdialog->production.worklist, pdialog->pcity);
3398 can_slide = FALSE;
3399 center_tile_mapcanvas(pdialog->pcity->tile);
3400 can_slide = TRUE;
3401 if (!client_is_observer()) {
3402 set_cityopt_values(pdialog); /* need not be in real_city_dialog_refresh */
3405 real_city_dialog_refresh(pdialog->pcity);
3407 /* recenter the city map(s) */
3408 city_dialog_map_recenter(pdialog->overview.map_canvas.sw);
3409 if (pdialog->happiness.map_canvas.sw) {
3410 city_dialog_map_recenter(pdialog->happiness.map_canvas.sw);