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