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