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