webperimental: killstack decides stack protects.
[freeciv.git] / client / gui-gtk-3.0 / citydlg.c
blob1738a3eb89d56559107f216d685106072b5efe6e
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;
97 int height;
100 /* get 'struct unit_node' and related function */
101 #define SPECVEC_TAG unit_node
102 #define SPECVEC_TYPE struct unit_node
103 #include "specvec.h"
105 #define unit_node_vector_iterate(list, elt) \
106 TYPED_VECTOR_ITERATE(struct unit_node, list, elt)
107 #define unit_node_vector_iterate_end VECTOR_ITERATE_END
109 enum { OVERVIEW_PAGE, MAP_PAGE, BUILDINGS_PAGE, WORKLIST_PAGE,
110 HAPPINESS_PAGE, CMA_PAGE, TRADE_PAGE, MISC_PAGE
113 #define NUM_CITIZENS_SHOWN 30
114 #define NUM_INFO_FIELDS 13 /* number of fields in city_info */
115 #define NUM_PAGES 6 /* the number of pages in city dialog notebook
116 * (+1) if you change this, you must add an
117 * entry to misc_whichtab_label[] */
119 /* minimal size for the city map scrolling windows*/
120 #define CITY_MAP_MIN_SIZE_X 200
121 #define CITY_MAP_MIN_SIZE_Y 150
123 struct city_map_canvas {
124 GtkWidget *sw;
125 GtkWidget *ebox;
126 GtkWidget *darea;
129 struct city_dialog {
130 struct city *pcity;
132 GtkWidget *shell;
133 GtkWidget *name_label;
134 cairo_surface_t *map_canvas_store_unscaled;
135 GtkWidget *notebook;
137 GtkWidget *popup_menu;
138 GtkWidget *citizen_images;
139 cairo_surface_t *citizen_surface;
141 struct {
142 struct city_map_canvas map_canvas;
144 GtkWidget *production_bar;
145 GtkWidget *production_combo;
146 GtkWidget *buy_command;
147 GtkWidget *improvement_list;
149 GtkWidget *supported_units_frame;
150 GtkWidget *supported_unit_table;
152 GtkWidget *present_units_frame;
153 GtkWidget *present_unit_table;
155 struct unit_node_vector supported_units;
156 struct unit_node_vector present_units;
158 GtkWidget *info_ebox[NUM_INFO_FIELDS];
159 GtkWidget *info_label[NUM_INFO_FIELDS];
161 GtkListStore* change_production_store;
162 } overview;
164 struct {
165 GtkWidget *production_label;
166 GtkWidget *production_bar;
167 GtkWidget *buy_command;
168 GtkWidget *worklist;
169 } production;
171 struct {
172 struct city_map_canvas map_canvas;
174 GtkWidget *widget;
175 GtkWidget *info_ebox[NUM_INFO_FIELDS];
176 GtkWidget *info_label[NUM_INFO_FIELDS];
177 GtkWidget *citizens;
178 } happiness;
180 struct cma_dialog *cma_editor;
182 struct {
183 GtkWidget *rename_command;
184 GtkWidget *new_citizens_radio[3];
185 GtkWidget *disband_on_settler;
186 GtkWidget *whichtab_radio[NUM_PAGES];
187 short block_signal;
188 } misc;
190 GtkWidget *buy_shell, *sell_shell;
191 GtkTreeSelection *change_selection;
192 GtkWidget *rename_shell, *rename_input;
194 GtkWidget *show_units_command;
195 GtkWidget *prev_command, *next_command;
197 Impr_type_id sell_id;
199 int cwidth;
202 static struct dialog_list *dialog_list;
203 static bool city_dialogs_have_been_initialised = FALSE;
204 static int canvas_width, canvas_height;
205 static int new_dialog_def_page = OVERVIEW_PAGE;
206 static int last_page = OVERVIEW_PAGE;
208 static bool is_showing_workertask_dialog = FALSE;
210 static struct
212 struct city *owner;
213 struct tile *loc;
214 } workertask_req;
216 static bool low_citydlg;
218 /****************************************/
220 static void initialize_city_dialogs(void);
221 static void city_dialog_map_create(struct city_dialog *pdialog,
222 struct city_map_canvas *cmap_canvas);
223 static void city_dialog_map_recenter(GtkWidget *map_canvas_sw);
225 static struct city_dialog *get_city_dialog(struct city *pcity);
226 static gboolean keyboard_handler(GtkWidget * widget, GdkEventKey * event,
227 struct city_dialog *pdialog);
229 static GtkWidget *create_city_info_table(struct city_dialog *pdialog,
230 GtkWidget **info_ebox,
231 GtkWidget **info_label);
232 static void create_and_append_overview_page(struct city_dialog *pdialog);
233 static void create_and_append_map_page(struct city_dialog *pdialog);
234 static void create_and_append_buildings_page(struct city_dialog *pdialog);
235 static void create_and_append_worklist_page(struct city_dialog *pdialog);
236 static void create_and_append_happiness_page(struct city_dialog *pdialog);
237 static void create_and_append_cma_page(struct city_dialog *pdialog);
238 static void create_and_append_settings_page(struct city_dialog *pdialog);
240 static struct city_dialog *create_city_dialog(struct city *pcity);
242 static void city_dialog_update_title(struct city_dialog *pdialog);
243 static void city_dialog_update_citizens(struct city_dialog *pdialog);
244 static void city_dialog_update_information(GtkWidget **info_ebox,
245 GtkWidget **info_label,
246 struct city_dialog *pdialog);
247 static void city_dialog_update_map(struct city_dialog *pdialog);
248 static void city_dialog_update_building(struct city_dialog *pdialog);
249 static void city_dialog_update_improvement_list(struct city_dialog
250 *pdialog);
251 static void city_dialog_update_supported_units(struct city_dialog
252 *pdialog);
253 static void city_dialog_update_present_units(struct city_dialog *pdialog);
254 static void city_dialog_update_prev_next(void);
256 static void show_units_response(void *data);
258 static gboolean supported_unit_callback(GtkWidget * w, GdkEventButton * ev,
259 gpointer data);
260 static gboolean present_unit_callback(GtkWidget * w, GdkEventButton * ev,
261 gpointer data);
262 static gboolean supported_unit_middle_callback(GtkWidget * w,
263 GdkEventButton * ev,
264 gpointer data);
265 static gboolean present_unit_middle_callback(GtkWidget * w,
266 GdkEventButton * ev,
267 gpointer data);
269 static void unit_center_callback(GtkWidget * w, gpointer data);
270 static void unit_activate_callback(GtkWidget * w, gpointer data);
271 static void supported_unit_activate_close_callback(GtkWidget * w,
272 gpointer data);
273 static void present_unit_activate_close_callback(GtkWidget * w,
274 gpointer data);
275 static void unit_load_callback(GtkWidget * w, gpointer data);
276 static void unit_unload_callback(GtkWidget * w, gpointer data);
277 static void unit_sentry_callback(GtkWidget * w, gpointer data);
278 static void unit_fortify_callback(GtkWidget * w, gpointer data);
279 static void unit_disband_callback(GtkWidget * w, gpointer data);
280 static void unit_homecity_callback(GtkWidget * w, gpointer data);
281 static void unit_upgrade_callback(GtkWidget * w, gpointer data);
283 static gboolean citizens_callback(GtkWidget * w, GdkEventButton * ev,
284 gpointer data);
285 static gboolean button_down_citymap(GtkWidget * w, GdkEventButton * ev,
286 gpointer data);
287 static void draw_map_canvas(struct city_dialog *pdialog);
289 static void buy_callback(GtkWidget * w, gpointer data);
290 static void change_production_callback(GtkComboBox *combo,
291 struct city_dialog *pdialog);
293 static void sell_callback(struct impr_type *pimprove, gpointer data);
294 static void sell_callback_response(GtkWidget *w, gint response, gpointer data);
296 static void impr_callback(GtkTreeView *view, GtkTreePath *path,
297 GtkTreeViewColumn *col, gpointer data);
299 static void rename_callback(GtkWidget * w, gpointer data);
300 static void rename_popup_callback(gpointer data, gint response,
301 const char *input);
302 static void set_cityopt_values(struct city_dialog *pdialog);
303 static void cityopt_callback(GtkWidget * w, gpointer data);
304 static void misc_whichtab_callback(GtkWidget * w, gpointer data);
306 static void city_destroy_callback(GtkWidget *w, gpointer data);
307 static void close_city_dialog(struct city_dialog *pdialog);
308 static void citydlg_response_callback(GtkDialog *dlg, gint response,
309 void *data);
310 static void close_callback(GtkWidget *w, gpointer data);
311 static void switch_city_callback(GtkWidget *w, gpointer data);
313 /****************************************************************
314 Called to set the dimensions of the city dialog, both on
315 startup and if the tileset is changed.
316 *****************************************************************/
317 static void init_citydlg_dimensions(void)
319 canvas_width = get_citydlg_canvas_width();
320 canvas_height = get_citydlg_canvas_height();
323 /****************************************************************
324 Initialize stuff needed for city dialogs
325 *****************************************************************/
326 static void initialize_city_dialogs(void)
328 int height;
330 fc_assert_ret(!city_dialogs_have_been_initialised);
332 dialog_list = dialog_list_new();
333 init_citydlg_dimensions();
334 height = screen_height();
336 /* Use default layout when height cannot be determined
337 * (when height == 0) */
338 if (height > 0 && height <= TINYSCREEN_MAX_HEIGHT) {
339 low_citydlg = TRUE;
340 } else {
341 low_citydlg = FALSE;
344 city_dialogs_have_been_initialised = TRUE;
347 /****************************************************************
348 Called when the tileset changes.
349 *****************************************************************/
350 void reset_city_dialogs(void)
352 if (!city_dialogs_have_been_initialised) {
353 return;
356 init_citydlg_dimensions();
358 dialog_list_iterate(dialog_list, pdialog) {
359 /* There's no reasonable way to resize a GtkImage, so we don't try.
360 Instead we just redraw the overview within the existing area. The
361 player has to close and reopen the dialog to fix this. */
362 city_dialog_update_map(pdialog);
363 } dialog_list_iterate_end;
365 popdown_all_city_dialogs();
368 /****************************************************************
369 Return city dialog of the given city, or NULL is it doesn't
370 already exist
371 *****************************************************************/
372 static struct city_dialog *get_city_dialog(struct city *pcity)
374 if (!city_dialogs_have_been_initialised) {
375 initialize_city_dialogs();
378 dialog_list_iterate(dialog_list, pdialog) {
379 if (pdialog->pcity == pcity)
380 return pdialog;
382 dialog_list_iterate_end;
383 return NULL;
386 /***************************************************************************
387 Redraw map canvas on expose.
388 ****************************************************************************/
389 static gboolean canvas_exposed_cb(GtkWidget *w, cairo_t *cr,
390 gpointer data)
392 struct city_dialog *pdialog = data;
394 cairo_scale(cr, CITYMAP_SCALE, CITYMAP_SCALE);
395 cairo_set_source_surface(cr, pdialog->map_canvas_store_unscaled, 0, 0);
396 if (!gtk_widget_get_sensitive(pdialog->overview.map_canvas.ebox)) {
397 cairo_paint_with_alpha(cr, 0.5);
398 } else {
399 cairo_paint(cr);
402 return TRUE;
405 /***************************************************************************
406 Create a city map widget; used in the overview and in the happiness page.
407 ****************************************************************************/
408 static void city_dialog_map_create(struct city_dialog *pdialog,
409 struct city_map_canvas *cmap_canvas)
411 GtkWidget *sw, *ebox, *darea;
413 sw = gtk_scrolled_window_new(NULL, NULL);
414 gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(sw),
415 CITYMAP_WIDTH);
416 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw),
417 CITYMAP_HEIGHT);
418 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
419 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
420 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
421 GTK_SHADOW_NONE);
423 ebox = gtk_event_box_new();
424 gtk_widget_set_halign(ebox, GTK_ALIGN_CENTER);
425 gtk_widget_set_valign(ebox, GTK_ALIGN_CENTER);
426 gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
427 gtk_container_add(GTK_CONTAINER(sw), ebox);
429 darea = gtk_drawing_area_new();
430 gtk_widget_add_events(darea, GDK_BUTTON_PRESS_MASK);
431 gtk_widget_set_size_request(darea, CITYMAP_WIDTH, CITYMAP_HEIGHT);
432 g_signal_connect(ebox, "button-press-event",
433 G_CALLBACK(button_down_citymap), pdialog);
434 g_signal_connect(darea, "draw",
435 G_CALLBACK(canvas_exposed_cb), pdialog);
436 gtk_container_add(GTK_CONTAINER(ebox), darea);
438 /* save all widgets for the city map */
439 cmap_canvas->sw = sw;
440 cmap_canvas->ebox = ebox;
441 cmap_canvas->darea = darea;
444 /****************************************************************
445 Center city dialog map.
446 *****************************************************************/
447 static void city_dialog_map_recenter(GtkWidget *map_canvas_sw) {
448 GtkAdjustment *adjust = NULL;
449 gdouble value;
451 fc_assert_ret(map_canvas_sw != NULL);
453 adjust = gtk_scrolled_window_get_hadjustment(
454 GTK_SCROLLED_WINDOW(map_canvas_sw));
455 value = (gtk_adjustment_get_lower(adjust)
456 + gtk_adjustment_get_upper(adjust)
457 - gtk_adjustment_get_page_size(adjust)) / 2;
458 gtk_adjustment_set_value(adjust, value);
459 gtk_adjustment_value_changed(adjust);
461 adjust = gtk_scrolled_window_get_vadjustment(
462 GTK_SCROLLED_WINDOW(map_canvas_sw));
463 value = (gtk_adjustment_get_lower(adjust)
464 + gtk_adjustment_get_upper(adjust)
465 - gtk_adjustment_get_page_size(adjust)) / 2;
466 gtk_adjustment_set_value(adjust, value);
467 gtk_adjustment_value_changed(adjust);
470 /****************************************************************
471 Refresh city dialog of the given city
472 *****************************************************************/
473 void real_city_dialog_refresh(struct city *pcity)
475 struct city_dialog *pdialog = get_city_dialog(pcity);
477 log_debug("CITYMAP_WIDTH: %d", CITYMAP_WIDTH);
478 log_debug("CITYMAP_HEIGHT: %d", CITYMAP_HEIGHT);
479 log_debug("CITYMAP_SCALE: %.3f", CITYMAP_SCALE);
481 if (city_owner(pcity) == client.conn.playing) {
482 city_report_dialog_update_city(pcity);
483 economy_report_dialog_update();
486 if (!pdialog)
487 return;
489 city_dialog_update_title(pdialog);
490 city_dialog_update_citizens(pdialog);
491 city_dialog_update_information(pdialog->overview.info_ebox,
492 pdialog->overview.info_label, pdialog);
493 city_dialog_update_map(pdialog);
494 city_dialog_update_building(pdialog);
495 city_dialog_update_improvement_list(pdialog);
496 city_dialog_update_supported_units(pdialog);
497 city_dialog_update_present_units(pdialog);
499 if (!client_has_player() || city_owner(pcity) == client_player()) {
500 bool have_present_units = (unit_list_size(pcity->tile->units) > 0);
502 refresh_worklist(pdialog->production.worklist);
504 if (!low_citydlg) {
505 city_dialog_update_information(pdialog->happiness.info_ebox,
506 pdialog->happiness.info_label, pdialog);
508 refresh_happiness_dialog(pdialog->pcity);
509 if (game.info.citizen_nationality) {
510 citizens_dialog_refresh(pdialog->pcity);
513 if (!client_is_observer()) {
514 refresh_cma_dialog(pdialog->pcity, REFRESH_ALL);
517 gtk_widget_set_sensitive(pdialog->show_units_command,
518 can_client_issue_orders() &&
519 have_present_units);
520 } else {
521 /* Set the buttons we do not want live while a Diplomat investigates */
522 gtk_widget_set_sensitive(pdialog->show_units_command, FALSE);
526 /****************************************************************
527 Refresh city dialogs of unit's homecity and city where unit
528 currently is.
529 *****************************************************************/
530 void refresh_unit_city_dialogs(struct unit *punit)
532 struct city *pcity_sup, *pcity_pre;
533 struct city_dialog *pdialog;
535 pcity_sup = game_city_by_number(punit->homecity);
536 pcity_pre = tile_city(unit_tile(punit));
538 if (pcity_sup && (pdialog = get_city_dialog(pcity_sup))) {
539 city_dialog_update_supported_units(pdialog);
542 if (pcity_pre && (pdialog = get_city_dialog(pcity_pre))) {
543 city_dialog_update_present_units(pdialog);
547 /****************************************************************
548 popup the dialog 10% inside the main-window
549 *****************************************************************/
550 void real_city_dialog_popup(struct city *pcity)
552 struct city_dialog *pdialog;
554 if (!(pdialog = get_city_dialog(pcity))) {
555 pdialog = create_city_dialog(pcity);
558 gtk_window_present(GTK_WINDOW(pdialog->shell));
560 /* center the city map(s); this must be *after* the city dialog was drawn
561 * else the size information is missing! */
562 city_dialog_map_recenter(pdialog->overview.map_canvas.sw);
563 if (pdialog->happiness.map_canvas.sw) {
564 city_dialog_map_recenter(pdialog->happiness.map_canvas.sw);
568 /****************************************************************
569 Return whether city dialog for given city is open
570 *****************************************************************/
571 bool city_dialog_is_open(struct city *pcity)
573 return get_city_dialog(pcity) != NULL;
576 /****************************************************************
577 popdown the dialog
578 *****************************************************************/
579 void popdown_city_dialog(struct city *pcity)
581 struct city_dialog *pdialog = get_city_dialog(pcity);
583 if (pdialog) {
584 close_city_dialog(pdialog);
588 /****************************************************************
589 popdown all dialogs
590 *****************************************************************/
591 void popdown_all_city_dialogs(void)
593 if (!city_dialogs_have_been_initialised) {
594 return;
597 while (dialog_list_size(dialog_list)) {
598 close_city_dialog(dialog_list_get(dialog_list, 0));
600 dialog_list_destroy(dialog_list);
602 city_dialogs_have_been_initialised = FALSE;
605 /**************************************************************************
606 Keyboard handler for city dialog
607 **************************************************************************/
608 static gboolean keyboard_handler(GtkWidget * widget, GdkEventKey * event,
609 struct city_dialog *pdialog)
611 if (event->state & GDK_CONTROL_MASK) {
612 switch (event->keyval) {
613 case GDK_KEY_Left:
614 gtk_notebook_prev_page(GTK_NOTEBOOK(pdialog->notebook));
615 return TRUE;
617 case GDK_KEY_Right:
618 gtk_notebook_next_page(GTK_NOTEBOOK(pdialog->notebook));
619 return TRUE;
621 default:
622 break;
626 return FALSE;
629 /**************************************************************************
630 Destroy info popup dialog when button released
631 **************************************************************************/
632 static gboolean show_info_button_release(GtkWidget *w, GdkEventButton *ev,
633 gpointer data)
635 gtk_grab_remove(w);
636 gdk_device_ungrab(ev->device, ev->time);
637 gtk_widget_destroy(w);
638 return FALSE;
641 enum { FIELD_FOOD, FIELD_SHIELD, FIELD_TRADE, FIELD_GOLD, FIELD_LUXURY,
642 FIELD_SCIENCE, FIELD_GRANARY, FIELD_GROWTH, FIELD_CORRUPTION,
643 FIELD_WASTE, FIELD_CULTURE, FIELD_POLLUTION, FIELD_ILLNESS
646 /****************************************************************
647 Popup info dialog
648 *****************************************************************/
649 static gboolean show_info_popup(GtkWidget *w, GdkEventButton *ev,
650 gpointer data)
652 struct city_dialog *pdialog = g_object_get_data(G_OBJECT(w), "pdialog");
654 if (ev->button == 1) {
655 GtkWidget *p, *label, *frame;
656 char buf[1024];
658 switch (GPOINTER_TO_UINT(data)) {
659 case FIELD_FOOD:
660 get_city_dialog_output_text(pdialog->pcity, O_FOOD, buf, sizeof(buf));
661 break;
662 case FIELD_SHIELD:
663 get_city_dialog_output_text(pdialog->pcity, O_SHIELD,
664 buf, sizeof(buf));
665 break;
666 case FIELD_TRADE:
667 get_city_dialog_output_text(pdialog->pcity, O_TRADE, buf, sizeof(buf));
668 break;
669 case FIELD_GOLD:
670 get_city_dialog_output_text(pdialog->pcity, O_GOLD, buf, sizeof(buf));
671 break;
672 case FIELD_SCIENCE:
673 get_city_dialog_output_text(pdialog->pcity, O_SCIENCE,
674 buf, sizeof(buf));
675 break;
676 case FIELD_LUXURY:
677 get_city_dialog_output_text(pdialog->pcity, O_LUXURY,
678 buf, sizeof(buf));
679 break;
680 case FIELD_CULTURE:
681 get_city_dialog_culture_text(pdialog->pcity, buf, sizeof(buf));
682 break;
683 case FIELD_POLLUTION:
684 get_city_dialog_pollution_text(pdialog->pcity, buf, sizeof(buf));
685 break;
686 case FIELD_ILLNESS:
687 get_city_dialog_illness_text(pdialog->pcity, buf, sizeof(buf));
688 break;
689 default:
690 return TRUE;
693 p = gtk_window_new(GTK_WINDOW_POPUP);
694 gtk_widget_set_name(p, "Freeciv");
695 gtk_container_set_border_width(GTK_CONTAINER(p), 2);
696 gtk_window_set_position(GTK_WINDOW(p), GTK_WIN_POS_MOUSE);
698 frame = gtk_frame_new(NULL);
699 gtk_container_add(GTK_CONTAINER(p), frame);
701 label = gtk_label_new(buf);
702 gtk_widget_set_name(label, "city_info_label");
703 gtk_widget_set_margin_left(label, 4);
704 gtk_widget_set_margin_right(label, 4);
705 gtk_widget_set_margin_top(label, 4);
706 gtk_widget_set_margin_bottom(label, 4);
707 gtk_container_add(GTK_CONTAINER(frame), label);
708 gtk_widget_show_all(p);
710 gdk_device_grab(ev->device, gtk_widget_get_window(p),
711 GDK_OWNERSHIP_NONE, TRUE, GDK_BUTTON_RELEASE_MASK, NULL,
712 ev->time);
713 gtk_grab_add(p);
715 g_signal_connect_after(p, "button_release_event",
716 G_CALLBACK(show_info_button_release), NULL);
718 return TRUE;
721 /****************************************************************
722 used once in the overview page and once in the happiness page
723 **info_label points to the info_label in the respective struct
724 ****************************************************************/
725 static GtkWidget *create_city_info_table(struct city_dialog *pdialog,
726 GtkWidget **info_ebox,
727 GtkWidget **info_label)
729 int i;
730 GtkWidget *table, *label, *ebox;
732 static const char *output_label[NUM_INFO_FIELDS] = { N_("Food:"),
733 N_("Prod:"),
734 N_("Trade:"),
735 N_("Gold:"),
736 N_("Luxury:"),
737 N_("Science:"),
738 N_("Granary:"),
739 N_("Change in:"),
740 N_("Corruption:"),
741 N_("Waste:"),
742 N_("Culture:"),
743 N_("Pollution:"),
744 N_("Plague Risk:")
746 static bool output_label_done;
748 table = gtk_grid_new();
749 g_object_set(table, "margin", 4, NULL);
751 intl_slist(ARRAY_SIZE(output_label), output_label, &output_label_done);
753 for (i = 0; i < NUM_INFO_FIELDS; i++) {
754 label = gtk_label_new(output_label[i]);
755 switch (i) {
756 case 2:
757 case 5:
758 case 7:
759 gtk_widget_set_margin_bottom(label, 5);
760 break;
761 case 3:
762 case 6:
763 case 8:
764 gtk_widget_set_margin_top(label, 5);
765 break;
766 default:
767 break;
769 gtk_widget_set_margin_right(label, 5);
770 gtk_widget_set_name(label, "city_label"); /* for font style? */
771 gtk_widget_set_halign(label, GTK_ALIGN_START);
772 gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
773 gtk_grid_attach(GTK_GRID(table), label, 0, i, 1, 1);
775 ebox = gtk_event_box_new();
776 switch (i) {
777 case 2:
778 case 5:
779 case 7:
780 gtk_widget_set_margin_bottom(ebox, 5);
781 break;
782 case 3:
783 case 6:
784 case 8:
785 gtk_widget_set_margin_top(ebox, 5);
786 break;
787 default:
788 break;
790 gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
791 g_object_set_data(G_OBJECT(ebox), "pdialog", pdialog);
792 g_signal_connect(ebox, "button_press_event",
793 G_CALLBACK(show_info_popup), GUINT_TO_POINTER(i));
794 info_ebox[i] = ebox;
796 label = gtk_label_new("");
797 info_label[i] = label;
798 gtk_widget_set_name(label, "city_label"); /* ditto */
799 gtk_widget_set_halign(label, GTK_ALIGN_START);
800 gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
802 gtk_container_add(GTK_CONTAINER(ebox), label);
804 gtk_grid_attach(GTK_GRID(table), ebox, 1, i, 1, 1);
807 gtk_widget_show_all(table);
809 return table;
812 /****************************************************************
813 Create main citydlg map
814 *****************************************************************/
815 static void create_citydlg_main_map(struct city_dialog *pdialog,
816 GtkWidget *container)
818 GtkWidget *frame;
820 frame = gtk_frame_new(_("City map"));
821 gtk_widget_set_size_request(frame, CITY_MAP_MIN_SIZE_X,
822 CITY_MAP_MIN_SIZE_Y);
823 gtk_container_add(GTK_CONTAINER(container), frame);
825 city_dialog_map_create(pdialog, &pdialog->overview.map_canvas);
826 gtk_container_add(GTK_CONTAINER(frame), pdialog->overview.map_canvas.sw);
829 /****************************************************************
830 Create improvements list
831 *****************************************************************/
832 static GtkWidget *create_citydlg_improvement_list(struct city_dialog *pdialog,
833 GtkWidget *vbox)
835 GtkWidget *view;
836 GtkListStore *store;
837 GtkCellRenderer *rend;
839 /* improvements */
840 store = gtk_list_store_new(5, G_TYPE_POINTER, GDK_TYPE_PIXBUF,
841 G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
843 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
844 gtk_widget_set_hexpand(view, TRUE);
845 gtk_widget_set_vexpand(view, TRUE);
846 g_object_unref(store);
847 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
848 gtk_widget_set_name(view, "small_font");
849 pdialog->overview.improvement_list = view;
851 gtk_widget_set_tooltip_markup(view,
852 _("Press <b>ENTER</b> or double-click to sell an improvement."));
854 rend = gtk_cell_renderer_pixbuf_new();
855 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL,
856 rend, "pixbuf", 1, NULL);
857 rend = gtk_cell_renderer_text_new();
858 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL,
859 rend, "text", 2,
860 "strikethrough", 4, NULL);
861 rend = gtk_cell_renderer_text_new();
862 g_object_set(rend, "xalign", 1.0, NULL);
863 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL,
864 rend, "text", 3,
865 "strikethrough", 4, NULL);
867 g_signal_connect(view, "row_activated", G_CALLBACK(impr_callback),
868 pdialog);
870 return view;
873 /****************************************************************
874 **** Overview page ****
875 +- GtkWidget *page ------------------------------------------+
876 | +- GtkWidget *middle -----------+------------------------+ |
877 | | City map | Production | |
878 | +-------------------------------+------------------------+ |
879 +------------------------------------------------------------+
880 | +- GtkWidget *bottom -------+----------------------------+ |
881 | | Info | +- GtkWidget *right -----+ | |
882 | | | | supported units | | |
883 | | | +------------------------+ | |
884 | | | | present units | | |
885 | | | +------------------------+ | |
886 | +---------------------------+----------------------------+ |
887 +------------------------------------------------------------+
888 *****************************************************************/
889 static void create_and_append_overview_page(struct city_dialog *pdialog)
891 GtkWidget *page, *bottom;
892 GtkWidget *hbox, *right, *vbox, *frame, *table;
893 GtkWidget *label, *sw, *view, *bar, *production_combo;
894 GtkCellRenderer *rend;
895 GtkListStore *production_store;
896 /* TRANS: Overview tab in city dialog */
897 const char *tab_title = _("_Overview");
898 int unit_height = tileset_unit_with_upkeep_height(tileset);
900 /* main page */
901 page = gtk_grid_new();
902 gtk_orientable_set_orientation(GTK_ORIENTABLE(page),
903 GTK_ORIENTATION_VERTICAL);
904 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
905 label = gtk_label_new_with_mnemonic(tab_title);
906 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
908 if (!low_citydlg) {
909 GtkWidget *middle;
911 /* middle: city map, improvements */
912 middle = gtk_grid_new();
913 gtk_grid_set_column_spacing(GTK_GRID(middle), 6);
914 gtk_container_add(GTK_CONTAINER(page), middle);
916 /* city map */
917 create_citydlg_main_map(pdialog, middle);
919 /* improvements */
920 vbox = gtk_grid_new();
921 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox),
922 GTK_ORIENTATION_VERTICAL);
923 gtk_container_add(GTK_CONTAINER(middle), vbox);
925 view = create_citydlg_improvement_list(pdialog, middle);
927 label = g_object_new(GTK_TYPE_LABEL, "label", _("Production:"),
928 "xalign", 0.0, "yalign", 0.5, NULL);
929 gtk_container_add(GTK_CONTAINER(vbox), label);
931 hbox = gtk_grid_new();
932 gtk_grid_set_column_spacing(GTK_GRID(hbox), 10);
933 gtk_container_add(GTK_CONTAINER(vbox), hbox);
935 production_store = gtk_list_store_new(4, GDK_TYPE_PIXBUF, G_TYPE_STRING,
936 G_TYPE_INT, G_TYPE_BOOLEAN);
937 pdialog->overview.change_production_store = production_store;
939 production_combo =
940 gtk_combo_box_new_with_model(GTK_TREE_MODEL(production_store));
941 gtk_widget_set_hexpand(production_combo, TRUE);
942 pdialog->overview.production_combo = production_combo;
943 gtk_container_add(GTK_CONTAINER(hbox), production_combo);
944 g_object_unref(production_store);
945 g_signal_connect(production_combo, "changed",
946 G_CALLBACK(change_production_callback), pdialog);
948 gtk_cell_layout_clear(GTK_CELL_LAYOUT(production_combo));
949 rend = gtk_cell_renderer_pixbuf_new();
950 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(production_combo), rend, TRUE);
951 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(production_combo),
952 rend, "pixbuf", 0, NULL);
953 g_object_set(rend, "xalign", 0.0, NULL);
955 rend = gtk_cell_renderer_text_new();
956 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(production_combo), rend, TRUE);
957 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(production_combo),
958 rend, "text", 1, "strikethrough", 3, NULL);
960 bar = gtk_progress_bar_new();
961 gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(bar), TRUE);
962 pdialog->overview.production_bar = bar;
963 gtk_container_add(GTK_CONTAINER(production_combo), bar);
964 gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(production_combo), 3);
966 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(bar), _("%d/%d %d turns"));
968 pdialog->overview.buy_command = gtk_stockbutton_new(GTK_STOCK_EXECUTE,
969 _("_Buy"));
970 gtk_container_add(GTK_CONTAINER(hbox), pdialog->overview.buy_command);
971 g_signal_connect(pdialog->overview.buy_command, "clicked",
972 G_CALLBACK(buy_callback), pdialog);
974 label = g_object_new(GTK_TYPE_LABEL, "use-underline", TRUE,
975 "mnemonic-widget", view,
976 "label", _("I_mprovements:"),
977 "xalign", 0.0, "yalign", 0.5, NULL);
978 gtk_container_add(GTK_CONTAINER(vbox), label);
980 sw = gtk_scrolled_window_new(NULL, NULL);
981 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
982 GTK_SHADOW_ETCHED_IN);
983 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
984 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
985 gtk_container_add(GTK_CONTAINER(vbox), sw);
987 gtk_container_add(GTK_CONTAINER(sw), view);
988 } else {
989 pdialog->overview.buy_command = NULL;
990 pdialog->overview.production_bar = NULL;
991 pdialog->overview.production_combo = NULL;
992 pdialog->overview.change_production_store = NULL;
995 /* bottom: info, units */
996 bottom = gtk_grid_new();
997 gtk_grid_set_column_spacing(GTK_GRID(bottom), 6);
998 gtk_container_add(GTK_CONTAINER(page), bottom);
1000 /* info */
1001 frame = gtk_frame_new(_("Info"));
1002 gtk_container_add(GTK_CONTAINER(bottom), frame);
1004 table = create_city_info_table(pdialog,
1005 pdialog->overview.info_ebox,
1006 pdialog->overview.info_label);
1007 gtk_widget_set_halign(table, GTK_ALIGN_CENTER);
1008 gtk_widget_set_valign(table, GTK_ALIGN_CENTER);
1009 gtk_container_add(GTK_CONTAINER(frame), table);
1011 /* right: present and supported units (overview page) */
1012 right = gtk_grid_new();
1013 gtk_orientable_set_orientation(GTK_ORIENTABLE(right),
1014 GTK_ORIENTATION_VERTICAL);
1015 gtk_container_add(GTK_CONTAINER(bottom), right);
1017 pdialog->overview.supported_units_frame = gtk_frame_new("");
1018 gtk_container_add(GTK_CONTAINER(right),
1019 pdialog->overview.supported_units_frame);
1020 pdialog->overview.present_units_frame = gtk_frame_new("");
1021 gtk_container_add(GTK_CONTAINER(right),
1022 pdialog->overview.present_units_frame);
1024 /* supported units */
1025 sw = gtk_scrolled_window_new(NULL, NULL);
1026 gtk_widget_set_hexpand(sw, TRUE);
1027 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1028 GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
1029 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1030 GTK_SHADOW_NONE);
1031 gtk_container_add(GTK_CONTAINER(pdialog->overview.supported_units_frame),
1032 sw);
1035 table = gtk_grid_new();
1036 gtk_grid_set_column_spacing(GTK_GRID(table), 2);
1037 gtk_widget_set_size_request(table, -1, unit_height);
1038 gtk_container_add(GTK_CONTAINER(sw), table);
1040 gtk_container_set_focus_hadjustment(GTK_CONTAINER(table),
1041 gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(sw)));
1042 gtk_container_set_focus_vadjustment(GTK_CONTAINER(table),
1043 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw)));
1045 pdialog->overview.supported_unit_table = table;
1046 unit_node_vector_init(&pdialog->overview.supported_units);
1048 /* present units */
1049 sw = gtk_scrolled_window_new(NULL, NULL);
1050 gtk_widget_set_hexpand(sw, TRUE);
1051 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1052 GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
1053 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1054 GTK_SHADOW_NONE);
1055 gtk_container_add(GTK_CONTAINER(pdialog->overview.present_units_frame), sw);
1057 table = gtk_grid_new();
1058 gtk_grid_set_column_spacing(GTK_GRID(table), 2);
1059 gtk_widget_set_size_request(table, -1, unit_height);
1060 gtk_container_add(GTK_CONTAINER(sw), table);
1062 gtk_container_set_focus_hadjustment(GTK_CONTAINER(table),
1063 gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(sw)));
1064 gtk_container_set_focus_vadjustment(GTK_CONTAINER(table),
1065 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw)));
1067 pdialog->overview.present_unit_table = table;
1068 unit_node_vector_init(&pdialog->overview.present_units);
1070 /* show page */
1071 gtk_widget_show_all(page);
1074 /****************************************************************
1075 Create map page for small screens
1076 *****************************************************************/
1077 static void create_and_append_map_page(struct city_dialog *pdialog)
1079 if (low_citydlg) {
1080 GtkWidget *page;
1081 GtkWidget *label;
1082 const char *tab_title = _("Citymap");
1084 page = gtk_grid_new();
1085 gtk_orientable_set_orientation(GTK_ORIENTABLE(page),
1086 GTK_ORIENTATION_VERTICAL);
1087 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1088 label = gtk_label_new_with_mnemonic(tab_title);
1089 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1091 create_citydlg_main_map(pdialog, page);
1093 gtk_widget_show_all(page);
1097 /****************************************************************
1098 Something dragged to worklist dialog.
1099 *****************************************************************/
1100 static void target_drag_data_received(GtkWidget *w,
1101 GdkDragContext *context,
1102 gint x, gint y,
1103 GtkSelectionData *data,
1104 guint info, guint time,
1105 gpointer user_data)
1107 struct city_dialog *pdialog = (struct city_dialog *) user_data;
1108 GtkTreeModel *model;
1109 GtkTreePath *path;
1111 if (NULL != client.conn.playing
1112 && city_owner(pdialog->pcity) != client.conn.playing) {
1113 gtk_drag_finish(context, FALSE, FALSE, time);
1116 if (gtk_tree_get_row_drag_data(data, &model, &path)) {
1117 GtkTreeIter it;
1119 if (gtk_tree_model_get_iter(model, &it, path)) {
1120 cid id;
1121 struct universal univ;
1123 gtk_tree_model_get(model, &it, 0, &id, -1);
1124 univ = cid_production(id);
1125 city_change_production(pdialog->pcity, &univ);
1126 gtk_drag_finish(context, TRUE, FALSE, time);
1128 gtk_tree_path_free(path);
1131 gtk_drag_finish(context, FALSE, FALSE, time);
1134 /****************************************************************
1135 Create production page header - what tab this actually is,
1136 depends on screen size and layout.
1137 *****************************************************************/
1138 static void create_production_header(struct city_dialog *pdialog, GtkContainer *contain)
1140 GtkWidget *hbox, *bar;
1142 hbox = gtk_grid_new();
1143 g_object_set(hbox, "margin", 2, NULL);
1144 gtk_grid_set_column_spacing(GTK_GRID(hbox), 10);
1145 gtk_container_add(contain, hbox);
1147 /* The label is set in city_dialog_update_building() */
1148 bar = gtk_progress_bar_new();
1149 gtk_widget_set_hexpand(bar, TRUE);
1150 gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(bar), TRUE);
1151 pdialog->production.production_bar = bar;
1152 gtk_container_add(GTK_CONTAINER(hbox), bar);
1153 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(bar), _("%d/%d %d turns"));
1155 add_worklist_dnd_target(bar);
1157 g_signal_connect(bar, "drag_data_received",
1158 G_CALLBACK(target_drag_data_received), pdialog);
1160 pdialog->production.buy_command = gtk_stockbutton_new(GTK_STOCK_EXECUTE,
1161 _("_Buy"));
1162 gtk_container_add(GTK_CONTAINER(hbox), pdialog->production.buy_command);
1164 g_signal_connect(pdialog->production.buy_command, "clicked",
1165 G_CALLBACK(buy_callback), pdialog);
1168 /****************************************************************
1169 Create buildings list page for small screens
1170 *****************************************************************/
1171 static void create_and_append_buildings_page(struct city_dialog *pdialog)
1173 if (low_citydlg) {
1174 GtkWidget *page;
1175 GtkWidget *label;
1176 GtkWidget *vbox;
1177 GtkWidget *view;
1178 const char *tab_title = _("Buildings");
1180 page = gtk_grid_new();
1181 gtk_orientable_set_orientation(GTK_ORIENTABLE(page),
1182 GTK_ORIENTATION_VERTICAL);
1183 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1184 label = gtk_label_new_with_mnemonic(tab_title);
1186 create_production_header(pdialog, GTK_CONTAINER(page));
1187 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1189 vbox = gtk_grid_new();
1190 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox),
1191 GTK_ORIENTATION_VERTICAL);
1192 gtk_container_add(GTK_CONTAINER(page), vbox);
1194 view = create_citydlg_improvement_list(pdialog, vbox);
1196 gtk_container_add(GTK_CONTAINER(vbox), view);
1198 gtk_widget_show_all(page);
1202 /****************************************************************
1203 **** Production Page ****
1204 *****************************************************************/
1205 static void create_and_append_worklist_page(struct city_dialog *pdialog)
1207 const char *tab_title = _("P_roduction");
1208 GtkWidget *label = gtk_label_new_with_mnemonic(tab_title);
1209 GtkWidget *page, *editor;
1211 page = gtk_grid_new();
1212 gtk_orientable_set_orientation(GTK_ORIENTABLE(page),
1213 GTK_ORIENTATION_VERTICAL);
1214 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1215 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1217 /* stuff that's being currently built */
1218 if (!low_citydlg) {
1219 label = g_object_new(GTK_TYPE_LABEL,
1220 "label", _("Production:"),
1221 "xalign", 0.0, "yalign", 0.5, NULL);
1222 pdialog->production.production_label = label;
1223 gtk_container_add(GTK_CONTAINER(page), label);
1225 create_production_header(pdialog, GTK_CONTAINER(page));
1226 } else {
1227 pdialog->production.production_label = NULL;
1230 editor = create_worklist();
1231 g_object_set(editor, "margin", 6, NULL);
1232 reset_city_worklist(editor, pdialog->pcity);
1233 gtk_container_add(GTK_CONTAINER(page), editor);
1234 pdialog->production.worklist = editor;
1236 gtk_widget_show_all(page);
1239 /***************************************************************************
1240 **** Happiness Page ****
1241 +- GtkWidget *page ----------+-------------------------------------------+
1242 | +- GtkWidget *left ------+ | +- GtkWidget *right --------------------+ |
1243 | | Info | | | City map | |
1244 | +- GtkWidget *citizens --+ | +- GtkWidget pdialog->happiness.widget -+ |
1245 | | Citizens data | | | Happiness | |
1246 | +------------------------+ | +---------------------------------------+ |
1247 +----------------------------+-------------------------------------------+
1248 ****************************************************************************/
1249 static void create_and_append_happiness_page(struct city_dialog *pdialog)
1251 GtkWidget *page, *label, *table, *right, *left, *frame;
1252 const char *tab_title = _("Happ_iness");
1254 /* main page */
1255 page = gtk_grid_new();
1256 gtk_grid_set_column_spacing(GTK_GRID(page), 6);
1257 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1258 label = gtk_label_new_with_mnemonic(tab_title);
1259 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1261 /* left: info, citizens */
1262 left = gtk_grid_new();
1263 gtk_orientable_set_orientation(GTK_ORIENTABLE(left),
1264 GTK_ORIENTATION_VERTICAL);
1265 gtk_container_add(GTK_CONTAINER(page), left);
1267 if (!low_citydlg) {
1268 /* upper left: info */
1269 frame = gtk_frame_new(_("Info"));
1270 gtk_container_add(GTK_CONTAINER(left), frame);
1272 table = create_city_info_table(pdialog,
1273 pdialog->happiness.info_ebox,
1274 pdialog->happiness.info_label);
1275 gtk_widget_set_halign(table, GTK_ALIGN_CENTER);
1276 gtk_container_add(GTK_CONTAINER(frame), table);
1279 /* lower left: citizens */
1280 if (game.info.citizen_nationality) {
1281 pdialog->happiness.citizens = gtk_grid_new();
1282 gtk_orientable_set_orientation(
1283 GTK_ORIENTABLE(pdialog->happiness.citizens),
1284 GTK_ORIENTATION_VERTICAL);
1285 gtk_container_add(GTK_CONTAINER(left), pdialog->happiness.citizens);
1286 gtk_container_add(GTK_CONTAINER(pdialog->happiness.citizens),
1287 citizens_dialog_display(pdialog->pcity));
1290 /* right: city map, happiness */
1291 right = gtk_grid_new();
1292 gtk_orientable_set_orientation(GTK_ORIENTABLE(right),
1293 GTK_ORIENTATION_VERTICAL);
1294 gtk_container_add(GTK_CONTAINER(page), right);
1296 if (!low_citydlg) {
1297 /* upper right: city map */
1298 frame = gtk_frame_new(_("City map"));
1299 gtk_widget_set_size_request(frame, CITY_MAP_MIN_SIZE_X,
1300 CITY_MAP_MIN_SIZE_Y);
1301 gtk_container_add(GTK_CONTAINER(right), frame);
1303 city_dialog_map_create(pdialog, &pdialog->happiness.map_canvas);
1304 gtk_container_add(GTK_CONTAINER(frame), pdialog->happiness.map_canvas.sw);
1307 /* lower right: happiness */
1308 pdialog->happiness.widget = gtk_grid_new();
1309 gtk_orientable_set_orientation(GTK_ORIENTABLE(pdialog->happiness.widget),
1310 GTK_ORIENTATION_VERTICAL);
1311 gtk_container_add(GTK_CONTAINER(right), pdialog->happiness.widget);
1312 gtk_container_add(GTK_CONTAINER(pdialog->happiness.widget),
1313 get_top_happiness_display(pdialog->pcity, low_citydlg));
1315 /* show page */
1316 gtk_widget_show_all(page);
1319 /****************************************************************
1320 **** Citizen Management Agent (CMA) Page ****
1321 *****************************************************************/
1322 static void create_and_append_cma_page(struct city_dialog *pdialog)
1324 GtkWidget *page, *label;
1325 const char *tab_title = _("_Governor");
1327 page = gtk_grid_new();
1329 label = gtk_label_new_with_mnemonic(tab_title);
1331 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1333 pdialog->cma_editor = create_cma_dialog(pdialog->pcity, low_citydlg);
1334 gtk_container_add(GTK_CONTAINER(page), pdialog->cma_editor->shell);
1336 gtk_widget_show(page);
1339 /****************************************************************
1340 **** Misc. Settings Page ****
1341 *****************************************************************/
1342 static void create_and_append_settings_page(struct city_dialog *pdialog)
1344 int i;
1345 GtkWidget *vbox2, *page, *frame, *label, *button;
1346 GtkSizeGroup *size;
1347 GSList *group;
1348 const char *tab_title = _("_Settings");
1350 static const char *new_citizens_output_label[] = {
1351 N_("Luxury"),
1352 N_("Science"),
1353 N_("Gold")
1356 static const char *disband_label = N_("Disband if build settler at size 1");
1358 static const char *misc_whichtab_label[NUM_PAGES] = {
1359 N_("Overview page"),
1360 N_("Production page"),
1361 N_("Happiness page"),
1362 N_("Governor page"),
1363 N_("This Settings page"),
1364 N_("Last active page")
1367 static bool new_citizens_label_done;
1368 static bool misc_whichtab_label_done;
1370 /* initialize signal_blocker */
1371 pdialog->misc.block_signal = 0;
1374 page = gtk_grid_new();
1375 gtk_grid_set_column_spacing(GTK_GRID(page), 18);
1376 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
1378 size = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
1380 label = gtk_label_new_with_mnemonic(tab_title);
1382 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
1384 /* new_citizens radio */
1385 frame = gtk_frame_new(_("New citizens produce"));
1386 gtk_grid_attach(GTK_GRID(page), frame, 0, 0, 1, 1);
1387 gtk_size_group_add_widget(size, frame);
1389 vbox2 = gtk_grid_new();
1390 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2),
1391 GTK_ORIENTATION_VERTICAL);
1392 gtk_container_add(GTK_CONTAINER(frame), vbox2);
1394 intl_slist(ARRAY_SIZE(new_citizens_output_label), new_citizens_output_label,
1395 &new_citizens_label_done);
1397 group = NULL;
1398 for (i = 0; i < ARRAY_SIZE(new_citizens_output_label); i++) {
1399 button = gtk_radio_button_new_with_mnemonic(group, new_citizens_output_label[i]);
1400 pdialog->misc.new_citizens_radio[i] = button;
1401 gtk_container_add(GTK_CONTAINER(vbox2), button);
1402 g_signal_connect(button, "toggled",
1403 G_CALLBACK(cityopt_callback), pdialog);
1404 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
1407 /* next is the next-time-open radio group in the right column */
1408 frame = gtk_frame_new(_("Next time open"));
1409 gtk_grid_attach(GTK_GRID(page), frame, 1, 0, 1, 1);
1410 gtk_size_group_add_widget(size, frame);
1412 vbox2 = gtk_grid_new();
1413 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2),
1414 GTK_ORIENTATION_VERTICAL);
1415 gtk_container_add(GTK_CONTAINER(frame), vbox2);
1417 intl_slist(ARRAY_SIZE(misc_whichtab_label), misc_whichtab_label,
1418 &misc_whichtab_label_done);
1420 group = NULL;
1421 for (i = 0; i < ARRAY_SIZE(misc_whichtab_label); i++) {
1422 button = gtk_radio_button_new_with_mnemonic(group, misc_whichtab_label[i]);
1423 pdialog->misc.whichtab_radio[i] = button;
1424 gtk_container_add(GTK_CONTAINER(vbox2), button);
1425 g_signal_connect(button, "toggled",
1426 G_CALLBACK(misc_whichtab_callback), GINT_TO_POINTER(i));
1427 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
1430 /* now we go back and fill the hbox rename */
1431 frame = gtk_frame_new(_("City"));
1432 gtk_widget_set_margin_top(frame, 12);
1433 gtk_widget_set_margin_bottom(frame, 12);
1434 gtk_grid_attach(GTK_GRID(page), frame, 0, 1, 1, 1);
1436 vbox2 = gtk_grid_new();
1437 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2),
1438 GTK_ORIENTATION_VERTICAL);
1439 gtk_container_add(GTK_CONTAINER(frame), vbox2);
1441 button = gtk_button_new_with_mnemonic(_("R_ename..."));
1442 pdialog->misc.rename_command = button;
1443 gtk_container_add(GTK_CONTAINER(vbox2), button);
1444 g_signal_connect(button, "clicked",
1445 G_CALLBACK(rename_callback), pdialog);
1447 gtk_widget_set_sensitive(button, can_client_issue_orders());
1449 /* the disband-if-size-1 button */
1450 button = gtk_check_button_new_with_mnemonic(_(disband_label));
1451 pdialog->misc.disband_on_settler = button;
1452 gtk_container_add(GTK_CONTAINER(vbox2), button);
1453 g_signal_connect(button, "toggled",
1454 G_CALLBACK(cityopt_callback), pdialog);
1456 /* we choose which page to popup by default */
1457 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
1458 (pdialog->
1459 misc.whichtab_radio[new_dialog_def_page]),
1460 TRUE);
1462 set_cityopt_values(pdialog);
1464 gtk_widget_show_all(page);
1466 if (new_dialog_def_page == (NUM_PAGES - 1)) {
1467 gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog->notebook),
1468 last_page);
1469 } else {
1470 gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog->notebook),
1471 new_dialog_def_page);
1478 /****************************************************************
1479 **** Main City Dialog ****
1480 +----------------------------+-------------------------------+
1481 | GtkWidget *top: Citizens | city name |
1482 +----------------------------+-------------------------------+
1483 | <notebook tab> |
1484 +------------------------------------------------------------+
1485 *****************************************************************/
1486 static struct city_dialog *create_city_dialog(struct city *pcity)
1488 struct city_dialog *pdialog;
1489 GtkWidget *close_command;
1490 GtkWidget *vbox, *hbox, *cbox, *ebox;
1491 int citizen_bar_width;
1492 int citizen_bar_height;
1494 if (!city_dialogs_have_been_initialised) {
1495 initialize_city_dialogs();
1498 pdialog = fc_malloc(sizeof(struct city_dialog));
1499 pdialog->pcity = pcity;
1500 pdialog->buy_shell = NULL;
1501 pdialog->sell_shell = NULL;
1502 pdialog->rename_shell = NULL;
1503 pdialog->happiness.map_canvas.sw = NULL; /* make sure NULL if spy */
1504 pdialog->happiness.map_canvas.ebox = NULL; /* ditto */
1505 pdialog->happiness.map_canvas.darea = NULL; /* ditto */
1506 pdialog->happiness.citizens = NULL; /* ditto */
1507 pdialog->cma_editor = NULL;
1508 pdialog->map_canvas_store_unscaled
1509 = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1510 canvas_width, canvas_height);
1512 pdialog->shell = gtk_dialog_new();
1513 gtk_window_set_title(GTK_WINDOW(pdialog->shell), city_name_get(pcity));
1514 setup_dialog(pdialog->shell, toplevel);
1515 gtk_window_set_role(GTK_WINDOW(pdialog->shell), "city");
1517 g_signal_connect(pdialog->shell, "destroy",
1518 G_CALLBACK(city_destroy_callback), pdialog);
1519 gtk_window_set_position(GTK_WINDOW(pdialog->shell), GTK_WIN_POS_MOUSE);
1520 gtk_widget_set_name(pdialog->shell, "Freeciv");
1522 gtk_widget_realize(pdialog->shell);
1524 /* keep the icon of the executable on Windows (see PR#36491) */
1525 #ifndef FREECIV_MSWINDOWS
1527 GdkPixbuf *pixbuf = sprite_get_pixbuf(get_icon_sprite(tileset, ICON_CITYDLG));
1529 /* Only call this after tileset_load_tiles is called. */
1530 gtk_window_set_icon(GTK_WINDOW(pdialog->shell), pixbuf);
1531 g_object_unref(pixbuf);
1533 #endif /* FREECIV_MSWINDOWS */
1535 /* Restore size of the city dialog. */
1536 gtk_window_set_default_size(GTK_WINDOW(pdialog->shell),
1537 GUI_GTK_OPTION(citydlg_xsize),
1538 GUI_GTK_OPTION(citydlg_ysize));
1540 pdialog->popup_menu = gtk_menu_new();
1542 vbox = gtk_dialog_get_content_area(GTK_DIALOG(pdialog->shell));
1543 hbox = gtk_grid_new();
1544 gtk_grid_set_column_homogeneous(GTK_GRID(hbox), TRUE);
1545 gtk_container_add(GTK_CONTAINER(vbox), hbox);
1547 /**** Citizens bar here ****/
1548 cbox = gtk_grid_new();
1549 gtk_container_add(GTK_CONTAINER(hbox), cbox);
1551 ebox = gtk_event_box_new();
1552 gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
1553 gtk_container_add(GTK_CONTAINER(cbox), ebox);
1555 citizen_bar_width = tileset_small_sprite_width(tileset) * NUM_CITIZENS_SHOWN;
1556 citizen_bar_height = tileset_small_sprite_height(tileset);
1558 pdialog->citizen_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1559 citizen_bar_width, citizen_bar_height);
1560 pdialog->citizen_images = gtk_image_new_from_surface(pdialog->citizen_surface);
1562 gtk_widget_add_events(pdialog->citizen_images, GDK_BUTTON_PRESS_MASK);
1563 gtk_widget_set_margin_left(pdialog->citizen_images, 2);
1564 gtk_widget_set_margin_right(pdialog->citizen_images, 2);
1565 gtk_widget_set_margin_top(pdialog->citizen_images, 2);
1566 gtk_widget_set_margin_bottom(pdialog->citizen_images, 2);
1567 gtk_widget_set_halign(pdialog->citizen_images, GTK_ALIGN_START);
1568 gtk_widget_set_valign(pdialog->citizen_images, GTK_ALIGN_CENTER);
1569 gtk_container_add(GTK_CONTAINER(ebox), pdialog->citizen_images);
1570 g_signal_connect(G_OBJECT(ebox), "button-press-event",
1571 G_CALLBACK(citizens_callback), pdialog);
1573 /**** City name label here ****/
1574 pdialog->name_label = gtk_label_new(NULL);
1575 gtk_widget_set_hexpand(pdialog->name_label, TRUE);
1576 gtk_widget_set_halign(pdialog->name_label, GTK_ALIGN_START);
1577 gtk_widget_set_valign(pdialog->name_label, GTK_ALIGN_CENTER);
1578 gtk_container_add(GTK_CONTAINER(hbox), pdialog->name_label);
1580 /**** -Start of Notebook- ****/
1582 pdialog->notebook = gtk_notebook_new();
1583 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(pdialog->notebook),
1584 GTK_POS_BOTTOM);
1585 gtk_container_add(GTK_CONTAINER(vbox), pdialog->notebook);
1587 create_and_append_overview_page(pdialog);
1588 create_and_append_map_page(pdialog);
1589 create_and_append_buildings_page(pdialog);
1590 create_and_append_worklist_page(pdialog);
1592 /* only create these tabs if not a spy */
1593 if (!client_has_player() || city_owner(pcity) == client_player()) {
1594 create_and_append_happiness_page(pdialog);
1597 if (city_owner(pcity) == client_player()
1598 && !client_is_observer()) {
1599 create_and_append_cma_page(pdialog);
1600 create_and_append_settings_page(pdialog);
1601 } else {
1602 gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog->notebook),
1603 OVERVIEW_PAGE);
1606 /**** End of Notebook ****/
1608 /* bottom buttons */
1610 pdialog->show_units_command =
1611 gtk_dialog_add_button(GTK_DIALOG(pdialog->shell), _("_List present units..."), CDLGR_UNITS);
1613 g_signal_connect(GTK_DIALOG(pdialog->shell), "response",
1614 G_CALLBACK(citydlg_response_callback), pdialog);
1616 pdialog->prev_command = gtk_stockbutton_new(GTK_STOCK_GO_BACK,
1617 _("_Prev city"));
1618 gtk_dialog_add_action_widget(GTK_DIALOG(pdialog->shell),
1619 pdialog->prev_command, 1);
1621 pdialog->next_command = gtk_stockbutton_new(GTK_STOCK_GO_FORWARD,
1622 _("_Next city"));
1623 gtk_dialog_add_action_widget(GTK_DIALOG(pdialog->shell),
1624 pdialog->next_command, 2);
1626 if (city_owner(pcity) != client.conn.playing) {
1627 gtk_widget_set_sensitive(pdialog->prev_command, FALSE);
1628 gtk_widget_set_sensitive(pdialog->next_command, FALSE);
1631 close_command = gtk_dialog_add_button(GTK_DIALOG(pdialog->shell),
1632 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
1634 gtk_dialog_set_default_response(GTK_DIALOG(pdialog->shell),
1635 GTK_RESPONSE_CLOSE);
1637 g_signal_connect(close_command, "clicked",
1638 G_CALLBACK(close_callback), pdialog);
1640 g_signal_connect(pdialog->prev_command, "clicked",
1641 G_CALLBACK(switch_city_callback), pdialog);
1643 g_signal_connect(pdialog->next_command, "clicked",
1644 G_CALLBACK(switch_city_callback), pdialog);
1646 /* some other things we gotta do */
1648 g_signal_connect(pdialog->shell, "key_press_event",
1649 G_CALLBACK(keyboard_handler), pdialog);
1651 dialog_list_prepend(dialog_list, pdialog);
1653 real_city_dialog_refresh(pdialog->pcity);
1655 /* need to do this every time a new dialog is opened. */
1656 city_dialog_update_prev_next();
1658 gtk_widget_show_all(pdialog->shell);
1660 gtk_window_set_focus(GTK_WINDOW(pdialog->shell), close_command);
1662 return pdialog;
1665 /*********** Functions to update parts of the dialog ************/
1666 /****************************************************************
1667 Update title of city dialog.
1668 *****************************************************************/
1669 static void city_dialog_update_title(struct city_dialog *pdialog)
1671 gchar *buf;
1672 const gchar *now;
1674 if (city_unhappy(pdialog->pcity)) {
1675 /* TRANS: city dialog title */
1676 buf = g_strdup_printf(_("<b>%s</b> - %s citizens - DISORDER"),
1677 city_name_get(pdialog->pcity),
1678 population_to_text(city_population(pdialog->pcity)));
1679 } else if (city_celebrating(pdialog->pcity)) {
1680 /* TRANS: city dialog title */
1681 buf = g_strdup_printf(_("<b>%s</b> - %s citizens - celebrating"),
1682 city_name_get(pdialog->pcity),
1683 population_to_text(city_population(pdialog->pcity)));
1684 } else if (city_happy(pdialog->pcity)) {
1685 /* TRANS: city dialog title */
1686 buf = g_strdup_printf(_("<b>%s</b> - %s citizens - happy"),
1687 city_name_get(pdialog->pcity),
1688 population_to_text(city_population(pdialog->pcity)));
1689 } else {
1690 /* TRANS: city dialog title */
1691 buf = g_strdup_printf(_("<b>%s</b> - %s citizens"),
1692 city_name_get(pdialog->pcity),
1693 population_to_text(city_population(pdialog->pcity)));
1696 now = gtk_label_get_text(GTK_LABEL(pdialog->name_label));
1697 if (strcmp(now, buf) != 0) {
1698 gtk_window_set_title(GTK_WINDOW(pdialog->shell), city_name_get(pdialog->pcity));
1699 gtk_label_set_markup(GTK_LABEL(pdialog->name_label), buf);
1702 g_free(buf);
1705 /****************************************************************
1706 Update citizens in city dialog
1707 *****************************************************************/
1708 static void city_dialog_update_citizens(struct city_dialog *pdialog)
1710 enum citizen_category categories[MAX_CITY_SIZE];
1711 int i, width;
1712 int citizen_bar_height;
1713 struct city *pcity = pdialog->pcity;
1714 int num_citizens = get_city_citizen_types(pcity, FEELING_FINAL, categories);
1715 cairo_t *cr;
1717 /* If there is not enough space we stack the icons. We draw from left to */
1718 /* right. width is how far we go to the right for each drawn pixmap. The */
1719 /* last icon is always drawn in full, and so has reserved */
1720 /* tileset_small_sprite_width(tileset) pixels. */
1722 if (num_citizens > 1) {
1723 width = MIN(tileset_small_sprite_width(tileset),
1724 ((NUM_CITIZENS_SHOWN - 1) * tileset_small_sprite_width(tileset)) /
1725 (num_citizens - 1));
1726 } else {
1727 width = tileset_small_sprite_width(tileset);
1729 pdialog->cwidth = width;
1731 /* overview page */
1732 citizen_bar_height = tileset_small_sprite_height(tileset);
1734 cr = cairo_create(pdialog->citizen_surface);
1736 for (i = 0; i < num_citizens; i++) {
1737 cairo_set_source_surface(cr,
1738 get_citizen_sprite(tileset, categories[i], i, pcity)->surface,
1739 i * width, 0);
1740 cairo_rectangle(cr, i * width, 0, width, citizen_bar_height);
1741 cairo_fill(cr);
1744 cairo_destroy(cr);
1747 /****************************************************************
1748 Update textual info fields in city dialog
1749 *****************************************************************/
1750 static void city_dialog_update_information(GtkWidget **info_ebox,
1751 GtkWidget **info_label,
1752 struct city_dialog *pdialog)
1754 int i, illness = 0;
1755 char buf[NUM_INFO_FIELDS][512];
1756 struct city *pcity = pdialog->pcity;
1757 int granaryturns;
1758 GdkRGBA red = {1.0, 0, 0, 1.0};
1759 GdkRGBA *color;
1761 enum { FOOD, SHIELD, TRADE, GOLD, LUXURY, SCIENCE,
1762 GRANARY, GROWTH, CORRUPTION, WASTE, CULTURE,
1763 POLLUTION, ILLNESS
1766 /* fill the buffers with the necessary info */
1767 fc_snprintf(buf[FOOD], sizeof(buf[FOOD]), "%3d (%+4d)",
1768 pcity->prod[O_FOOD], pcity->surplus[O_FOOD]);
1769 fc_snprintf(buf[SHIELD], sizeof(buf[SHIELD]), "%3d (%+4d)",
1770 pcity->prod[O_SHIELD] + pcity->waste[O_SHIELD],
1771 pcity->surplus[O_SHIELD]);
1772 fc_snprintf(buf[TRADE], sizeof(buf[TRADE]), "%3d (%+4d)",
1773 pcity->surplus[O_TRADE] + pcity->waste[O_TRADE],
1774 pcity->surplus[O_TRADE]);
1775 fc_snprintf(buf[GOLD], sizeof(buf[GOLD]), "%3d (%+4d)",
1776 pcity->prod[O_GOLD], pcity->surplus[O_GOLD]);
1777 fc_snprintf(buf[LUXURY], sizeof(buf[LUXURY]), "%3d",
1778 pcity->prod[O_LUXURY]);
1779 fc_snprintf(buf[SCIENCE], sizeof(buf[SCIENCE]), "%3d",
1780 pcity->prod[O_SCIENCE]);
1781 fc_snprintf(buf[GRANARY], sizeof(buf[GRANARY]), "%4d/%-4d",
1782 pcity->food_stock, city_granary_size(city_size_get(pcity)));
1784 granaryturns = city_turns_to_grow(pcity);
1785 if (granaryturns == 0) {
1786 /* TRANS: city growth is blocked. Keep short. */
1787 fc_snprintf(buf[GROWTH], sizeof(buf[GROWTH]), _("blocked"));
1788 } else if (granaryturns == FC_INFINITY) {
1789 /* TRANS: city is not growing. Keep short. */
1790 fc_snprintf(buf[GROWTH], sizeof(buf[GROWTH]), _("never"));
1791 } else {
1792 /* A negative value means we'll have famine in that many turns.
1793 But that's handled down below. */
1794 /* TRANS: city growth turns. Keep short. */
1795 fc_snprintf(buf[GROWTH], sizeof(buf[GROWTH]),
1796 PL_("%d turn", "%d turns", abs(granaryturns)),
1797 abs(granaryturns));
1799 fc_snprintf(buf[CORRUPTION], sizeof(buf[CORRUPTION]), "%4d",
1800 pcity->waste[O_TRADE]);
1801 fc_snprintf(buf[WASTE], sizeof(buf[WASTE]), "%4d",
1802 pcity->waste[O_SHIELD]);
1803 fc_snprintf(buf[CULTURE], sizeof(buf[CULTURE]), "%4d",
1804 pcity->client.culture);
1805 fc_snprintf(buf[POLLUTION], sizeof(buf[POLLUTION]), "%4d",
1806 pcity->pollution);
1807 if (!game.info.illness_on) {
1808 fc_snprintf(buf[ILLNESS], sizeof(buf[ILLNESS]), " -.-");
1809 } else {
1810 illness = city_illness_calc(pcity, NULL, NULL, NULL, NULL);
1811 /* illness is in tenth of percent */
1812 fc_snprintf(buf[ILLNESS], sizeof(buf[ILLNESS]), "%4.1f",
1813 (float)illness / 10.0);
1816 /* stick 'em in the labels */
1817 for (i = 0; i < NUM_INFO_FIELDS; i++) {
1818 gtk_label_set_text(GTK_LABEL(info_label[i]), buf[i]);
1822 * Special style stuff for granary, growth and pollution below. The
1823 * "4" below is arbitrary. 3 turns should be enough of a warning.
1825 color = (granaryturns > -4 && granaryturns < 0) ? &red : NULL;
1826 gtk_widget_override_color(info_label[GRANARY], GTK_STATE_FLAG_NORMAL, color);
1828 color = (granaryturns == 0 || pcity->surplus[O_FOOD] < 0) ? &red : NULL;
1829 gtk_widget_override_color(info_label[GROWTH], GTK_STATE_FLAG_NORMAL, color);
1831 /* someone could add the color &orange for better granularity here */
1833 color = (pcity->pollution >= 10) ? &red : NULL;
1834 gtk_widget_override_color(info_label[POLLUTION], GTK_STATE_FLAG_NORMAL, color);
1836 /* illness is in tenth of percent, i.e 100 != 10.0% */
1837 color = (illness >= 100) ? &red : NULL;
1838 gtk_widget_override_color(info_label[ILLNESS], GTK_STATE_FLAG_NORMAL, color);
1841 /****************************************************************
1842 Update map display of city dialog
1843 *****************************************************************/
1844 static void city_dialog_update_map(struct city_dialog *pdialog)
1846 struct canvas store = FC_STATIC_CANVAS_INIT;
1848 store.surface = pdialog->map_canvas_store_unscaled;
1850 /* The drawing is done in three steps.
1851 * 1. First we render to a pixmap with the appropriate canvas size.
1852 * 2. Then the pixmap is rendered into a pixbuf of equal size.
1853 * 3. Finally this pixbuf is composited and scaled onto the GtkImage's
1854 * target pixbuf.
1857 city_dialog_redraw_map(pdialog->pcity, &store);
1859 /* draw to real window */
1860 draw_map_canvas(pdialog);
1862 if (cma_is_city_under_agent(pdialog->pcity, NULL)) {
1863 gtk_widget_set_sensitive(pdialog->overview.map_canvas.ebox, FALSE);
1864 if (pdialog->happiness.map_canvas.ebox) {
1865 gtk_widget_set_sensitive(pdialog->happiness.map_canvas.ebox, FALSE);
1867 } else {
1868 gtk_widget_set_sensitive(pdialog->overview.map_canvas.ebox, TRUE);
1869 if (pdialog->happiness.map_canvas.ebox) {
1870 gtk_widget_set_sensitive(pdialog->happiness.map_canvas.ebox, TRUE);
1875 /****************************************************************
1876 Update what city is building and buy cost in city dialog
1877 *****************************************************************/
1878 static void city_dialog_update_building(struct city_dialog *pdialog)
1880 char buf[32], buf2[200];
1881 gdouble pct;
1883 GtkListStore* store;
1884 GtkTreeIter iter;
1885 struct universal targets[MAX_NUM_PRODUCTION_TARGETS];
1886 struct item items[MAX_NUM_PRODUCTION_TARGETS];
1887 int targets_used, item;
1888 struct city *pcity = pdialog->pcity;
1889 gboolean sensitive = city_can_buy(pcity);
1890 const char *descr = city_production_name_translation(pcity);
1891 int cost = city_production_build_shield_cost(pcity);
1893 if (pdialog->overview.buy_command != NULL) {
1894 gtk_widget_set_sensitive(pdialog->overview.buy_command, sensitive);
1896 if (pdialog->production.buy_command != NULL) {
1897 gtk_widget_set_sensitive(pdialog->production.buy_command, sensitive);
1900 /* Make sure build slots info is up to date */
1901 if (pdialog->production.production_label != NULL) {
1902 int build_slots = city_build_slots(pcity);
1904 /* Only display extra info if more than one slot is available */
1905 if (build_slots > 1) {
1906 fc_snprintf(buf2, sizeof(buf2),
1907 /* TRANS: never actually used with built_slots <= 1 */
1908 PL_("Production (up to %d unit per turn):",
1909 "Production (up to %d units per turn):", build_slots),
1910 build_slots);
1911 gtk_label_set_text(
1912 GTK_LABEL(pdialog->production.production_label), buf2);
1913 } else {
1914 gtk_label_set_text(
1915 GTK_LABEL(pdialog->production.production_label), _("Production:"));
1919 /* Update what the city is working on */
1920 get_city_dialog_production(pcity, buf, sizeof(buf));
1922 if (cost > 0) {
1923 pct = (gdouble) pcity->shield_stock / (gdouble) cost;
1924 pct = CLAMP(pct, 0.0, 1.0);
1925 } else {
1926 pct = 1.0;
1929 if (pdialog->overview.production_bar != NULL) {
1930 fc_snprintf(buf2, sizeof(buf2), "%s%s\n%s", descr,
1931 worklist_is_empty(&pcity->worklist) ? "" : " (+)", buf);
1932 gtk_progress_bar_set_text(
1933 GTK_PROGRESS_BAR(pdialog->overview.production_bar), buf2);
1934 gtk_progress_bar_set_fraction(
1935 GTK_PROGRESS_BAR(pdialog->overview.production_bar), pct);
1938 if (pdialog->production.production_bar != NULL) {
1939 fc_snprintf(buf2, sizeof(buf2), "%s%s: %s", descr,
1940 worklist_is_empty(&pcity->worklist) ? "" : " (+)", buf);
1941 gtk_progress_bar_set_text(
1942 GTK_PROGRESS_BAR(pdialog->production.production_bar), buf2);
1943 gtk_progress_bar_set_fraction(
1944 GTK_PROGRESS_BAR(pdialog->production.production_bar), pct);
1947 if (pdialog->overview.production_combo != NULL) {
1948 gtk_combo_box_set_active(GTK_COMBO_BOX(pdialog->overview.production_combo),
1949 -1);
1952 store = pdialog->overview.change_production_store;
1953 if (store != NULL) {
1954 gtk_list_store_clear(pdialog->overview.change_production_store);
1956 targets_used
1957 = collect_eventually_buildable_targets(targets, pdialog->pcity, FALSE);
1958 name_and_sort_items(targets, targets_used, items, FALSE, pcity);
1960 for (item = 0; item < targets_used; item++) {
1961 if (can_city_build_now(pcity, &items[item].item)) {
1962 const char* name;
1963 struct sprite* sprite;
1964 GdkPixbuf *pix;
1965 struct universal target = items[item].item;
1966 bool useless;
1968 if (VUT_UTYPE == target.kind) {
1969 name = utype_name_translation(target.value.utype);
1970 sprite = get_unittype_sprite(tileset, target.value.utype,
1971 direction8_invalid());
1972 useless = FALSE;
1973 } else {
1974 name = improvement_name_translation(target.value.building);
1975 sprite = get_building_sprite(tileset, target.value.building);
1976 useless = is_improvement_redundant(pcity, target.value.building);
1978 pix = sprite_get_pixbuf(sprite);
1979 gtk_list_store_append(store, &iter);
1980 gtk_list_store_set(store, &iter, 0, pix,
1981 1, name, 3, useless,
1982 2, (gint)cid_encode(items[item].item),-1);
1983 g_object_unref(G_OBJECT(pix));
1988 /* work around GTK+ refresh bug. */
1989 if (pdialog->overview.production_bar != NULL) {
1990 gtk_widget_queue_resize(pdialog->overview.production_bar);
1992 if (pdialog->production.production_bar != NULL) {
1993 gtk_widget_queue_resize(pdialog->production.production_bar);
1997 /****************************************************************
1998 Update list of improvements in city dialog
1999 *****************************************************************/
2000 static void city_dialog_update_improvement_list(struct city_dialog *pdialog)
2002 int total, item, targets_used;
2003 struct universal targets[MAX_NUM_PRODUCTION_TARGETS];
2004 struct item items[MAX_NUM_PRODUCTION_TARGETS];
2005 GtkTreeModel *model;
2006 GtkListStore *store;
2008 model =
2009 gtk_tree_view_get_model(GTK_TREE_VIEW(pdialog->overview.improvement_list));
2010 store = GTK_LIST_STORE(model);
2012 targets_used = collect_already_built_targets(targets, pdialog->pcity);
2013 name_and_sort_items(targets, targets_used, items, FALSE, pdialog->pcity);
2015 gtk_list_store_clear(store);
2017 total = 0;
2018 for (item = 0; item < targets_used; item++) {
2019 GdkPixbuf *pix;
2020 GtkTreeIter it;
2021 int upkeep;
2022 struct sprite *sprite;
2023 struct universal target = items[item].item;
2025 fc_assert_action(VUT_IMPROVEMENT == target.kind, continue);
2026 /* This takes effects (like Adam Smith's) into account. */
2027 upkeep = city_improvement_upkeep(pdialog->pcity, target.value.building);
2028 sprite = get_building_sprite(tileset, target.value.building);
2030 pix = sprite_get_pixbuf(sprite);
2031 gtk_list_store_append(store, &it);
2032 gtk_list_store_set(store, &it,
2033 0, target.value.building,
2034 1, pix,
2035 2, items[item].descr,
2036 3, upkeep,
2037 4, is_improvement_redundant(pdialog->pcity, target.value.building),
2038 -1);
2039 g_object_unref(G_OBJECT(pix));
2041 total += upkeep;
2045 /****************************************************************
2046 Update list of supported units in city dialog
2047 *****************************************************************/
2048 static void city_dialog_update_supported_units(struct city_dialog *pdialog)
2050 struct unit_list *units;
2051 struct unit_node_vector *nodes;
2052 int n, m, i;
2053 gchar *buf;
2054 int free_unhappy = get_city_bonus(pdialog->pcity, EFT_MAKE_CONTENT_MIL);
2056 if (NULL != client.conn.playing
2057 && city_owner(pdialog->pcity) != client.conn.playing) {
2058 units = pdialog->pcity->client.info_units_supported;
2059 } else {
2060 units = pdialog->pcity->units_supported;
2063 nodes = &pdialog->overview.supported_units;
2065 n = unit_list_size(units);
2066 m = unit_node_vector_size(nodes);
2068 if (m > n) {
2069 i = 0;
2070 unit_node_vector_iterate(nodes, elt) {
2071 if (i++ >= n) {
2072 gtk_widget_destroy(elt->cmd);
2074 } unit_node_vector_iterate_end;
2076 unit_node_vector_reserve(nodes, n);
2077 } else {
2078 for (i = m; i < n; i++) {
2079 GtkWidget *cmd, *pix;
2080 struct unit_node node;
2082 cmd = gtk_button_new();
2083 node.cmd = cmd;
2085 gtk_button_set_relief(GTK_BUTTON(cmd), GTK_RELIEF_NONE);
2086 gtk_widget_add_events(cmd,
2087 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2089 pix = gtk_image_new();
2090 node.pix = pix;
2091 node.height = tileset_unit_with_upkeep_height(tileset);
2093 gtk_container_add(GTK_CONTAINER(cmd), pix);
2095 gtk_grid_attach(GTK_GRID(pdialog->overview.supported_unit_table),
2096 cmd, i, 0, 1, 1);
2097 unit_node_vector_append(nodes, node);
2101 i = 0;
2102 unit_list_iterate(units, punit) {
2103 struct unit_node *pnode;
2104 int happy_cost = city_unit_unhappiness(punit, &free_unhappy);
2106 pnode = unit_node_vector_get(nodes, i);
2107 if (pnode) {
2108 GtkWidget *cmd, *pix;
2110 cmd = pnode->cmd;
2111 pix = pnode->pix;
2113 put_unit_image_city_overlays(punit, GTK_IMAGE(pix), pnode->height,
2114 punit->upkeep, happy_cost);
2116 g_signal_handlers_disconnect_matched(cmd,
2117 G_SIGNAL_MATCH_FUNC,
2118 0, 0, NULL, supported_unit_callback, NULL);
2120 g_signal_handlers_disconnect_matched(cmd,
2121 G_SIGNAL_MATCH_FUNC,
2122 0, 0, NULL, supported_unit_middle_callback, NULL);
2124 gtk_widget_set_tooltip_text(cmd, unit_description(punit));
2126 g_signal_connect(cmd, "button_press_event",
2127 G_CALLBACK(supported_unit_callback),
2128 GINT_TO_POINTER(punit->id));
2130 g_signal_connect(cmd, "button_release_event",
2131 G_CALLBACK(supported_unit_middle_callback),
2132 GINT_TO_POINTER(punit->id));
2134 if (city_owner(pdialog->pcity) != client.conn.playing) {
2135 gtk_widget_set_sensitive(cmd, FALSE);
2136 } else {
2137 gtk_widget_set_sensitive(cmd, TRUE);
2140 gtk_widget_show(pix);
2141 gtk_widget_show(cmd);
2143 i++;
2144 } unit_list_iterate_end;
2146 buf = g_strdup_printf(_("Supported units %d"), n);
2147 gtk_frame_set_label(GTK_FRAME(pdialog->overview.supported_units_frame), buf);
2148 g_free(buf);
2151 /****************************************************************
2152 Update list of present units in city dialog
2153 *****************************************************************/
2154 static void city_dialog_update_present_units(struct city_dialog *pdialog)
2156 struct unit_list *units;
2157 struct unit_node_vector *nodes;
2158 int n, m, i;
2159 gchar *buf;
2161 if (NULL != client.conn.playing
2162 && city_owner(pdialog->pcity) != client.conn.playing) {
2163 units = pdialog->pcity->client.info_units_present;
2164 } else {
2165 units = pdialog->pcity->tile->units;
2168 nodes = &pdialog->overview.present_units;
2170 n = unit_list_size(units);
2171 m = unit_node_vector_size(nodes);
2173 if (m > n) {
2174 i = 0;
2175 unit_node_vector_iterate(nodes, elt) {
2176 if (i++ >= n) {
2177 gtk_widget_destroy(elt->cmd);
2179 } unit_node_vector_iterate_end;
2181 unit_node_vector_reserve(nodes, n);
2182 } else {
2183 for (i = m; i < n; i++) {
2184 GtkWidget *cmd, *pix;
2185 struct unit_node node;
2187 cmd = gtk_button_new();
2188 node.cmd = cmd;
2190 gtk_button_set_relief(GTK_BUTTON(cmd), GTK_RELIEF_NONE);
2191 gtk_widget_add_events(cmd,
2192 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2194 pix = gtk_image_new();
2195 node.pix = pix;
2196 node.height = tileset_full_tile_height(tileset);
2198 gtk_container_add(GTK_CONTAINER(cmd), pix);
2200 gtk_grid_attach(GTK_GRID(pdialog->overview.present_unit_table),
2201 cmd, i, 0, 1, 1);
2202 unit_node_vector_append(nodes, node);
2206 i = 0;
2207 unit_list_iterate(units, punit) {
2208 struct unit_node *pnode;
2210 pnode = unit_node_vector_get(nodes, i);
2211 if (pnode) {
2212 GtkWidget *cmd, *pix;
2214 cmd = pnode->cmd;
2215 pix = pnode->pix;
2217 put_unit_image(punit, GTK_IMAGE(pix), pnode->height);
2219 g_signal_handlers_disconnect_matched(cmd,
2220 G_SIGNAL_MATCH_FUNC,
2221 0, 0, NULL, present_unit_callback, NULL);
2223 g_signal_handlers_disconnect_matched(cmd,
2224 G_SIGNAL_MATCH_FUNC,
2225 0, 0, NULL, present_unit_middle_callback, NULL);
2227 gtk_widget_set_tooltip_text(cmd, unit_description(punit));
2229 g_signal_connect(cmd, "button_press_event",
2230 G_CALLBACK(present_unit_callback),
2231 GINT_TO_POINTER(punit->id));
2233 g_signal_connect(cmd, "button_release_event",
2234 G_CALLBACK(present_unit_middle_callback),
2235 GINT_TO_POINTER(punit->id));
2237 if (city_owner(pdialog->pcity) != client.conn.playing) {
2238 gtk_widget_set_sensitive(cmd, FALSE);
2239 } else {
2240 gtk_widget_set_sensitive(cmd, TRUE);
2243 gtk_widget_show(pix);
2244 gtk_widget_show(cmd);
2246 i++;
2247 } unit_list_iterate_end;
2249 buf = g_strdup_printf(_("Present units %d"), n);
2250 gtk_frame_set_label(GTK_FRAME(pdialog->overview.present_units_frame), buf);
2251 g_free(buf);
2254 /****************************************************************
2255 Updates the sensitivity of the the prev and next buttons.
2256 this does not need pdialog as a parameter, since it iterates
2257 over all the open dialogs.
2258 note: we still need the sensitivity code in create_city_dialog()
2259 for the spied dialogs.
2260 *****************************************************************/
2261 static void city_dialog_update_prev_next(void)
2263 int count = 0;
2264 int city_number;
2266 if (client_is_global_observer()) {
2267 return; /* Keep them insensitive as initially set */
2270 city_number = city_list_size(client.conn.playing->cities);
2272 /* the first time, we see if all the city dialogs are open */
2273 dialog_list_iterate(dialog_list, pdialog) {
2274 if (city_owner(pdialog->pcity) == client.conn.playing) {
2275 count++;
2277 } dialog_list_iterate_end;
2279 if (count == city_number) { /* all are open, shouldn't prev/next */
2280 dialog_list_iterate(dialog_list, pdialog) {
2281 gtk_widget_set_sensitive(pdialog->prev_command, FALSE);
2282 gtk_widget_set_sensitive(pdialog->next_command, FALSE);
2283 } dialog_list_iterate_end;
2284 } else {
2285 dialog_list_iterate(dialog_list, pdialog) {
2286 if (city_owner(pdialog->pcity) == client.conn.playing) {
2287 gtk_widget_set_sensitive(pdialog->prev_command, TRUE);
2288 gtk_widget_set_sensitive(pdialog->next_command, TRUE);
2290 } dialog_list_iterate_end;
2294 /****************************************************************
2295 User clicked button from action area.
2296 *****************************************************************/
2297 static void citydlg_response_callback(GtkDialog *dlg, gint response,
2298 void *data)
2300 switch (response) {
2301 case CDLGR_UNITS:
2302 show_units_response(data);
2303 break;
2307 /****************************************************************
2308 User has clicked show units
2309 *****************************************************************/
2310 static void show_units_response(void *data)
2312 struct city_dialog *pdialog = (struct city_dialog *) data;
2313 struct tile *ptile = pdialog->pcity->tile;
2315 if (unit_list_size(ptile->units)) {
2316 unit_select_dialog_popup(ptile);
2320 /****************************************************************
2321 Set city menu position
2322 *****************************************************************/
2323 static void city_menu_position(GtkMenu *menu, gint *x, gint *y,
2324 gboolean *push_in, gpointer data)
2326 GtkWidget *widget;
2327 GtkAllocation allocation;
2328 gint xpos;
2329 gint ypos;
2331 fc_assert_ret(GTK_IS_BUTTON(data));
2333 widget = GTK_WIDGET(data);
2335 gtk_widget_get_allocation(widget, &allocation);
2337 gdk_window_get_origin(gtk_widget_get_window(widget), &xpos, &ypos);
2339 xpos += allocation.x + allocation.width/2;
2340 ypos += allocation.y + allocation.height/2;
2342 *x = xpos;
2343 *y = ypos;
2344 *push_in = TRUE;
2347 /****************************************************************
2348 Destroy widget -callback
2349 *****************************************************************/
2350 static void destroy_func(GtkWidget *w, gpointer data)
2352 gtk_widget_destroy(w);
2355 /****************************************************************
2356 Pop-up menu to change attributes of supported units
2357 *****************************************************************/
2358 static gboolean supported_unit_callback(GtkWidget * w, GdkEventButton * ev,
2359 gpointer data)
2361 GtkWidget *menu, *item;
2362 struct city_dialog *pdialog;
2363 struct city *pcity;
2364 struct unit *punit =
2365 player_unit_by_number(client_player(), (size_t) data);
2367 if (NULL != punit
2368 && NULL != (pcity = game_city_by_number(punit->homecity))
2369 && NULL != (pdialog = get_city_dialog(pcity))) {
2371 if (ev->type != GDK_BUTTON_PRESS || ev->button == 2 || ev->button == 3
2372 || !can_client_issue_orders()) {
2373 return FALSE;
2376 menu = pdialog->popup_menu;
2378 gtk_menu_popdown(GTK_MENU(menu));
2379 gtk_container_foreach(GTK_CONTAINER(menu), destroy_func, NULL);
2381 item = gtk_menu_item_new_with_mnemonic(_("Cen_ter"));
2382 g_signal_connect(item, "activate",
2383 G_CALLBACK(unit_center_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"));
2388 g_signal_connect(item, "activate",
2389 G_CALLBACK(unit_activate_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(_("Activate unit, _close dialog"));
2394 g_signal_connect(item, "activate",
2395 G_CALLBACK(supported_unit_activate_close_callback),
2396 GINT_TO_POINTER(punit->id));
2397 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2399 item = gtk_menu_item_new_with_mnemonic(_("_Disband unit"));
2400 g_signal_connect(item, "activate",
2401 G_CALLBACK(unit_disband_callback),
2402 GINT_TO_POINTER(punit->id));
2403 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2405 if (!unit_can_do_action(punit, ACTION_DISBAND_UNIT)) {
2406 gtk_widget_set_sensitive(item, FALSE);
2409 gtk_widget_show_all(menu);
2411 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
2412 city_menu_position, w, ev->button, ev->time);
2416 return TRUE;
2419 /****************************************************************
2420 Pop-up menu to change attributes of units, ex. change homecity.
2421 *****************************************************************/
2422 static gboolean present_unit_callback(GtkWidget * w, GdkEventButton * ev,
2423 gpointer data)
2425 GtkWidget *menu, *item;
2426 struct city_dialog *pdialog;
2427 struct city *pcity;
2428 struct unit *punit =
2429 player_unit_by_number(client_player(), (size_t) data);
2431 if (NULL != punit
2432 && NULL != (pcity = tile_city(unit_tile(punit)))
2433 && NULL != (pdialog = get_city_dialog(pcity))) {
2435 if (ev->type != GDK_BUTTON_PRESS || ev->button == 2 || ev->button == 3
2436 || !can_client_issue_orders()) {
2437 return FALSE;
2440 menu = pdialog->popup_menu;
2442 gtk_menu_popdown(GTK_MENU(menu));
2443 gtk_container_foreach(GTK_CONTAINER(menu), destroy_func, NULL);
2445 item = gtk_menu_item_new_with_mnemonic(_("_Activate unit"));
2446 g_signal_connect(item, "activate",
2447 G_CALLBACK(unit_activate_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(_("Activate unit, _close dialog"));
2452 g_signal_connect(item, "activate",
2453 G_CALLBACK(present_unit_activate_close_callback),
2454 GINT_TO_POINTER(punit->id));
2455 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2457 item = gtk_menu_item_new_with_mnemonic(_("_Load unit"));
2458 g_signal_connect(item, "activate",
2459 G_CALLBACK(unit_load_callback),
2460 GINT_TO_POINTER(punit->id));
2461 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2463 if (!unit_can_load(punit)) {
2464 gtk_widget_set_sensitive(item, FALSE);
2467 item = gtk_menu_item_new_with_mnemonic(_("_Unload unit"));
2468 g_signal_connect(item, "activate",
2469 G_CALLBACK(unit_unload_callback),
2470 GINT_TO_POINTER(punit->id));
2471 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2473 if (!can_unit_unload(punit, unit_transport_get(punit))
2474 || !can_unit_exist_at_tile(&(wld.map), punit, unit_tile(punit))) {
2475 gtk_widget_set_sensitive(item, FALSE);
2478 item = gtk_menu_item_new_with_mnemonic(_("_Sentry unit"));
2479 g_signal_connect(item, "activate",
2480 G_CALLBACK(unit_sentry_callback),
2481 GINT_TO_POINTER(punit->id));
2482 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2484 if (punit->activity == ACTIVITY_SENTRY
2485 || !can_unit_do_activity(punit, ACTIVITY_SENTRY)) {
2486 gtk_widget_set_sensitive(item, FALSE);
2489 item = gtk_menu_item_new_with_mnemonic(_("_Fortify unit"));
2490 g_signal_connect(item, "activate",
2491 G_CALLBACK(unit_fortify_callback),
2492 GINT_TO_POINTER(punit->id));
2493 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2495 if (punit->activity == ACTIVITY_FORTIFYING
2496 || !can_unit_do_activity(punit, ACTIVITY_FORTIFYING)) {
2497 gtk_widget_set_sensitive(item, FALSE);
2500 item = gtk_menu_item_new_with_mnemonic(_("_Disband unit"));
2501 g_signal_connect(item, "activate",
2502 G_CALLBACK(unit_disband_callback),
2503 GINT_TO_POINTER(punit->id));
2504 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2506 if (!unit_can_do_action(punit, ACTION_DISBAND_UNIT)) {
2507 gtk_widget_set_sensitive(item, FALSE);
2510 item = gtk_menu_item_new_with_mnemonic(
2511 action_id_name_translation(ACTION_HOME_CITY));
2512 g_signal_connect(item, "activate",
2513 G_CALLBACK(unit_homecity_callback),
2514 GINT_TO_POINTER(punit->id));
2515 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2516 gtk_widget_set_sensitive(item, can_unit_change_homecity_to(punit, pcity));
2518 item = gtk_menu_item_new_with_mnemonic(_("U_pgrade unit"));
2519 g_signal_connect(item, "activate",
2520 G_CALLBACK(unit_upgrade_callback),
2521 GINT_TO_POINTER(punit->id));
2522 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2524 if (!can_client_issue_orders()
2525 || NULL == can_upgrade_unittype(client.conn.playing,
2526 unit_type_get(punit))) {
2527 gtk_widget_set_sensitive(item, FALSE);
2530 gtk_widget_show_all(menu);
2532 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
2533 city_menu_position, w, ev->button, ev->time);
2535 return TRUE;
2538 /****************************************************************
2539 if user middle-clicked on a unit, activate it and close dialog
2540 *****************************************************************/
2541 static gboolean present_unit_middle_callback(GtkWidget * w,
2542 GdkEventButton * ev,
2543 gpointer data)
2545 struct city_dialog *pdialog;
2546 struct city *pcity;
2547 struct unit *punit =
2548 player_unit_by_number(client_player(), (size_t) data);
2550 if (NULL != punit
2551 && NULL != (pcity = tile_city(unit_tile(punit)))
2552 && NULL != (pdialog = get_city_dialog(pcity))
2553 && can_client_issue_orders()) {
2555 if (ev->button == 3) {
2556 unit_focus_set(punit);
2557 } else if (ev->button == 2) {
2558 unit_focus_set(punit);
2559 close_city_dialog(pdialog);
2563 return TRUE;
2566 /****************************************************************
2567 if user middle-clicked on a unit, activate it and close dialog
2568 *****************************************************************/
2569 static gboolean supported_unit_middle_callback(GtkWidget * w,
2570 GdkEventButton * ev,
2571 gpointer data)
2573 struct city_dialog *pdialog;
2574 struct city *pcity;
2575 struct unit *punit =
2576 player_unit_by_number(client_player(), (size_t) data);
2578 if (NULL != punit
2579 && NULL != (pcity = game_city_by_number(punit->homecity))
2580 && NULL != (pdialog = get_city_dialog(pcity))
2581 && can_client_issue_orders()) {
2583 if (ev->button == 3) {
2584 unit_focus_set(punit);
2585 } else if (ev->button == 2) {
2586 unit_focus_set(punit);
2587 close_city_dialog(pdialog);
2591 return TRUE;
2594 /****************************************************************
2595 User has requested centering to unit
2596 *****************************************************************/
2597 static void unit_center_callback(GtkWidget * w, gpointer data)
2599 struct unit *punit =
2600 player_unit_by_number(client_player(), (size_t)data);
2602 if (NULL != punit) {
2603 center_tile_mapcanvas(unit_tile(punit));
2607 /****************************************************************
2608 User has requested unit activation
2609 *****************************************************************/
2610 static void unit_activate_callback(GtkWidget * w, gpointer data)
2612 struct unit *punit =
2613 player_unit_by_number(client_player(), (size_t)data);
2615 if (NULL != punit) {
2616 unit_focus_set(punit);
2620 /****************************************************************
2621 User has requested some supported unit to be activated and
2622 city dialog to be closed
2623 *****************************************************************/
2624 static void supported_unit_activate_close_callback(GtkWidget * w,
2625 gpointer data)
2627 struct unit *punit =
2628 player_unit_by_number(client_player(), (size_t)data);
2630 if (NULL != punit) {
2631 struct city *pcity =
2632 player_city_by_number(client_player(), punit->homecity);
2634 unit_focus_set(punit);
2635 if (NULL != pcity) {
2636 struct city_dialog *pdialog = get_city_dialog(pcity);
2638 if (NULL != pdialog) {
2639 close_city_dialog(pdialog);
2645 /****************************************************************
2646 User has requested some present unit to be activated and
2647 city dialog to be closed
2648 *****************************************************************/
2649 static void present_unit_activate_close_callback(GtkWidget * w,
2650 gpointer data)
2652 struct unit *punit =
2653 player_unit_by_number(client_player(), (size_t)data);
2655 if (NULL != punit) {
2656 struct city *pcity = tile_city(unit_tile(punit));
2658 unit_focus_set(punit);
2659 if (NULL != pcity) {
2660 struct city_dialog *pdialog = get_city_dialog(pcity);
2662 if (NULL != pdialog) {
2663 close_city_dialog(pdialog);
2669 /****************************************************************
2670 User has requested unit to be loaded to transport
2671 *****************************************************************/
2672 static void unit_load_callback(GtkWidget * w, gpointer data)
2674 struct unit *punit =
2675 player_unit_by_number(client_player(), (size_t)data);
2677 if (NULL != punit) {
2678 request_transport(punit, unit_tile(punit));
2682 /****************************************************************
2683 User has requested unit to be unloaded from transport
2684 *****************************************************************/
2685 static void unit_unload_callback(GtkWidget * w, gpointer data)
2687 struct unit *punit =
2688 player_unit_by_number(client_player(), (size_t)data);
2690 if (NULL != punit) {
2691 request_unit_unload(punit);
2695 /****************************************************************
2696 User has requested unit to be sentried
2697 *****************************************************************/
2698 static void unit_sentry_callback(GtkWidget * w, gpointer data)
2700 struct unit *punit =
2701 player_unit_by_number(client_player(), (size_t)data);
2703 if (NULL != punit) {
2704 request_unit_sentry(punit);
2708 /****************************************************************
2709 User has requested unit to be fortified
2710 *****************************************************************/
2711 static void unit_fortify_callback(GtkWidget * w, gpointer data)
2713 struct unit *punit =
2714 player_unit_by_number(client_player(), (size_t)data);
2716 if (NULL != punit) {
2717 request_unit_fortify(punit);
2721 /****************************************************************
2722 User has requested unit to be disbanded
2723 *****************************************************************/
2724 static void unit_disband_callback(GtkWidget * w, gpointer data)
2726 struct unit_list *punits;
2727 struct unit *punit =
2728 player_unit_by_number(client_player(), (size_t)data);
2730 if (NULL == punit) {
2731 return;
2734 punits = unit_list_new();
2735 unit_list_append(punits, punit);
2736 popup_disband_dialog(punits);
2737 unit_list_destroy(punits);
2740 /****************************************************************
2741 User has requested unit to change homecity to city where it
2742 currently is
2743 *****************************************************************/
2744 static void unit_homecity_callback(GtkWidget * w, gpointer data)
2746 struct unit *punit =
2747 player_unit_by_number(client_player(), (size_t)data);
2749 if (NULL != punit) {
2750 request_unit_change_homecity(punit);
2754 /****************************************************************
2755 User has requested unit to be upgraded
2756 *****************************************************************/
2757 static void unit_upgrade_callback(GtkWidget *w, gpointer data)
2759 struct unit_list *punits;
2760 struct unit *punit =
2761 player_unit_by_number(client_player(), (size_t)data);
2763 if (NULL == punit) {
2764 return;
2767 punits = unit_list_new();
2768 unit_list_append(punits, punit);
2769 popup_upgrade_dialog(punits);
2770 unit_list_destroy(punits);
2773 /*** Callbacks for citizen bar, map funcs that are not update ***/
2774 /****************************************************************
2775 Somebody clicked our list of citizens. If they clicked a specialist
2776 then change the type of him, else do nothing.
2777 *****************************************************************/
2778 static gboolean citizens_callback(GtkWidget *w, GdkEventButton *ev,
2779 gpointer data)
2781 struct city_dialog *pdialog = data;
2782 struct city *pcity = pdialog->pcity;
2783 int citnum, tlen, len;
2785 if (!can_client_issue_orders()) {
2786 return FALSE;
2789 tlen = tileset_small_sprite_width(tileset);
2790 len = (city_size_get(pcity) - 1) * pdialog->cwidth + tlen;
2791 if (ev->x > len) {
2792 /* no citizen that far to the right */
2793 return FALSE;
2795 citnum = MIN(city_size_get(pcity) - 1, ev->x / pdialog->cwidth);
2797 city_rotate_specialist(pcity, citnum);
2799 return TRUE;
2802 /**************************************************************************
2803 Set requested workertask
2804 **************************************************************************/
2805 static void set_city_workertask(GtkWidget *w, gpointer data)
2807 enum unit_activity act = (enum unit_activity)GPOINTER_TO_INT(data);
2808 struct city *pcity = workertask_req.owner;
2809 struct tile *ptile = workertask_req.loc;
2810 struct packet_worker_task task;
2812 task.city_id = pcity->id;
2814 if (act == ACTIVITY_LAST) {
2815 task.tgt = -1;
2816 task.want = 0;
2817 } else {
2818 enum extra_cause cause = activity_to_extra_cause(act);
2819 struct extra_type *tgt;
2821 if (cause != EC_NONE) {
2822 tgt = next_extra_for_tile(ptile, cause, city_owner(pcity), NULL);
2823 } else {
2824 tgt = NULL;
2827 if (tgt == NULL) {
2828 struct terrain *pterr = tile_terrain(ptile);
2830 if ((act != ACTIVITY_TRANSFORM
2831 || pterr->transform_result == NULL || pterr->transform_result == pterr)
2832 && (act != ACTIVITY_IRRIGATE
2833 || pterr->irrigation_result == NULL || pterr->irrigation_result == pterr)
2834 && (act != ACTIVITY_MINE
2835 || pterr->mining_result == NULL || pterr->mining_result == pterr)) {
2836 /* No extra to order */
2837 output_window_append(ftc_client, _("There's no suitable extra to order."));
2839 return;
2842 task.tgt = -1;
2843 } else {
2844 task.tgt = extra_index(tgt);
2847 task.want = 100;
2850 task.tile_id = ptile->index;
2851 task.activity = act;
2853 send_packet_worker_task(&client.conn, &task);
2856 /****************************************************************
2857 Destroy workertask dlg
2858 *****************************************************************/
2859 static void workertask_dlg_destroy(GtkWidget *w, gpointer data)
2861 is_showing_workertask_dialog = FALSE;
2864 /**************************************************************************
2865 Open dialog for setting worker task
2866 **************************************************************************/
2867 static void popup_workertask_dlg(struct city *pcity, struct tile *ptile)
2869 if (!is_showing_workertask_dialog) {
2870 GtkWidget *shl;
2871 struct terrain *pterr = tile_terrain(ptile);
2872 struct universal for_terr = { .kind = VUT_TERRAIN,
2873 .value = { .terrain = pterr }};
2874 struct worker_task *ptask;
2876 is_showing_workertask_dialog = TRUE;
2877 workertask_req.owner = pcity;
2878 workertask_req.loc = ptile;
2880 shl = choice_dialog_start(GTK_WINDOW(toplevel),
2881 _("What Action to Request"),
2882 _("Select autosettler activity:"));
2884 ptask = worker_task_list_get(pcity->task_reqs, 0);
2885 if (ptask != NULL) {
2886 choice_dialog_add(shl, _("Clear request"),
2887 G_CALLBACK(set_city_workertask),
2888 GINT_TO_POINTER(ACTIVITY_LAST), FALSE, NULL);
2891 if ((pterr->mining_result == pterr
2892 && effect_cumulative_max(EFT_MINING_POSSIBLE, &for_terr) > 0)
2893 || (pterr->mining_result != pterr && pterr->mining_result != NULL
2894 && effect_cumulative_max(EFT_MINING_TF_POSSIBLE, &for_terr) > 0)) {
2895 choice_dialog_add(shl, _("Mine"),
2896 G_CALLBACK(set_city_workertask),
2897 GINT_TO_POINTER(ACTIVITY_MINE), FALSE, NULL);
2899 if ((pterr->irrigation_result == pterr
2900 && effect_cumulative_max(EFT_IRRIG_POSSIBLE, &for_terr) > 0)
2901 || (pterr->irrigation_result != pterr && pterr->irrigation_result != NULL
2902 && effect_cumulative_max(EFT_IRRIG_TF_POSSIBLE, &for_terr) > 0)) {
2903 choice_dialog_add(shl, _("Irrigate"),
2904 G_CALLBACK(set_city_workertask),
2905 GINT_TO_POINTER(ACTIVITY_IRRIGATE), FALSE, NULL);
2907 if (next_extra_for_tile(ptile, EC_ROAD, city_owner(pcity), NULL) != NULL) {
2908 choice_dialog_add(shl, _("Road"),
2909 G_CALLBACK(set_city_workertask),
2910 GINT_TO_POINTER(ACTIVITY_GEN_ROAD), FALSE, NULL);
2912 if (pterr->transform_result != pterr && pterr->transform_result != NULL
2913 && effect_cumulative_max(EFT_TRANSFORM_POSSIBLE, &for_terr) > 0) {
2914 choice_dialog_add(shl, _("Transform"),
2915 G_CALLBACK(set_city_workertask),
2916 GINT_TO_POINTER(ACTIVITY_TRANSFORM), FALSE, NULL);
2919 choice_dialog_add(shl, GTK_STOCK_CANCEL, 0, 0, FALSE, NULL);
2920 choice_dialog_end(shl);
2922 g_signal_connect(shl, "destroy", G_CALLBACK(workertask_dlg_destroy),
2923 NULL);
2927 /**************************************************************************
2928 User has pressed button on citymap
2929 **************************************************************************/
2930 static gboolean button_down_citymap(GtkWidget *w, GdkEventButton *ev,
2931 gpointer data)
2933 struct city_dialog *pdialog = data;
2934 int canvas_x, canvas_y, city_x, city_y;
2936 if (!can_client_issue_orders()) {
2937 return FALSE;
2940 canvas_x = ev->x * (double)canvas_width / (double)CITYMAP_WIDTH;
2941 canvas_y = ev->y * (double)canvas_height / (double)CITYMAP_HEIGHT;
2943 if (canvas_to_city_pos(&city_x, &city_y,
2944 city_map_radius_sq_get(pdialog->pcity),
2945 canvas_x, canvas_y)) {
2946 if (ev->button == 1) {
2947 city_toggle_worker(pdialog->pcity, city_x, city_y);
2948 } else if (ev->button == 3) {
2949 struct city *pcity = pdialog->pcity;
2951 popup_workertask_dlg(pdialog->pcity,
2952 city_map_to_tile(pcity->tile, city_map_radius_sq_get(pcity),
2953 city_x, city_y));
2957 return TRUE;
2960 /****************************************************************
2961 Set map canvas to be drawn
2962 *****************************************************************/
2963 static void draw_map_canvas(struct city_dialog *pdialog)
2965 gtk_widget_queue_draw(pdialog->overview.map_canvas.darea);
2966 if (pdialog->happiness.map_canvas.darea) { /* in case of spy */
2967 gtk_widget_queue_draw(pdialog->happiness.map_canvas.darea);
2971 /********* Callbacks for Buy, Change, Sell, Worklist ************/
2972 /****************************************************************
2973 User has answered buy cost dialog
2974 *****************************************************************/
2975 static void buy_callback_response(GtkWidget *w, gint response, gpointer data)
2977 struct city_dialog *pdialog = data;
2979 if (response == GTK_RESPONSE_YES) {
2980 city_buy_production(pdialog->pcity);
2982 gtk_widget_destroy(w);
2985 /****************************************************************
2986 User has clicked buy-button
2987 *****************************************************************/
2988 static void buy_callback(GtkWidget *w, gpointer data)
2990 GtkWidget *shell;
2991 struct city_dialog *pdialog = data;
2992 const char *name = city_production_name_translation(pdialog->pcity);
2993 int value = city_production_buy_gold_cost(pdialog->pcity);
2994 char buf[1024];
2996 if (!can_client_issue_orders()) {
2997 return;
3000 fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Treasury contains %d gold.",
3001 "Treasury contains %d gold.",
3002 client_player()->economic.gold),
3003 client_player()->economic.gold);
3005 if (value <= client_player()->economic.gold) {
3006 shell = gtk_message_dialog_new(NULL,
3007 GTK_DIALOG_DESTROY_WITH_PARENT,
3008 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
3009 /* TRANS: Last %s is pre-pluralised "Treasury contains %d gold." */
3010 PL_("Buy %s for %d gold?\n%s",
3011 "Buy %s for %d gold?\n%s", value),
3012 name, value, buf);
3013 setup_dialog(shell, pdialog->shell);
3014 gtk_window_set_title(GTK_WINDOW(shell), _("Buy It!"));
3015 gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_NO);
3016 g_signal_connect(shell, "response", G_CALLBACK(buy_callback_response),
3017 pdialog);
3018 gtk_window_present(GTK_WINDOW(shell));
3019 } else {
3020 shell = gtk_message_dialog_new(NULL,
3021 GTK_DIALOG_DESTROY_WITH_PARENT,
3022 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
3023 /* TRANS: Last %s is pre-pluralised "Treasury contains %d gold." */
3024 PL_("%s costs %d gold.\n%s",
3025 "%s costs %d gold.\n%s", value),
3026 name, value, buf);
3027 setup_dialog(shell, pdialog->shell);
3028 gtk_window_set_title(GTK_WINDOW(shell), _("Buy It!"));
3029 g_signal_connect(shell, "response", G_CALLBACK(gtk_widget_destroy),
3030 NULL);
3031 gtk_window_present(GTK_WINDOW(shell));
3035 /****************************************************************************
3036 Callback for the dropdown production menu.
3037 ****************************************************************************/
3038 static void change_production_callback(GtkComboBox *combo,
3039 struct city_dialog *pdialog)
3041 GtkTreeIter iter;
3043 if (can_client_issue_orders()
3044 && gtk_combo_box_get_active_iter(combo, &iter)) {
3045 cid id;
3046 struct universal univ;
3048 gtk_tree_model_get(gtk_combo_box_get_model(combo), &iter, 2, &id, -1);
3049 univ = cid_production(id);
3050 city_change_production(pdialog->pcity, &univ);
3054 /****************************************************************
3055 User has clicked sell-button
3056 *****************************************************************/
3057 static void sell_callback(struct impr_type *pimprove, gpointer data)
3059 GtkWidget *shl;
3060 struct city_dialog *pdialog = (struct city_dialog *) data;
3061 pdialog->sell_id = improvement_number(pimprove);
3062 int price;
3064 if (!can_client_issue_orders()) {
3065 return;
3068 if (test_player_sell_building_now(client.conn.playing, pdialog->pcity,
3069 pimprove) != TR_SUCCESS) {
3070 return;
3073 price = impr_sell_gold(pimprove);
3074 shl = gtk_message_dialog_new(NULL,
3075 GTK_DIALOG_DESTROY_WITH_PARENT,
3076 GTK_MESSAGE_QUESTION,
3077 GTK_BUTTONS_YES_NO,
3078 PL_("Sell %s for %d gold?",
3079 "Sell %s for %d gold?", price),
3080 city_improvement_name_translation(pdialog->pcity, pimprove), price);
3081 setup_dialog(shl, pdialog->shell);
3082 pdialog->sell_shell = shl;
3084 gtk_window_set_title(GTK_WINDOW(shl), _("Sell It!"));
3085 gtk_window_set_position(GTK_WINDOW(shl), GTK_WIN_POS_CENTER_ON_PARENT);
3087 g_signal_connect(shl, "response",
3088 G_CALLBACK(sell_callback_response), pdialog);
3090 gtk_window_present(GTK_WINDOW(shl));
3093 /****************************************************************
3094 User has responded to sell price dialog
3095 *****************************************************************/
3096 static void sell_callback_response(GtkWidget *w, gint response, gpointer data)
3098 struct city_dialog *pdialog = data;
3100 if (response == GTK_RESPONSE_YES) {
3101 city_sell_improvement(pdialog->pcity, pdialog->sell_id);
3103 gtk_widget_destroy(w);
3105 pdialog->sell_shell = NULL;
3108 /****************************************************************
3109 this is here because it's closely related to the sell stuff
3110 *****************************************************************/
3111 static void impr_callback(GtkTreeView *view, GtkTreePath *path,
3112 GtkTreeViewColumn *col, gpointer data)
3114 GtkTreeModel *model;
3115 GtkTreeIter it;
3116 GdkWindow *win;
3117 GdkDeviceManager *manager;
3118 GdkModifierType mask;
3119 struct impr_type *pimprove;
3121 model = gtk_tree_view_get_model(view);
3123 if (!gtk_tree_model_get_iter(model, &it, path)) {
3124 return;
3127 gtk_tree_model_get(model, &it, 0, &pimprove, -1);
3129 win = gdk_get_default_root_window();
3130 manager = gdk_display_get_device_manager(gdk_window_get_display(win));
3132 gdk_window_get_device_position(win,
3133 gdk_device_manager_get_client_pointer(manager),
3134 NULL, NULL, &mask);
3136 if (!(mask & GDK_CONTROL_MASK)) {
3137 sell_callback(pimprove, data);
3138 } else {
3139 if (is_great_wonder(pimprove)) {
3140 popup_help_dialog_typed(improvement_name_translation(pimprove), HELP_WONDER);
3141 } else {
3142 popup_help_dialog_typed(improvement_name_translation(pimprove), HELP_IMPROVEMENT);
3147 /******* Callbacks for stuff on the Misc. Settings page *********/
3148 /****************************************************************
3149 Called when Rename button pressed
3150 *****************************************************************/
3151 static void rename_callback(GtkWidget *w, gpointer data)
3153 struct city_dialog *pdialog;
3155 pdialog = (struct city_dialog *) data;
3157 pdialog->rename_shell = input_dialog_create(GTK_WINDOW(pdialog->shell),
3158 /* "shellrenamecity" */
3159 _("Rename City"),
3160 _("What should we rename the city to?"),
3161 city_name_get(pdialog->pcity),
3162 rename_popup_callback, pdialog);
3165 /****************************************************************
3166 Called when user has finished with "Rename City" popup
3167 *****************************************************************/
3168 static void rename_popup_callback(gpointer data, gint response,
3169 const char *input)
3171 struct city_dialog *pdialog = data;
3173 if (pdialog) {
3174 if (response == GTK_RESPONSE_OK) {
3175 city_rename(pdialog->pcity, input);
3176 } /* else CANCEL or DELETE_EVENT */
3178 pdialog->rename_shell = NULL;
3182 /****************************************************************
3183 Sets which page will be set on reopen of dialog
3184 *****************************************************************/
3185 static void misc_whichtab_callback(GtkWidget * w, gpointer data)
3187 new_dialog_def_page = GPOINTER_TO_INT(data);
3190 /**************************************************************************
3191 City options callbacks
3192 **************************************************************************/
3193 static void cityopt_callback(GtkWidget * w, gpointer data)
3195 struct city_dialog *pdialog = (struct city_dialog *) data;
3197 if (!can_client_issue_orders()) {
3198 return;
3201 if (!pdialog->misc.block_signal){
3202 struct city *pcity = pdialog->pcity;
3203 bv_city_options new_options;
3205 fc_assert(CITYO_LAST == 3);
3207 BV_CLR_ALL(new_options);
3208 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pdialog->misc.disband_on_settler))) {
3209 BV_SET(new_options, CITYO_DISBAND);
3211 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pdialog->misc.new_citizens_radio[1]))) {
3212 BV_SET(new_options, CITYO_SCIENCE_SPECIALISTS);
3214 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pdialog->misc.new_citizens_radio[2]))) {
3215 BV_SET(new_options, CITYO_GOLD_SPECIALISTS);
3218 dsend_packet_city_options_req(&client.conn, pcity->id,new_options);
3222 /**************************************************************************
3223 refresh the city options (auto_[land, air, sea, helicopter] and
3224 disband-is-size-1) in the misc page.
3225 **************************************************************************/
3226 static void set_cityopt_values(struct city_dialog *pdialog)
3228 struct city *pcity = pdialog->pcity;
3230 pdialog->misc.block_signal = 1;
3232 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pdialog->misc.disband_on_settler),
3233 is_city_option_set(pcity, CITYO_DISBAND));
3235 if (is_city_option_set(pcity, CITYO_SCIENCE_SPECIALISTS)) {
3236 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
3237 (pdialog->misc.new_citizens_radio[1]), TRUE);
3238 } else if (is_city_option_set(pcity, CITYO_GOLD_SPECIALISTS)) {
3239 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
3240 (pdialog->misc.new_citizens_radio[2]), TRUE);
3241 } else {
3242 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
3243 (pdialog->misc.new_citizens_radio[0]), TRUE);
3245 pdialog->misc.block_signal = 0;
3248 /*************** Callbacks for: Close, Prev, Next. **************/
3249 /****************************************************************
3250 User has clicked rename city-button
3251 *****************************************************************/
3252 static void close_callback(GtkWidget *w, gpointer data)
3254 close_city_dialog((struct city_dialog *) data);
3257 /****************************************************************
3258 User has closed rename city dialog
3259 *****************************************************************/
3260 static void city_destroy_callback(GtkWidget *w, gpointer data)
3262 struct city_dialog *pdialog;
3264 pdialog = (struct city_dialog *) data;
3266 gtk_widget_hide(pdialog->shell);
3268 if (game.info.citizen_nationality) {
3269 citizens_dialog_close(pdialog->pcity);
3271 close_happiness_dialog(pdialog->pcity);
3272 close_cma_dialog(pdialog->pcity);
3274 /* Save size of the city dialog. */
3275 GUI_GTK_OPTION(citydlg_xsize)
3276 = CLIP(GUI_GTK3_CITYDLG_MIN_XSIZE,
3277 gtk_widget_get_allocated_width(pdialog->shell),
3278 GUI_GTK3_CITYDLG_MAX_XSIZE);
3279 GUI_GTK_OPTION(citydlg_ysize)
3280 = CLIP(GUI_GTK3_CITYDLG_MIN_XSIZE,
3281 gtk_widget_get_allocated_height(pdialog->shell),
3282 GUI_GTK3_CITYDLG_MAX_XSIZE);
3284 last_page
3285 = gtk_notebook_get_current_page(GTK_NOTEBOOK(pdialog->notebook));
3287 if (pdialog->popup_menu) {
3288 gtk_widget_destroy(pdialog->popup_menu);
3291 dialog_list_remove(dialog_list, pdialog);
3293 unit_node_vector_free(&pdialog->overview.supported_units);
3294 unit_node_vector_free(&pdialog->overview.present_units);
3296 if (pdialog->buy_shell) {
3297 gtk_widget_destroy(pdialog->buy_shell);
3299 if (pdialog->sell_shell) {
3300 gtk_widget_destroy(pdialog->sell_shell);
3302 if (pdialog->rename_shell) {
3303 gtk_widget_destroy(pdialog->rename_shell);
3306 cairo_surface_destroy(pdialog->map_canvas_store_unscaled);
3307 cairo_surface_destroy(pdialog->citizen_surface);
3309 free(pdialog);
3311 /* need to do this every time a new dialog is closed. */
3312 city_dialog_update_prev_next();
3315 /************************************************************************
3316 Close city dialog
3317 *************************************************************************/
3318 static void close_city_dialog(struct city_dialog *pdialog)
3320 gtk_widget_destroy(pdialog->shell);
3323 /************************************************************************
3324 Callback for the prev/next buttons. Switches to the previous/next
3325 city.
3326 *************************************************************************/
3327 static void switch_city_callback(GtkWidget *w, gpointer data)
3329 struct city_dialog *pdialog = (struct city_dialog *) data;
3330 int i, j, dir, size;
3331 struct city *new_pcity = NULL;
3333 if (client_is_global_observer()) {
3334 return;
3337 size = city_list_size(client.conn.playing->cities);
3339 fc_assert_ret(city_dialogs_have_been_initialised);
3340 fc_assert_ret(size >= 1);
3341 fc_assert_ret(city_owner(pdialog->pcity) == client.conn.playing);
3343 if (size == 1) {
3344 return;
3347 /* dir = 1 will advance to the city, dir = -1 will get previous */
3348 if (w == pdialog->next_command) {
3349 dir = 1;
3350 } else if (w == pdialog->prev_command) {
3351 dir = -1;
3352 } else {
3353 /* Always fails. */
3354 fc_assert_ret(w == pdialog->next_command
3355 || w == pdialog->prev_command);
3356 dir = 1;
3359 for (i = 0; i < size; i++) {
3360 if (pdialog->pcity == city_list_get(client.conn.playing->cities, i)) {
3361 break;
3365 fc_assert_ret(i < size);
3367 for (j = 1; j < size; j++) {
3368 struct city *other_pcity = city_list_get(client.conn.playing->cities,
3369 (i + dir * j + size) % size);
3370 struct city_dialog *other_pdialog = get_city_dialog(other_pcity);
3372 fc_assert_ret(other_pdialog != pdialog);
3373 if (!other_pdialog) {
3374 new_pcity = other_pcity;
3375 break;
3379 if (!new_pcity) {
3380 /* Every other city has an open city dialog. */
3381 return;
3384 /* cleanup happiness dialog */
3385 if (game.info.citizen_nationality) {
3386 citizens_dialog_close(pdialog->pcity);
3388 close_happiness_dialog(pdialog->pcity);
3390 pdialog->pcity = new_pcity;
3392 /* reinitialize happiness, and cma dialogs */
3393 if (game.info.citizen_nationality) {
3394 gtk_container_add(GTK_CONTAINER(pdialog->happiness.citizens),
3395 citizens_dialog_display(pdialog->pcity));
3397 gtk_container_add(GTK_CONTAINER(pdialog->happiness.widget),
3398 get_top_happiness_display(pdialog->pcity, low_citydlg));
3399 if (!client_is_observer()) {
3400 fc_assert(pdialog->cma_editor != NULL);
3401 pdialog->cma_editor->pcity = new_pcity;
3404 reset_city_worklist(pdialog->production.worklist, pdialog->pcity);
3406 can_slide = FALSE;
3407 center_tile_mapcanvas(pdialog->pcity->tile);
3408 can_slide = TRUE;
3409 if (!client_is_observer()) {
3410 set_cityopt_values(pdialog); /* need not be in real_city_dialog_refresh */
3413 real_city_dialog_refresh(pdialog->pcity);
3415 /* recenter the city map(s) */
3416 city_dialog_map_recenter(pdialog->overview.map_canvas.sw);
3417 if (pdialog->happiness.map_canvas.sw) {
3418 city_dialog_map_recenter(pdialog->happiness.map_canvas.sw);