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)
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 ***********************************************************************/
15 #include <fc_config.h>
23 #include <gdk/gdkkeysyms.h>
26 #include "bitvector.h"
43 #include "client_main.h"
54 /* client/gui-gtk-2.0 */
55 #include "citizensinfo.h"
61 #include "gui_stuff.h"
62 #include "happiness.h"
71 #define CITYMAP_WIDTH MIN(512, canvas_width)
72 #define CITYMAP_HEIGHT (CITYMAP_WIDTH * canvas_height / canvas_width)
73 #define CITYMAP_SCALE ((double)CITYMAP_WIDTH / (double)canvas_width)
77 /* get 'struct dialog_list' and related function */
78 #define SPECLIST_TAG dialog
79 #define SPECLIST_TYPE struct city_dialog
82 #define dialog_list_iterate(dialoglist, pdialog) \
83 TYPED_LIST_ITERATE(struct city_dialog, dialoglist, pdialog)
84 #define dialog_list_iterate_end LIST_ITERATE_END
91 /* get 'struct unit_node' and related function */
92 #define SPECVEC_TAG unit_node
93 #define SPECVEC_TYPE struct unit_node
96 #define unit_node_vector_iterate(list, elt) \
97 TYPED_VECTOR_ITERATE(struct unit_node, list, elt)
98 #define unit_node_vector_iterate_end VECTOR_ITERATE_END
100 enum { OVERVIEW_PAGE
, WORKLIST_PAGE
,
101 HAPPINESS_PAGE
, CMA_PAGE
, TRADE_PAGE
, MISC_PAGE
104 enum info_style
{ NORMAL
, ORANGE
, RED
, NUM_INFO_STYLES
};
106 #define NUM_CITIZENS_SHOWN 30
107 #define NUM_INFO_FIELDS 13 /* number of fields in city_info */
108 #define NUM_PAGES 6 /* the number of pages in city dialog notebook
109 * (+1) if you change this, you must add an
110 * entry to misc_whichtab_label[] */
112 /* minimal size for the city map scrolling windows*/
113 #define CITY_MAP_MIN_SIZE_X 200
114 #define CITY_MAP_MIN_SIZE_Y 150
116 struct city_map_canvas
{
126 GtkWidget
*name_label
;
127 GdkPixbuf
*map_canvas_store
, *map_pixbuf_unscaled
;
128 GdkPixmap
*map_canvas_store_unscaled
;
131 GtkWidget
*popup_menu
;
132 GtkWidget
*citizen_pixmap
;
135 struct city_map_canvas map_canvas
;
137 GtkWidget
*production_bar
;
138 GtkWidget
*production_combo
;
139 GtkWidget
*buy_command
;
140 GtkWidget
*improvement_list
;
142 GtkWidget
*supported_units_frame
;
143 GtkWidget
*supported_unit_table
;
145 GtkWidget
*present_units_frame
;
146 GtkWidget
*present_unit_table
;
148 struct unit_node_vector supported_units
;
149 struct unit_node_vector present_units
;
151 GtkWidget
*info_ebox
[NUM_INFO_FIELDS
];
152 GtkWidget
*info_label
[NUM_INFO_FIELDS
];
154 GtkListStore
* change_production_store
;
158 GtkWidget
*production_label
;
159 GtkWidget
*production_bar
;
160 GtkWidget
*buy_command
;
165 struct city_map_canvas map_canvas
;
168 GtkWidget
*info_ebox
[NUM_INFO_FIELDS
];
169 GtkWidget
*info_label
[NUM_INFO_FIELDS
];
173 struct cma_dialog
*cma_editor
;
176 GtkWidget
*rename_command
;
177 GtkWidget
*new_citizens_radio
[3];
178 GtkWidget
*disband_on_settler
;
179 GtkWidget
*whichtab_radio
[NUM_PAGES
];
183 GtkWidget
*buy_shell
, *sell_shell
;
184 GtkTreeSelection
*change_selection
;
185 GtkWidget
*rename_shell
, *rename_input
;
187 GtkWidget
*show_units_command
;
188 GtkWidget
*prev_command
, *next_command
;
190 Impr_type_id sell_id
;
195 static GtkRcStyle
*info_label_style
[NUM_INFO_STYLES
] = { NULL
, NULL
, NULL
};
197 static struct dialog_list
*dialog_list
;
198 static bool city_dialogs_have_been_initialised
= FALSE
;
199 static int canvas_width
, canvas_height
;
200 static int new_dialog_def_page
= OVERVIEW_PAGE
;
201 static int last_page
= OVERVIEW_PAGE
;
203 /****************************************/
205 static void initialize_city_dialogs(void);
206 static void city_dialog_map_create(struct city_dialog
*pdialog
,
207 struct city_map_canvas
*cmap_canvas
);
208 static void city_dialog_map_recenter(GtkWidget
*map_canvas_sw
);
210 static struct city_dialog
*get_city_dialog(struct city
*pcity
);
211 static gboolean
keyboard_handler(GtkWidget
* widget
, GdkEventKey
* event
,
212 struct city_dialog
*pdialog
);
214 static GtkWidget
*create_city_info_table(struct city_dialog
*pdialog
,
215 GtkWidget
**info_ebox
,
216 GtkWidget
**info_label
);
217 static void create_and_append_overview_page(struct city_dialog
*pdialog
);
218 static void create_and_append_worklist_page(struct city_dialog
*pdialog
);
219 static void create_and_append_happiness_page(struct city_dialog
*pdialog
);
220 static void create_and_append_cma_page(struct city_dialog
*pdialog
);
221 static void create_and_append_settings_page(struct city_dialog
*pdialog
);
223 static struct city_dialog
*create_city_dialog(struct city
*pcity
);
225 static void city_dialog_update_title(struct city_dialog
*pdialog
);
226 static void city_dialog_update_citizens(struct city_dialog
*pdialog
);
227 static void city_dialog_update_information(GtkWidget
**info_ebox
,
228 GtkWidget
**info_label
,
229 struct city_dialog
*pdialog
);
230 static void city_dialog_update_map(struct city_dialog
*pdialog
);
231 static void city_dialog_update_building(struct city_dialog
*pdialog
);
232 static void city_dialog_update_improvement_list(struct city_dialog
234 static void city_dialog_update_supported_units(struct city_dialog
236 static void city_dialog_update_present_units(struct city_dialog
*pdialog
);
237 static void city_dialog_update_prev_next(void);
239 static void show_units_callback(GtkWidget
* w
, gpointer data
);
241 static gboolean
supported_unit_callback(GtkWidget
* w
, GdkEventButton
* ev
,
243 static gboolean
present_unit_callback(GtkWidget
* w
, GdkEventButton
* ev
,
245 static gboolean
supported_unit_middle_callback(GtkWidget
* w
,
248 static gboolean
present_unit_middle_callback(GtkWidget
* w
,
252 static void unit_center_callback(GtkWidget
* w
, gpointer data
);
253 static void unit_activate_callback(GtkWidget
* w
, gpointer data
);
254 static void supported_unit_activate_close_callback(GtkWidget
* w
,
256 static void present_unit_activate_close_callback(GtkWidget
* w
,
258 static void unit_load_callback(GtkWidget
* w
, gpointer data
);
259 static void unit_unload_callback(GtkWidget
* w
, gpointer data
);
260 static void unit_sentry_callback(GtkWidget
* w
, gpointer data
);
261 static void unit_fortify_callback(GtkWidget
* w
, gpointer data
);
262 static void unit_disband_callback(GtkWidget
* w
, gpointer data
);
263 static void unit_homecity_callback(GtkWidget
* w
, gpointer data
);
264 static void unit_upgrade_callback(GtkWidget
* w
, gpointer data
);
266 static gboolean
citizens_callback(GtkWidget
* w
, GdkEventButton
* ev
,
268 static gboolean
button_down_citymap(GtkWidget
* w
, GdkEventButton
* ev
,
270 static void draw_map_canvas(struct city_dialog
*pdialog
);
272 static void buy_callback(GtkWidget
* w
, gpointer data
);
273 static void change_production_callback(GtkComboBox
*combo
,
274 struct city_dialog
*pdialog
);
276 static void sell_callback(struct impr_type
*pimprove
, gpointer data
);
277 static void sell_callback_response(GtkWidget
*w
, gint response
, gpointer data
);
279 static void impr_callback(GtkTreeView
*view
, GtkTreePath
*path
,
280 GtkTreeViewColumn
*col
, gpointer data
);
282 static void rename_callback(GtkWidget
* w
, gpointer data
);
283 static void rename_popup_callback(gpointer data
, gint response
,
285 static void set_cityopt_values(struct city_dialog
*pdialog
);
286 static void cityopt_callback(GtkWidget
* w
, gpointer data
);
287 static void misc_whichtab_callback(GtkWidget
* w
, gpointer data
);
289 static void city_destroy_callback(GtkWidget
*w
, gpointer data
);
290 static void close_city_dialog(struct city_dialog
*pdialog
);
291 static void close_callback(GtkWidget
*w
, gpointer data
);
292 static void switch_city_callback(GtkWidget
* w
, gpointer data
);
294 /****************************************************************
295 Called to set the dimensions of the city dialog, both on
296 startup and if the tileset is changed.
297 *****************************************************************/
298 static void init_citydlg_dimensions(void)
300 canvas_width
= get_citydlg_canvas_width();
301 canvas_height
= get_citydlg_canvas_height();
304 /****************************************************************
305 Initialize stuff needed for city dialogs
306 *****************************************************************/
307 static void initialize_city_dialogs(void)
310 GdkColor orange
= { 0, 65535, 32768, 0 }; /* not currently used */
311 GdkColor red
= { 0, 65535, 0, 0 };
313 fc_assert_ret(!city_dialogs_have_been_initialised
);
315 dialog_list
= dialog_list_new();
316 init_citydlg_dimensions();
318 /* make the styles */
320 for (i
= 0; i
< NUM_INFO_STYLES
; i
++) {
321 info_label_style
[i
] = gtk_rc_style_new();
323 /* info_syle[NORMAL] is normal, don't change it */
324 info_label_style
[ORANGE
]->color_flags
[GTK_STATE_NORMAL
] |= GTK_RC_FG
;
325 info_label_style
[ORANGE
]->fg
[GTK_STATE_NORMAL
] = orange
;
326 info_label_style
[RED
]->color_flags
[GTK_STATE_NORMAL
] |= GTK_RC_FG
;
327 info_label_style
[RED
]->fg
[GTK_STATE_NORMAL
] = red
;
329 city_dialogs_have_been_initialised
= TRUE
;
332 /****************************************************************
333 Called when the tileset changes.
334 *****************************************************************/
335 void reset_city_dialogs(void)
337 if (!city_dialogs_have_been_initialised
) {
341 init_citydlg_dimensions();
343 dialog_list_iterate(dialog_list
, pdialog
) {
344 /* There's no reasonable way to resize a GtkPixcomm, so we don't try.
345 Instead we just redraw the overview within the existing area. The
346 player has to close and reopen the dialog to fix this. */
347 city_dialog_update_map(pdialog
);
348 } dialog_list_iterate_end
;
350 popdown_all_city_dialogs();
353 /****************************************************************
354 Return city dialog of the given city, or NULL is it doesn't
356 *****************************************************************/
357 static struct city_dialog
*get_city_dialog(struct city
*pcity
)
359 if (!city_dialogs_have_been_initialised
) {
360 initialize_city_dialogs();
363 dialog_list_iterate(dialog_list
, pdialog
) {
364 if (pdialog
->pcity
== pcity
)
367 dialog_list_iterate_end
;
371 /***************************************************************************
372 Create a city map widget; used in the overview and in the happiness page.
373 ****************************************************************************/
374 static void city_dialog_map_create(struct city_dialog
*pdialog
,
375 struct city_map_canvas
*cmap_canvas
)
377 GtkWidget
*sw
, *align
, *ebox
, *pixmap
;
379 sw
= gtk_scrolled_window_new(NULL
, NULL
);
380 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
),
381 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
382 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
),
385 align
= gtk_alignment_new(0.5, 0.5, 0.0, 0.0);
386 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw
), align
);
388 ebox
= gtk_event_box_new();
389 gtk_widget_add_events(ebox
, GDK_BUTTON_PRESS_MASK
);
390 gtk_container_add(GTK_CONTAINER(align
), ebox
);
392 pixmap
= gtk_image_new_from_pixbuf(pdialog
->map_canvas_store
);
393 g_signal_connect(ebox
, "button_press_event",
394 G_CALLBACK(button_down_citymap
), pdialog
);
395 gtk_container_add(GTK_CONTAINER(ebox
), pixmap
);
397 /* save all widgets for the city map */
398 cmap_canvas
->sw
= sw
;
399 cmap_canvas
->ebox
= ebox
;
400 cmap_canvas
->pixmap
= pixmap
;
403 /****************************************************************
404 Center city dialog map.
405 *****************************************************************/
406 static void city_dialog_map_recenter(GtkWidget
*map_canvas_sw
) {
407 GtkAdjustment
*adjust
= NULL
;
410 fc_assert_ret(map_canvas_sw
!= NULL
);
412 adjust
= gtk_scrolled_window_get_hadjustment(
413 GTK_SCROLLED_WINDOW(map_canvas_sw
));
414 value
= (adjust
->lower
+ adjust
->upper
- adjust
->page_size
) / 2;
415 gtk_adjustment_set_value(adjust
, value
);
416 gtk_adjustment_value_changed(adjust
);
418 adjust
= gtk_scrolled_window_get_vadjustment(
419 GTK_SCROLLED_WINDOW(map_canvas_sw
));
420 value
= (adjust
->lower
+ adjust
->upper
- adjust
->page_size
) / 2;
421 gtk_adjustment_set_value(adjust
, value
);
422 gtk_adjustment_value_changed(adjust
);
425 /****************************************************************
426 Refresh city dialog of the given city
427 *****************************************************************/
428 void real_city_dialog_refresh(struct city
*pcity
)
430 struct city_dialog
*pdialog
= get_city_dialog(pcity
);
432 log_debug("CITYMAP_WIDTH: %d", CITYMAP_WIDTH
);
433 log_debug("CITYMAP_HEIGHT: %d", CITYMAP_HEIGHT
);
434 log_debug("CITYMAP_SCALE: %.3f", CITYMAP_SCALE
);
436 if (city_owner(pcity
) == client
.conn
.playing
) {
437 city_report_dialog_update_city(pcity
);
438 economy_report_dialog_update();
444 city_dialog_update_title(pdialog
);
445 city_dialog_update_citizens(pdialog
);
446 city_dialog_update_information(pdialog
->overview
.info_ebox
,
447 pdialog
->overview
.info_label
, pdialog
);
448 city_dialog_update_map(pdialog
);
449 city_dialog_update_building(pdialog
);
450 city_dialog_update_improvement_list(pdialog
);
451 city_dialog_update_supported_units(pdialog
);
452 city_dialog_update_present_units(pdialog
);
454 if (!client_has_player() || city_owner(pcity
) == client_player()) {
455 bool have_present_units
= (unit_list_size(pcity
->tile
->units
) > 0);
457 refresh_worklist(pdialog
->production
.worklist
);
459 city_dialog_update_information(pdialog
->happiness
.info_ebox
,
460 pdialog
->happiness
.info_label
, pdialog
);
461 refresh_happiness_dialog(pdialog
->pcity
);
462 if (game
.info
.citizen_nationality
) {
463 citizens_dialog_refresh(pdialog
->pcity
);
466 if (!client_is_observer()) {
467 refresh_cma_dialog(pdialog
->pcity
, REFRESH_ALL
);
470 gtk_widget_set_sensitive(pdialog
->show_units_command
,
471 can_client_issue_orders() &&
474 /* Set the buttons we do not want live while a Diplomat investigates */
475 gtk_widget_set_sensitive(pdialog
->show_units_command
, FALSE
);
479 /****************************************************************
480 Refresh city dialogs of unit's homecity and city where unit
482 *****************************************************************/
483 void refresh_unit_city_dialogs(struct unit
*punit
)
485 struct city
*pcity_sup
, *pcity_pre
;
486 struct city_dialog
*pdialog
;
488 pcity_sup
= game_city_by_number(punit
->homecity
);
489 pcity_pre
= tile_city(unit_tile(punit
));
491 if (pcity_sup
&& (pdialog
= get_city_dialog(pcity_sup
)))
492 city_dialog_update_supported_units(pdialog
);
494 if (pcity_pre
&& (pdialog
= get_city_dialog(pcity_pre
)))
495 city_dialog_update_present_units(pdialog
);
498 /****************************************************************
499 popup the dialog 10% inside the main-window
500 *****************************************************************/
501 void real_city_dialog_popup(struct city
*pcity
)
503 struct city_dialog
*pdialog
;
505 if (!(pdialog
= get_city_dialog(pcity
))) {
506 pdialog
= create_city_dialog(pcity
);
509 gtk_window_present(GTK_WINDOW(pdialog
->shell
));
511 /* center the city map(s); this must be *after* the city dialog was drawn
512 * else the size information is missing! */
513 city_dialog_map_recenter(pdialog
->overview
.map_canvas
.sw
);
514 if (pdialog
->happiness
.map_canvas
.sw
) {
515 city_dialog_map_recenter(pdialog
->happiness
.map_canvas
.sw
);
519 /****************************************************************
520 Return whether city dialog for given city is open
521 *****************************************************************/
522 bool city_dialog_is_open(struct city
*pcity
)
524 return get_city_dialog(pcity
) != NULL
;
527 /****************************************************************
529 *****************************************************************/
530 void popdown_city_dialog(struct city
*pcity
)
532 struct city_dialog
*pdialog
= get_city_dialog(pcity
);
535 close_city_dialog(pdialog
);
539 /****************************************************************
541 *****************************************************************/
542 void popdown_all_city_dialogs(void)
546 if (!city_dialogs_have_been_initialised
) {
550 while (dialog_list_size(dialog_list
)) {
551 close_city_dialog(dialog_list_get(dialog_list
, 0));
553 dialog_list_destroy(dialog_list
);
555 for (i
= 0; i
< NUM_INFO_STYLES
; i
++) {
556 g_object_unref(info_label_style
[i
]);
559 city_dialogs_have_been_initialised
= FALSE
;
562 /**************************************************************************
563 Keyboard handler for city dialog
564 **************************************************************************/
565 static gboolean
keyboard_handler(GtkWidget
* widget
, GdkEventKey
* event
,
566 struct city_dialog
*pdialog
)
568 if (event
->state
& GDK_CONTROL_MASK
) {
569 switch (event
->keyval
) {
571 gtk_notebook_prev_page(GTK_NOTEBOOK(pdialog
->notebook
));
575 gtk_notebook_next_page(GTK_NOTEBOOK(pdialog
->notebook
));
586 /**************************************************************************
587 Destroy info popup dialog when button released
588 **************************************************************************/
589 static gboolean
show_info_button_release(GtkWidget
*w
, GdkEventButton
*ev
,
593 gdk_pointer_ungrab(GDK_CURRENT_TIME
);
594 gtk_widget_destroy(w
);
598 enum { FIELD_FOOD
, FIELD_SHIELD
, FIELD_TRADE
, FIELD_GOLD
, FIELD_LUXURY
,
599 FIELD_SCIENCE
, FIELD_GRANARY
, FIELD_GROWTH
, FIELD_CORRUPTION
,
600 FIELD_WASTE
, FIELD_CULTURE
, FIELD_POLLUTION
, FIELD_ILLNESS
603 /****************************************************************
605 *****************************************************************/
606 static gboolean
show_info_popup(GtkWidget
*w
, GdkEventButton
*ev
,
609 struct city_dialog
*pdialog
= g_object_get_data(G_OBJECT(w
), "pdialog");
611 if (ev
->button
== 1) {
612 GtkWidget
*p
, *label
, *frame
;
615 switch (GPOINTER_TO_UINT(data
)) {
617 get_city_dialog_output_text(pdialog
->pcity
, O_FOOD
, buf
, sizeof(buf
));
620 get_city_dialog_output_text(pdialog
->pcity
, O_SHIELD
,
624 get_city_dialog_output_text(pdialog
->pcity
, O_TRADE
, buf
, sizeof(buf
));
627 get_city_dialog_output_text(pdialog
->pcity
, O_GOLD
, buf
, sizeof(buf
));
630 get_city_dialog_output_text(pdialog
->pcity
, O_SCIENCE
,
634 get_city_dialog_output_text(pdialog
->pcity
, O_LUXURY
,
638 get_city_dialog_culture_text(pdialog
->pcity
, buf
, sizeof(buf
));
640 case FIELD_POLLUTION
:
641 get_city_dialog_pollution_text(pdialog
->pcity
, buf
, sizeof(buf
));
644 get_city_dialog_illness_text(pdialog
->pcity
, buf
, sizeof(buf
));
650 p
= gtk_window_new(GTK_WINDOW_POPUP
);
651 gtk_widget_set_name(p
, "Freeciv");
652 gtk_container_set_border_width(GTK_CONTAINER(p
), 2);
653 gtk_window_set_position(GTK_WINDOW(p
), GTK_WIN_POS_MOUSE
);
655 frame
= gtk_frame_new(NULL
);
656 gtk_container_add(GTK_CONTAINER(p
), frame
);
658 label
= gtk_label_new(buf
);
659 gtk_widget_set_name(label
, "city_info_label");
660 gtk_misc_set_padding(GTK_MISC(label
), 4, 4);
661 gtk_container_add(GTK_CONTAINER(frame
), label
);
662 gtk_widget_show_all(p
);
664 gdk_pointer_grab(p
->window
, TRUE
, GDK_BUTTON_RELEASE_MASK
,
665 NULL
, NULL
, ev
->time
);
668 g_signal_connect_after(p
, "button_release_event",
669 G_CALLBACK(show_info_button_release
), NULL
);
673 /****************************************************************
674 used once in the overview page and once in the happiness page
675 **info_label points to the info_label in the respective struct
676 ****************************************************************/
677 static GtkWidget
*create_city_info_table(struct city_dialog
*pdialog
,
678 GtkWidget
**info_ebox
,
679 GtkWidget
**info_label
)
682 GtkWidget
*hbox
, *table
, *label
, *ebox
;
684 static const char *output_label
[NUM_INFO_FIELDS
] = { N_("Food:"),
698 static bool output_label_done
;
700 hbox
= gtk_hbox_new(TRUE
, 0); /* to give the table padding inside the frame */
702 table
= gtk_table_new(NUM_INFO_FIELDS
, 2, FALSE
);
703 gtk_table_set_row_spacing(GTK_TABLE(table
), 2, 10);
704 gtk_table_set_row_spacing(GTK_TABLE(table
), 5, 10);
705 gtk_table_set_row_spacing(GTK_TABLE(table
), 7, 10);
706 gtk_table_set_col_spacing(GTK_TABLE(table
), 0, 5);
707 gtk_box_pack_start(GTK_BOX(hbox
), table
, FALSE
, FALSE
, 4);
709 intl_slist(ARRAY_SIZE(output_label
), output_label
, &output_label_done
);
711 for (i
= 0; i
< NUM_INFO_FIELDS
; i
++) {
712 label
= gtk_label_new(output_label
[i
]);
713 gtk_widget_set_name(label
, "city_label"); /* for font style? */
714 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
715 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, i
, i
+ 1, GTK_FILL
, 0,
718 ebox
= gtk_event_box_new();
719 g_object_set_data(G_OBJECT(ebox
), "pdialog", pdialog
);
720 g_signal_connect(ebox
, "button_press_event",
721 G_CALLBACK(show_info_popup
), GUINT_TO_POINTER(i
));
724 label
= gtk_label_new("");
725 info_label
[i
] = label
;
726 gtk_widget_set_name(label
, "city_label"); /* ditto */
727 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
729 gtk_container_add(GTK_CONTAINER(ebox
), label
);
731 gtk_table_attach(GTK_TABLE(table
), ebox
, 1, 2, i
, i
+ 1, GTK_FILL
, 0,
735 gtk_widget_show_all(hbox
);
739 /****************************************************************
740 **** Overview page ****
741 +- GtkWidget *page ------------------------------------------+
742 | +- GtkWidget *middle -----------+------------------------+ |
743 | | City map | Production | |
744 | +-------------------------------+------------------------+ |
745 +------------------------------------------------------------+
746 | +- GtkWidget *bottom -------+----------------------------+ |
747 | | Info | +- GtkWidget *right -----+ | |
748 | | | | supported units | | |
749 | | | +------------------------+ | |
750 | | | | present units | | |
751 | | | +------------------------+ | |
752 | +---------------------------+----------------------------+ |
753 +------------------------------------------------------------+
754 *****************************************************************/
755 static void create_and_append_overview_page(struct city_dialog
*pdialog
)
757 GtkWidget
*page
, *middle
, *bottom
;
758 GtkWidget
*hbox
, *right
, *vbox
, *align
, *frame
, *table
;
759 GtkWidget
*label
, *sw
, *view
, *bar
, *production_combo
;
760 GtkCellRenderer
*rend
;
762 GtkListStore
*production_store
;
763 /* TRANS: Overview tab in city dialog */
764 const char *tab_title
= _("_Overview");
765 int unit_height
= tileset_unit_with_upkeep_height(tileset
);
768 page
= gtk_vbox_new(FALSE
, 0);
769 gtk_container_set_border_width(GTK_CONTAINER(page
), 8);
770 label
= gtk_label_new_with_mnemonic(tab_title
);
771 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog
->notebook
), page
, label
);
773 /* middle: city map, improvements */
774 middle
= gtk_hbox_new(TRUE
, 6);
775 gtk_box_pack_start(GTK_BOX(page
), middle
, TRUE
, TRUE
, 0);
778 frame
= gtk_frame_new(_("City map"));
779 gtk_widget_set_size_request(frame
, CITY_MAP_MIN_SIZE_X
,
780 CITY_MAP_MIN_SIZE_Y
);
781 gtk_box_pack_start(GTK_BOX(middle
), frame
, FALSE
, TRUE
, 0);
783 city_dialog_map_create(pdialog
, &pdialog
->overview
.map_canvas
);
784 gtk_container_add(GTK_CONTAINER(frame
), pdialog
->overview
.map_canvas
.sw
);
787 vbox
= gtk_vbox_new(FALSE
, 0);
788 gtk_box_pack_start(GTK_BOX(middle
), vbox
, TRUE
, TRUE
, 0);
790 store
= gtk_list_store_new(5, G_TYPE_POINTER
, GDK_TYPE_PIXBUF
,
791 G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_BOOLEAN
);
793 view
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(store
));
794 g_object_unref(store
);
795 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
796 gtk_widget_set_name(view
, "small_font");
797 pdialog
->overview
.improvement_list
= view
;
799 gtk_widget_set_tooltip_markup(view
,
800 _("Press <b>ENTER</b> or double-click to sell an improvement."));
802 rend
= gtk_cell_renderer_pixbuf_new();
803 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), -1, NULL
,
804 rend
, "pixbuf", 1, NULL
);
805 rend
= gtk_cell_renderer_text_new();
806 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), -1, NULL
,
808 "strikethrough", 4, NULL
);
809 rend
= gtk_cell_renderer_text_new();
810 g_object_set(rend
, "xalign", 1.0, NULL
);
811 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), -1, NULL
,
813 "strikethrough", 4, NULL
);
815 g_signal_connect(view
, "row_activated", G_CALLBACK(impr_callback
),
818 label
= g_object_new(GTK_TYPE_LABEL
, "label", _("Production:"),
819 "xalign", 0.0, "yalign", 0.5, NULL
);
820 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
822 hbox
= gtk_hbox_new(FALSE
, 10);
823 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
825 production_store
= gtk_list_store_new(4, GDK_TYPE_PIXBUF
, G_TYPE_STRING
,
826 G_TYPE_INT
, G_TYPE_BOOLEAN
);
827 pdialog
->overview
.change_production_store
= production_store
;
830 gtk_combo_box_new_with_model(GTK_TREE_MODEL(production_store
));
831 pdialog
->overview
.production_combo
= production_combo
;
832 gtk_box_pack_start(GTK_BOX(hbox
), production_combo
, TRUE
, TRUE
, 0);
833 g_object_unref(production_store
);
834 g_signal_connect(production_combo
, "changed",
835 G_CALLBACK(change_production_callback
), pdialog
);
837 gtk_cell_layout_clear(GTK_CELL_LAYOUT(production_combo
));
838 rend
= gtk_cell_renderer_pixbuf_new();
839 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(production_combo
), rend
, TRUE
);
840 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(production_combo
),
841 rend
, "pixbuf", 0, NULL
);
842 g_object_set(rend
, "xalign", 0.0, NULL
);
844 rend
= gtk_cell_renderer_text_new();
845 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(production_combo
), rend
, TRUE
);
846 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(production_combo
),
847 rend
, "text", 1, "strikethrough", 3, NULL
);
849 bar
= gtk_progress_bar_new();
850 pdialog
->overview
.production_bar
= bar
;
851 gtk_container_add(GTK_CONTAINER(production_combo
), bar
);
852 gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(production_combo
), 3);
854 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(bar
), _("%d/%d %d turns"));
856 pdialog
->overview
.buy_command
= gtk_stockbutton_new(GTK_STOCK_EXECUTE
,
858 gtk_box_pack_start(GTK_BOX(hbox
), pdialog
->overview
.buy_command
,
860 g_signal_connect(pdialog
->overview
.buy_command
, "clicked",
861 G_CALLBACK(buy_callback
), pdialog
);
863 label
= g_object_new(GTK_TYPE_LABEL
, "use-underline", TRUE
,
864 "mnemonic-widget", view
,
865 "label", _("I_mprovements:"),
866 "xalign", 0.0, "yalign", 0.5, NULL
);
867 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
869 sw
= gtk_scrolled_window_new(NULL
, NULL
);
870 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
),
871 GTK_SHADOW_ETCHED_IN
);
872 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
),
873 GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
874 gtk_box_pack_start(GTK_BOX(vbox
), sw
, TRUE
, TRUE
, 0);
876 gtk_container_add(GTK_CONTAINER(sw
), view
);
878 /* bottom: info, units */
879 bottom
= gtk_hbox_new(FALSE
, 6);
880 gtk_box_pack_start(GTK_BOX(page
), bottom
, FALSE
, TRUE
, 0);
883 frame
= gtk_frame_new(_("Info"));
884 gtk_box_pack_start(GTK_BOX(bottom
), frame
, FALSE
, TRUE
, 0);
886 align
= gtk_alignment_new(0.5, 0.5, 0, 0);
887 gtk_container_add(GTK_CONTAINER(frame
), align
);
889 table
= create_city_info_table(pdialog
,
890 pdialog
->overview
.info_ebox
,
891 pdialog
->overview
.info_label
);
892 gtk_container_add(GTK_CONTAINER(align
), table
);
894 /* right: present and supported units (overview page) */
895 right
= gtk_vbox_new(FALSE
, 0);
896 gtk_box_pack_start(GTK_BOX(bottom
), right
, TRUE
, TRUE
, 0);
898 pdialog
->overview
.supported_units_frame
= gtk_frame_new("");
899 gtk_box_pack_start(GTK_BOX(right
), pdialog
->overview
.supported_units_frame
,
901 pdialog
->overview
.present_units_frame
= gtk_frame_new("");
902 gtk_box_pack_start(GTK_BOX(right
), pdialog
->overview
.present_units_frame
,
905 /* supported units */
906 sw
= gtk_scrolled_window_new(NULL
, NULL
);
907 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
),
908 GTK_POLICY_AUTOMATIC
, GTK_POLICY_NEVER
);
909 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
),
911 gtk_container_add(GTK_CONTAINER(pdialog
->overview
.supported_units_frame
),
914 align
= gtk_alignment_new(0.0, 0.0, 0.0, 0.0);
915 gtk_widget_set_size_request(align
, -1, unit_height
);
916 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw
), align
);
918 table
= gtk_table_new(0, 0, FALSE
);
919 gtk_table_set_col_spacings(GTK_TABLE(table
), 2);
920 gtk_container_add(GTK_CONTAINER(align
), table
);
922 gtk_container_set_focus_hadjustment(GTK_CONTAINER(table
),
923 gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(sw
)));
924 gtk_container_set_focus_vadjustment(GTK_CONTAINER(table
),
925 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw
)));
927 pdialog
->overview
.supported_unit_table
= table
;
928 unit_node_vector_init(&pdialog
->overview
.supported_units
);
931 sw
= gtk_scrolled_window_new(NULL
, NULL
);
932 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
),
933 GTK_POLICY_AUTOMATIC
, GTK_POLICY_NEVER
);
934 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
),
936 gtk_container_add(GTK_CONTAINER(pdialog
->overview
.present_units_frame
), sw
);
938 align
= gtk_alignment_new(0.0, 0.0, 0.0, 0.0);
939 gtk_widget_set_size_request(align
, -1, unit_height
);
940 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw
), align
);
942 table
= gtk_table_new(0, 0, FALSE
);
943 gtk_table_set_col_spacings(GTK_TABLE(table
), 2);
944 gtk_container_add(GTK_CONTAINER(align
), table
);
946 gtk_container_set_focus_hadjustment(GTK_CONTAINER(table
),
947 gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(sw
)));
948 gtk_container_set_focus_vadjustment(GTK_CONTAINER(table
),
949 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw
)));
951 pdialog
->overview
.present_unit_table
= table
;
952 unit_node_vector_init(&pdialog
->overview
.present_units
);
955 gtk_widget_show_all(page
);
959 /****************************************************************
960 Something dragged to worklist dialog
961 *****************************************************************/
963 target_drag_data_received(GtkWidget
*w
, GdkDragContext
*context
,
964 gint x
, gint y
, GtkSelectionData
*data
,
965 guint info
, guint time
, gpointer user_data
)
967 struct city_dialog
*pdialog
= (struct city_dialog
*) user_data
;
971 if (NULL
!= client
.conn
.playing
972 && city_owner(pdialog
->pcity
) != client
.conn
.playing
) {
973 gtk_drag_finish(context
, FALSE
, FALSE
, time
);
976 if (gtk_tree_get_row_drag_data(data
, &model
, &path
)) {
979 if (gtk_tree_model_get_iter(model
, &it
, path
)) {
981 struct universal univ
;
983 gtk_tree_model_get(model
, &it
, 0, &id
, -1);
984 univ
= cid_production(id
);
985 city_change_production(pdialog
->pcity
, &univ
);
986 gtk_drag_finish(context
, TRUE
, FALSE
, time
);
988 gtk_tree_path_free(path
);
991 gtk_drag_finish(context
, FALSE
, FALSE
, time
);
994 /****************************************************************
995 **** Production Page ****
996 *****************************************************************/
997 static void create_and_append_worklist_page(struct city_dialog
*pdialog
)
999 const char *tab_title
= _("P_roduction");
1000 GtkWidget
*label
= gtk_label_new_with_mnemonic(tab_title
);
1001 GtkWidget
*page
, *hbox
, *editor
, *bar
;
1003 page
= gtk_vbox_new(FALSE
, 0);
1004 gtk_container_set_border_width(GTK_CONTAINER(page
), 8);
1005 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog
->notebook
), page
, label
);
1007 /* stuff that's being currently built */
1009 label
= g_object_new(GTK_TYPE_LABEL
,
1010 "label", _("Production:"),
1011 "xalign", 0.0, "yalign", 0.5, NULL
);
1012 pdialog
->production
.production_label
= label
;
1013 gtk_box_pack_start(GTK_BOX(page
), label
, FALSE
, FALSE
, 0);
1015 hbox
= gtk_hbox_new(FALSE
, 10);
1016 gtk_box_pack_start(GTK_BOX(page
), hbox
, FALSE
, FALSE
, 2);
1018 /* The label is set in city_dialog_update_building() */
1019 bar
= gtk_progress_bar_new();
1020 pdialog
->production
.production_bar
= bar
;
1021 gtk_box_pack_start(GTK_BOX(hbox
), bar
, TRUE
, TRUE
, 0);
1022 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(bar
), _("%d/%d %d turns"));
1024 add_worklist_dnd_target(bar
);
1025 g_signal_connect(bar
, "drag_data_received",
1026 G_CALLBACK(target_drag_data_received
), pdialog
);
1028 pdialog
->production
.buy_command
= gtk_stockbutton_new(GTK_STOCK_EXECUTE
,
1030 gtk_box_pack_start(GTK_BOX(hbox
), pdialog
->production
.buy_command
,
1033 g_signal_connect(pdialog
->production
.buy_command
, "clicked",
1034 G_CALLBACK(buy_callback
), pdialog
);
1037 editor
= create_worklist();
1038 reset_city_worklist(editor
, pdialog
->pcity
);
1039 gtk_box_pack_start(GTK_BOX(page
), editor
, TRUE
, TRUE
, 6);
1040 pdialog
->production
.worklist
= editor
;
1042 gtk_widget_show_all(page
);
1045 /***************************************************************************
1046 **** Happiness Page ****
1047 +- GtkWidget *page ----------+-------------------------------------------+
1048 | +- GtkWidget *left ------+ | +- GtkWidget *right --------------------+ |
1049 | | Info | | | City map | |
1050 | +- GtkWidget *citizens --+ | +- GtkWidget pdialog->happiness.widget -+ |
1051 | | Citizens data | | | Happiness | |
1052 | +------------------------+ | +---------------------------------------+ |
1053 +----------------------------+-------------------------------------------+
1054 ****************************************************************************/
1055 static void create_and_append_happiness_page(struct city_dialog
*pdialog
)
1057 GtkWidget
*page
, *label
, *table
, *align
, *right
, *left
, *frame
;
1058 const char *tab_title
= _("Happ_iness");
1061 page
= gtk_hbox_new(FALSE
, 6);
1062 gtk_container_set_border_width(GTK_CONTAINER(page
), 8);
1063 label
= gtk_label_new_with_mnemonic(tab_title
);
1064 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog
->notebook
), page
, label
);
1066 /* left: info, citizens */
1067 left
= gtk_vbox_new(FALSE
, 0);
1068 gtk_box_pack_start(GTK_BOX(page
), left
, FALSE
, TRUE
, 0);
1070 /* upper left: info */
1071 frame
= gtk_frame_new(_("Info"));
1072 gtk_box_pack_start(GTK_BOX(left
), frame
, FALSE
, TRUE
, 0);
1074 align
= gtk_alignment_new(0.5, 0, 0, 0);
1075 gtk_container_add(GTK_CONTAINER(frame
), align
);
1077 table
= create_city_info_table(pdialog
,
1078 pdialog
->happiness
.info_ebox
,
1079 pdialog
->happiness
.info_label
);
1080 gtk_container_add(GTK_CONTAINER(align
), table
);
1082 /* lower left: citizens */
1083 if (game
.info
.citizen_nationality
) {
1084 pdialog
->happiness
.citizens
= gtk_vbox_new(FALSE
, 0);
1085 gtk_box_pack_start(GTK_BOX(left
), pdialog
->happiness
.citizens
,
1087 gtk_box_pack_start(GTK_BOX(pdialog
->happiness
.citizens
),
1088 citizens_dialog_display(pdialog
->pcity
),
1092 /* right: city map, happiness */
1093 right
= gtk_vbox_new(FALSE
, 0);
1094 gtk_box_pack_start(GTK_BOX(page
), right
, TRUE
, TRUE
, 0);
1096 /* upper right: city map */
1097 frame
= gtk_frame_new(_("City map"));
1098 gtk_widget_set_size_request(frame
, CITY_MAP_MIN_SIZE_X
,
1099 CITY_MAP_MIN_SIZE_Y
);
1100 gtk_box_pack_start(GTK_BOX(right
), frame
, TRUE
, TRUE
, 0);
1102 city_dialog_map_create(pdialog
, &pdialog
->happiness
.map_canvas
);
1103 gtk_container_add(GTK_CONTAINER(frame
), pdialog
->happiness
.map_canvas
.sw
);
1105 /* lower right: happiness */
1106 pdialog
->happiness
.widget
= gtk_vbox_new(FALSE
, 0);
1107 gtk_box_pack_start(GTK_BOX(right
), pdialog
->happiness
.widget
, FALSE
, TRUE
, 0);
1108 gtk_box_pack_start(GTK_BOX(pdialog
->happiness
.widget
),
1109 get_top_happiness_display(pdialog
->pcity
), TRUE
, TRUE
, 0);
1112 gtk_widget_show_all(page
);
1115 /****************************************************************
1116 **** Citizen Management Agent (CMA) Page ****
1117 *****************************************************************/
1118 static void create_and_append_cma_page(struct city_dialog
*pdialog
)
1120 GtkWidget
*page
, *label
;
1121 const char *tab_title
= _("_Governor");
1123 page
= gtk_vbox_new(FALSE
, 0);
1125 label
= gtk_label_new_with_mnemonic(tab_title
);
1127 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog
->notebook
), page
, label
);
1129 pdialog
->cma_editor
= create_cma_dialog(pdialog
->pcity
);
1130 gtk_box_pack_start(GTK_BOX(page
), pdialog
->cma_editor
->shell
, TRUE
, TRUE
, 0);
1132 gtk_widget_show(page
);
1135 /****************************************************************
1136 **** Misc. Settings Page ****
1137 *****************************************************************/
1138 static void create_and_append_settings_page(struct city_dialog
*pdialog
)
1141 GtkWidget
*vbox
, *vbox2
, *page
, *frame
, *label
, *button
;
1144 const char *tab_title
= _("_Settings");
1146 static const char *new_citizens_label
[] = {
1152 static const char *disband_label
= N_("Disband if build settler at size 1");
1154 static const char *misc_whichtab_label
[NUM_PAGES
] = {
1155 N_("Overview page"),
1156 N_("Production page"),
1157 N_("Happiness page"),
1158 N_("Governor page"),
1159 N_("This Settings page"),
1160 N_("Last active page")
1163 static bool new_citizens_label_done
;
1164 static bool misc_whichtab_label_done
;
1166 /* initialize signal_blocker */
1167 pdialog
->misc
.block_signal
= 0;
1170 page
= gtk_table_new(2, 2, FALSE
);
1171 gtk_table_set_col_spacings(GTK_TABLE(page
), 18);
1172 gtk_container_set_border_width(GTK_CONTAINER(page
), 8);
1174 size
= gtk_size_group_new(GTK_SIZE_GROUP_BOTH
);
1176 label
= gtk_label_new_with_mnemonic(tab_title
);
1178 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog
->notebook
), page
, label
);
1180 vbox
= gtk_vbox_new(FALSE
, 12);
1181 gtk_table_attach(GTK_TABLE(page
), vbox
, 0, 1, 0, 1,
1182 GTK_FILL
| GTK_EXPAND
, GTK_FILL
, 0, 0);
1183 gtk_size_group_add_widget(size
, vbox
);
1185 /* new_citizens radio */
1186 frame
= gtk_frame_new(_("New citizens are"));
1187 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 0);
1189 vbox2
= gtk_vbox_new(TRUE
, 0);
1190 gtk_container_add(GTK_CONTAINER(frame
), vbox2
);
1192 intl_slist(ARRAY_SIZE(new_citizens_label
), new_citizens_label
,
1193 &new_citizens_label_done
);
1196 for (i
= 0; i
< ARRAY_SIZE(new_citizens_label
); i
++) {
1197 button
= gtk_radio_button_new_with_mnemonic(group
, new_citizens_label
[i
]);
1198 pdialog
->misc
.new_citizens_radio
[i
] = button
;
1199 gtk_container_add(GTK_CONTAINER(vbox2
), button
);
1200 g_signal_connect(button
, "toggled",
1201 G_CALLBACK(cityopt_callback
), pdialog
);
1202 group
= gtk_radio_button_get_group(GTK_RADIO_BUTTON(button
));
1205 /* next is the next-time-open radio group in the right column */
1206 frame
= gtk_frame_new(_("Next time open"));
1207 gtk_table_attach(GTK_TABLE(page
), frame
, 1, 2, 0, 1,
1208 GTK_FILL
| GTK_EXPAND
, GTK_FILL
, 0, 0);
1209 gtk_size_group_add_widget(size
, frame
);
1211 vbox2
= gtk_vbox_new(TRUE
, 0);
1212 gtk_container_add(GTK_CONTAINER(frame
), vbox2
);
1214 intl_slist(ARRAY_SIZE(misc_whichtab_label
), misc_whichtab_label
,
1215 &misc_whichtab_label_done
);
1218 for (i
= 0; i
< ARRAY_SIZE(misc_whichtab_label
); i
++) {
1219 button
= gtk_radio_button_new_with_mnemonic(group
, misc_whichtab_label
[i
]);
1220 pdialog
->misc
.whichtab_radio
[i
] = button
;
1221 gtk_container_add(GTK_CONTAINER(vbox2
), button
);
1222 g_signal_connect(button
, "toggled",
1223 G_CALLBACK(misc_whichtab_callback
), GINT_TO_POINTER(i
));
1224 group
= gtk_radio_button_get_group(GTK_RADIO_BUTTON(button
));
1227 /* now we go back and fill the hbox rename */
1228 frame
= gtk_frame_new(_("City"));
1229 gtk_table_attach(GTK_TABLE(page
), frame
, 0, 1, 1, 2,
1230 GTK_FILL
| GTK_EXPAND
, GTK_FILL
, 0, 12);
1232 vbox2
= gtk_vbox_new(TRUE
, 0);
1233 gtk_container_add(GTK_CONTAINER(frame
), vbox2
);
1235 button
= gtk_button_new_with_mnemonic(_("R_ename..."));
1236 pdialog
->misc
.rename_command
= button
;
1237 gtk_container_add(GTK_CONTAINER(vbox2
), button
);
1238 g_signal_connect(button
, "clicked",
1239 G_CALLBACK(rename_callback
), pdialog
);
1241 gtk_widget_set_sensitive(button
, can_client_issue_orders());
1243 /* the disband-if-size-1 button */
1244 button
= gtk_check_button_new_with_mnemonic(_(disband_label
));
1245 pdialog
->misc
.disband_on_settler
= button
;
1246 gtk_container_add(GTK_CONTAINER(vbox2
), button
);
1247 g_signal_connect(button
, "toggled",
1248 G_CALLBACK(cityopt_callback
), pdialog
);
1250 /* we choose which page to popup by default */
1251 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
1253 misc
.whichtab_radio
[new_dialog_def_page
]),
1256 set_cityopt_values(pdialog
);
1258 gtk_widget_show_all(page
);
1260 if (new_dialog_def_page
== (NUM_PAGES
- 1)) {
1261 gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog
->notebook
),
1264 gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog
->notebook
),
1265 new_dialog_def_page
);
1272 /****************************************************************
1273 **** Main City Dialog ****
1274 +----------------------------+-------------------------------+
1275 | GtkWidget *top: Citizens | city name |
1276 +----------------------------+-------------------------------+
1278 +------------------------------------------------------------+
1279 *****************************************************************/
1280 static struct city_dialog
*create_city_dialog(struct city
*pcity
)
1282 struct city_dialog
*pdialog
;
1284 GtkWidget
*close_command
;
1285 GtkWidget
*vbox
, *hbox
, *cbox
, *ebox
;
1287 if (!city_dialogs_have_been_initialised
) {
1288 initialize_city_dialogs();
1291 pdialog
= fc_malloc(sizeof(struct city_dialog
));
1292 pdialog
->pcity
= pcity
;
1293 pdialog
->buy_shell
= NULL
;
1294 pdialog
->sell_shell
= NULL
;
1295 pdialog
->rename_shell
= NULL
;
1296 pdialog
->happiness
.map_canvas
.sw
= NULL
; /* make sure NULL if spy */
1297 pdialog
->happiness
.map_canvas
.ebox
= NULL
; /* ditto */
1298 pdialog
->happiness
.map_canvas
.pixmap
= NULL
; /* ditto */
1299 pdialog
->happiness
.citizens
= NULL
; /* ditto */
1300 pdialog
->cma_editor
= NULL
;
1301 pdialog
->map_canvas_store
= gdk_pixbuf_new(GDK_COLORSPACE_RGB
, TRUE
, 8,
1302 CITYMAP_WIDTH
, CITYMAP_HEIGHT
);
1303 pdialog
->map_pixbuf_unscaled
= NULL
;
1304 pdialog
->map_canvas_store_unscaled
1305 = gdk_pixmap_new(root_window
, canvas_width
, canvas_height
, -1);
1307 pdialog
->shell
= gtk_dialog_new_with_buttons(city_name_get(pcity
),
1311 setup_dialog(pdialog
->shell
, toplevel
);
1312 gtk_window_set_role(GTK_WINDOW(pdialog
->shell
), "city");
1314 g_signal_connect(pdialog
->shell
, "destroy",
1315 G_CALLBACK(city_destroy_callback
), pdialog
);
1316 gtk_window_set_position(GTK_WINDOW(pdialog
->shell
), GTK_WIN_POS_MOUSE
);
1317 gtk_widget_set_name(pdialog
->shell
, "Freeciv");
1319 gtk_widget_realize(pdialog
->shell
);
1321 /* keep the icon of the executable on Windows (see PR#36491) */
1322 #ifndef WIN32_NATIVE
1323 gtk_window_set_icon(GTK_WINDOW(pdialog
->shell
),
1324 sprite_get_pixbuf(get_icon_sprite(tileset
, ICON_CITYDLG
)));
1325 #endif /* WIN32_NATIVE */
1327 /* Restore size of the city dialog. */
1328 gtk_window_set_default_size(GTK_WINDOW(pdialog
->shell
),
1329 gui_options
.gui_gtk2_citydlg_xsize
,
1330 gui_options
.gui_gtk2_citydlg_ysize
);
1332 pdialog
->popup_menu
= gtk_menu_new();
1334 vbox
= GTK_DIALOG(pdialog
->shell
)->vbox
;
1335 hbox
= gtk_hbox_new(TRUE
, 0);
1336 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
1338 /**** Citizens bar here ****/
1339 cbox
= gtk_hbox_new(FALSE
, 0);
1340 gtk_box_pack_start(GTK_BOX(hbox
), cbox
, TRUE
, TRUE
, 0);
1342 ebox
= gtk_event_box_new();
1343 gtk_widget_add_events(ebox
, GDK_BUTTON_PRESS_MASK
);
1344 gtk_box_pack_start(GTK_BOX(cbox
), ebox
, FALSE
, FALSE
, 0);
1345 pdialog
->citizen_pixmap
=
1346 gtk_pixcomm_new(tileset_small_sprite_width(tileset
)
1347 * NUM_CITIZENS_SHOWN
,
1348 tileset_small_sprite_height(tileset
));
1349 gtk_misc_set_padding(GTK_MISC(pdialog
->citizen_pixmap
), 2, 2);
1350 gtk_misc_set_alignment(GTK_MISC(pdialog
->citizen_pixmap
), 0.0f
, 0.5f
);
1351 gtk_container_add(GTK_CONTAINER(ebox
), pdialog
->citizen_pixmap
);
1352 g_signal_connect(GTK_OBJECT(ebox
), "button_press_event",
1353 GTK_SIGNAL_FUNC(citizens_callback
), pdialog
);
1355 /**** City name label here ****/
1356 pdialog
->name_label
= gtk_label_new(NULL
);
1357 gtk_misc_set_alignment(GTK_MISC(pdialog
->name_label
), 0.0f
, 0.5f
);
1358 gtk_box_pack_start(GTK_BOX(hbox
), pdialog
->name_label
, TRUE
, TRUE
, 0);
1360 /**** -Start of Notebook- ****/
1362 pdialog
->notebook
= gtk_notebook_new();
1363 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(pdialog
->notebook
),
1365 gtk_box_pack_start(GTK_BOX(vbox
), pdialog
->notebook
, TRUE
, TRUE
, 0);
1367 create_and_append_overview_page(pdialog
);
1368 create_and_append_worklist_page(pdialog
);
1370 /* only create these tabs if not a spy */
1371 if (!client_has_player() || city_owner(pcity
) == client_player()) {
1372 create_and_append_happiness_page(pdialog
);
1375 if (city_owner(pcity
) == client_player()
1376 && !client_is_observer()) {
1377 create_and_append_cma_page(pdialog
);
1378 create_and_append_settings_page(pdialog
);
1380 gtk_notebook_set_current_page(GTK_NOTEBOOK(pdialog
->notebook
),
1384 /**** End of Notebook ****/
1386 /* bottom buttons */
1388 pdialog
->show_units_command
=
1389 gtk_button_new_with_mnemonic(_("_List present units..."));
1390 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(pdialog
->shell
)->action_area
),
1391 pdialog
->show_units_command
);
1392 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(GTK_DIALOG(pdialog
->shell
)->action_area
),
1393 pdialog
->show_units_command
, TRUE
);
1394 g_signal_connect(pdialog
->show_units_command
,
1396 G_CALLBACK(show_units_callback
), pdialog
);
1398 pdialog
->prev_command
= gtk_stockbutton_new(GTK_STOCK_GO_BACK
,
1400 gtk_dialog_add_action_widget(GTK_DIALOG(pdialog
->shell
),
1401 pdialog
->prev_command
, 1);
1403 pdialog
->next_command
= gtk_stockbutton_new(GTK_STOCK_GO_FORWARD
,
1405 gtk_dialog_add_action_widget(GTK_DIALOG(pdialog
->shell
),
1406 pdialog
->next_command
, 2);
1408 if (city_owner(pcity
) != client
.conn
.playing
) {
1409 gtk_widget_set_sensitive(pdialog
->prev_command
, FALSE
);
1410 gtk_widget_set_sensitive(pdialog
->next_command
, FALSE
);
1413 close_command
= gtk_dialog_add_button(GTK_DIALOG(pdialog
->shell
),
1414 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
);
1416 gtk_dialog_set_default_response(GTK_DIALOG(pdialog
->shell
),
1417 GTK_RESPONSE_CLOSE
);
1419 g_signal_connect(close_command
, "clicked",
1420 G_CALLBACK(close_callback
), pdialog
);
1422 g_signal_connect(pdialog
->prev_command
, "clicked",
1423 G_CALLBACK(switch_city_callback
), pdialog
);
1425 g_signal_connect(pdialog
->next_command
, "clicked",
1426 G_CALLBACK(switch_city_callback
), pdialog
);
1428 /* some other things we gotta do */
1430 g_signal_connect(pdialog
->shell
, "key_press_event",
1431 G_CALLBACK(keyboard_handler
), pdialog
);
1433 dialog_list_prepend(dialog_list
, pdialog
);
1435 real_city_dialog_refresh(pdialog
->pcity
);
1437 /* need to do this every time a new dialog is opened. */
1438 city_dialog_update_prev_next();
1440 gtk_widget_show_all(GTK_DIALOG(pdialog
->shell
)->vbox
);
1441 gtk_widget_show_all(GTK_DIALOG(pdialog
->shell
)->action_area
);
1443 gtk_window_set_focus(GTK_WINDOW(pdialog
->shell
), close_command
);
1448 /*********** Functions to update parts of the dialog ************/
1449 /****************************************************************
1450 Update title of city dialog.
1451 *****************************************************************/
1452 static void city_dialog_update_title(struct city_dialog
*pdialog
)
1457 if (city_unhappy(pdialog
->pcity
)) {
1458 /* TRANS: city dialog title */
1459 buf
= g_strdup_printf(_("<b>%s</b> - %s citizens - DISORDER"),
1460 city_name_get(pdialog
->pcity
),
1461 population_to_text(city_population(pdialog
->pcity
)));
1462 } else if (city_celebrating(pdialog
->pcity
)) {
1463 /* TRANS: city dialog title */
1464 buf
= g_strdup_printf(_("<b>%s</b> - %s citizens - celebrating"),
1465 city_name_get(pdialog
->pcity
),
1466 population_to_text(city_population(pdialog
->pcity
)));
1467 } else if (city_happy(pdialog
->pcity
)) {
1468 /* TRANS: city dialog title */
1469 buf
= g_strdup_printf(_("<b>%s</b> - %s citizens - happy"),
1470 city_name_get(pdialog
->pcity
),
1471 population_to_text(city_population(pdialog
->pcity
)));
1473 /* TRANS: city dialog title */
1474 buf
= g_strdup_printf(_("<b>%s</b> - %s citizens"),
1475 city_name_get(pdialog
->pcity
),
1476 population_to_text(city_population(pdialog
->pcity
)));
1479 now
= gtk_label_get_text(GTK_LABEL(pdialog
->name_label
));
1480 if (strcmp(now
, buf
) != 0) {
1481 gtk_window_set_title(GTK_WINDOW(pdialog
->shell
), city_name_get(pdialog
->pcity
));
1482 gtk_label_set_markup(GTK_LABEL(pdialog
->name_label
), buf
);
1488 /****************************************************************
1489 Update citizens in city dialog
1490 *****************************************************************/
1491 static void city_dialog_update_citizens(struct city_dialog
*pdialog
)
1493 enum citizen_category categories
[MAX_CITY_SIZE
];
1495 struct city
*pcity
= pdialog
->pcity
;
1496 int num_citizens
= get_city_citizen_types(pcity
, FEELING_FINAL
, categories
);
1498 /* If there is not enough space we stack the icons. We draw from left to */
1499 /* right. width is how far we go to the right for each drawn pixmap. The */
1500 /* last icon is always drawn in full, and so has reserved */
1501 /* tileset_small_sprite_width(tileset) pixels. */
1503 if (num_citizens
> 1) {
1504 width
= MIN(tileset_small_sprite_width(tileset
),
1505 ((NUM_CITIZENS_SHOWN
- 1) * tileset_small_sprite_width(tileset
)) /
1506 (num_citizens
- 1));
1508 width
= tileset_small_sprite_width(tileset
);
1510 pdialog
->cwidth
= width
;
1513 gtk_pixcomm_freeze(GTK_PIXCOMM(pdialog
->citizen_pixmap
));
1514 gtk_pixcomm_clear(GTK_PIXCOMM(pdialog
->citizen_pixmap
));
1516 size
= (num_citizens
- 1) * width
+ tileset_small_sprite_width(tileset
) +
1517 2 * GTK_MISC(pdialog
->citizen_pixmap
)->xpad
;
1518 gtk_widget_set_size_request(GTK_WIDGET(pdialog
->citizen_pixmap
), size
, -1);
1520 for (i
= 0; i
< num_citizens
; i
++) {
1521 gtk_pixcomm_copyto(GTK_PIXCOMM(pdialog
->citizen_pixmap
),
1522 get_citizen_sprite(tileset
, categories
[i
], i
, pcity
),
1525 gtk_pixcomm_thaw(GTK_PIXCOMM(pdialog
->citizen_pixmap
));
1528 /****************************************************************
1529 Update textual info fields in city dialog
1530 *****************************************************************/
1531 static void city_dialog_update_information(GtkWidget
**info_ebox
,
1532 GtkWidget
**info_label
,
1533 struct city_dialog
*pdialog
)
1535 int i
, style
, illness
= 0;
1536 char buf
[NUM_INFO_FIELDS
][512];
1537 struct city
*pcity
= pdialog
->pcity
;
1540 enum { FOOD
, SHIELD
, TRADE
, GOLD
, LUXURY
, SCIENCE
,
1541 GRANARY
, GROWTH
, CORRUPTION
, WASTE
, CULTURE
,
1545 /* fill the buffers with the necessary info */
1546 fc_snprintf(buf
[FOOD
], sizeof(buf
[FOOD
]), "%3d (%+4d)",
1547 pcity
->prod
[O_FOOD
], pcity
->surplus
[O_FOOD
]);
1548 fc_snprintf(buf
[SHIELD
], sizeof(buf
[SHIELD
]), "%3d (%+4d)",
1549 pcity
->prod
[O_SHIELD
] + pcity
->waste
[O_SHIELD
],
1550 pcity
->surplus
[O_SHIELD
]);
1551 fc_snprintf(buf
[TRADE
], sizeof(buf
[TRADE
]), "%3d (%+4d)",
1552 pcity
->surplus
[O_TRADE
] + pcity
->waste
[O_TRADE
],
1553 pcity
->surplus
[O_TRADE
]);
1554 fc_snprintf(buf
[GOLD
], sizeof(buf
[GOLD
]), "%3d (%+4d)",
1555 pcity
->prod
[O_GOLD
], pcity
->surplus
[O_GOLD
]);
1556 fc_snprintf(buf
[LUXURY
], sizeof(buf
[LUXURY
]), "%3d",
1557 pcity
->prod
[O_LUXURY
]);
1558 fc_snprintf(buf
[SCIENCE
], sizeof(buf
[SCIENCE
]), "%3d",
1559 pcity
->prod
[O_SCIENCE
]);
1560 fc_snprintf(buf
[GRANARY
], sizeof(buf
[GRANARY
]), "%4d/%-4d",
1561 pcity
->food_stock
, city_granary_size(city_size_get(pcity
)));
1563 granaryturns
= city_turns_to_grow(pcity
);
1564 if (granaryturns
== 0) {
1565 /* TRANS: city growth is blocked. Keep short. */
1566 fc_snprintf(buf
[GROWTH
], sizeof(buf
[GROWTH
]), _("blocked"));
1567 } else if (granaryturns
== FC_INFINITY
) {
1568 /* TRANS: city is not growing. Keep short. */
1569 fc_snprintf(buf
[GROWTH
], sizeof(buf
[GROWTH
]), _("never"));
1571 /* A negative value means we'll have famine in that many turns.
1572 But that's handled down below. */
1573 /* TRANS: city growth turns. Keep short. */
1574 fc_snprintf(buf
[GROWTH
], sizeof(buf
[GROWTH
]),
1575 PL_("%d turn", "%d turns", abs(granaryturns
)),
1578 fc_snprintf(buf
[CORRUPTION
], sizeof(buf
[CORRUPTION
]), "%4d",
1579 pcity
->waste
[O_TRADE
]);
1580 fc_snprintf(buf
[WASTE
], sizeof(buf
[WASTE
]), "%4d",
1581 pcity
->waste
[O_SHIELD
]);
1582 fc_snprintf(buf
[CULTURE
], sizeof(buf
[CULTURE
]), "%4d",
1583 pcity
->client
.culture
);
1584 fc_snprintf(buf
[POLLUTION
], sizeof(buf
[POLLUTION
]), "%4d",
1586 if (!game
.info
.illness_on
) {
1587 fc_snprintf(buf
[ILLNESS
], sizeof(buf
[ILLNESS
]), " -.-");
1589 illness
= city_illness_calc(pcity
, NULL
, NULL
, NULL
, NULL
);
1590 /* illness is in tenth of percent */
1591 fc_snprintf(buf
[ILLNESS
], sizeof(buf
[ILLNESS
]), "%4.1f",
1592 (float)illness
/ 10.0);
1595 /* stick 'em in the labels */
1597 for (i
= 0; i
< NUM_INFO_FIELDS
; i
++) {
1598 gtk_label_set_text(GTK_LABEL(info_label
[i
]), buf
[i
]);
1602 * Special style stuff for granary, growth and pollution below. The
1603 * "4" below is arbitrary. 3 turns should be enough of a warning.
1605 style
= (granaryturns
> -4 && granaryturns
< 0) ? RED
: NORMAL
;
1606 gtk_widget_modify_style(info_label
[GRANARY
], info_label_style
[style
]);
1608 style
= (granaryturns
== 0 || pcity
->surplus
[O_FOOD
] < 0) ? RED
: NORMAL
;
1609 gtk_widget_modify_style(info_label
[GROWTH
], info_label_style
[style
]);
1611 /* someone could add the info_label_style[ORANGE]
1612 * style for better granularity here */
1614 style
= (pcity
->pollution
>= 10) ? RED
: NORMAL
;
1615 gtk_widget_modify_style(info_label
[POLLUTION
], info_label_style
[style
]);
1617 /* illness is in tenth of percent, i.e 100 != 10.0% */
1618 style
= (illness
>= 100) ? RED
: NORMAL
;
1619 gtk_widget_modify_style(info_label
[ILLNESS
], info_label_style
[style
]);
1622 /****************************************************************
1623 Update map display of city dialog
1624 *****************************************************************/
1625 static void city_dialog_update_map(struct city_dialog
*pdialog
)
1627 struct canvas store
= {
1628 .type
= CANVAS_PIXMAP
,
1629 .v
= {.pixmap
= pdialog
->map_canvas_store_unscaled
}
1632 /* The drawing is done in three steps.
1633 * 1. First we render to a pixmap with the appropriate canvas size.
1634 * 2. Then the pixmap is rendered into a pixbuf of equal size.
1635 * 3. Finally this pixbuf is composited and scaled onto the GtkImage's
1639 city_dialog_redraw_map(pdialog
->pcity
, &store
);
1640 pdialog
->map_pixbuf_unscaled
1641 = gdk_pixbuf_get_from_drawable(pdialog
->map_pixbuf_unscaled
,
1642 pdialog
->map_canvas_store_unscaled
,
1645 canvas_width
, canvas_height
);
1646 gdk_pixbuf_composite(pdialog
->map_pixbuf_unscaled
,
1647 pdialog
->map_canvas_store
,
1648 0, 0, CITYMAP_WIDTH
, CITYMAP_HEIGHT
,
1650 (double)CITYMAP_WIDTH
/ (double)canvas_width
,
1651 (double)CITYMAP_HEIGHT
/ (double)canvas_height
,
1652 GDK_INTERP_BILINEAR
, 255);
1654 /* draw to real window */
1655 draw_map_canvas(pdialog
);
1657 if (cma_is_city_under_agent(pdialog
->pcity
, NULL
)) {
1658 gtk_widget_set_sensitive(pdialog
->overview
.map_canvas
.ebox
, FALSE
);
1659 if (pdialog
->happiness
.map_canvas
.ebox
) {
1660 gtk_widget_set_sensitive(pdialog
->happiness
.map_canvas
.ebox
, FALSE
);
1663 gtk_widget_set_sensitive(pdialog
->overview
.map_canvas
.ebox
, TRUE
);
1664 if (pdialog
->happiness
.map_canvas
.ebox
) {
1665 gtk_widget_set_sensitive(pdialog
->happiness
.map_canvas
.ebox
, TRUE
);
1670 /****************************************************************
1671 Update what city is building and buy cost in city dialog
1672 *****************************************************************/
1673 static void city_dialog_update_building(struct city_dialog
*pdialog
)
1675 char buf
[32], buf2
[200];
1678 GtkListStore
* store
;
1680 struct universal targets
[MAX_NUM_PRODUCTION_TARGETS
];
1681 struct item items
[MAX_NUM_PRODUCTION_TARGETS
];
1682 int targets_used
, item
;
1683 struct city
*pcity
= pdialog
->pcity
;
1684 gboolean sensitive
= city_can_buy(pcity
);
1685 const char *descr
= city_production_name_translation(pcity
);
1686 int cost
= city_production_build_shield_cost(pcity
);
1688 gtk_widget_set_sensitive(pdialog
->overview
.buy_command
, sensitive
);
1689 gtk_widget_set_sensitive(pdialog
->production
.buy_command
, sensitive
);
1691 /* Make sure build slots info is up to date */
1693 int build_slots
= city_build_slots(pcity
);
1694 /* Only display extra info if more than one slot is available */
1695 if (build_slots
> 1) {
1696 fc_snprintf(buf2
, sizeof(buf2
),
1697 /* TRANS: never actually used with built_slots<=1 */
1698 PL_("Production (up to %d unit per turn):",
1699 "Production (up to %d units per turn):", build_slots
),
1702 GTK_LABEL(pdialog
->production
.production_label
), buf2
);
1705 GTK_LABEL(pdialog
->production
.production_label
), _("Production:"));
1709 /* Update what the city is working on */
1710 get_city_dialog_production(pcity
, buf
, sizeof(buf
));
1713 pct
= (gdouble
) pcity
->shield_stock
/ (gdouble
) cost
;
1714 pct
= CLAMP(pct
, 0.0, 1.0);
1719 fc_snprintf(buf2
, sizeof(buf2
), "%s%s\n%s", descr
,
1720 worklist_is_empty(&pcity
->worklist
) ? "" : " (+)", buf
);
1721 gtk_progress_bar_set_text(
1722 GTK_PROGRESS_BAR(pdialog
->overview
.production_bar
), buf2
);
1723 gtk_progress_bar_set_fraction(
1724 GTK_PROGRESS_BAR(pdialog
->overview
.production_bar
), pct
);
1726 fc_snprintf(buf2
, sizeof(buf2
), "%s%s: %s", descr
,
1727 worklist_is_empty(&pcity
->worklist
) ? "" : " (+)", buf
);
1728 gtk_progress_bar_set_text(
1729 GTK_PROGRESS_BAR(pdialog
->production
.production_bar
), buf2
);
1730 gtk_progress_bar_set_fraction(
1731 GTK_PROGRESS_BAR(pdialog
->production
.production_bar
), pct
);
1733 store
= pdialog
->overview
.change_production_store
;
1734 gtk_combo_box_set_active(GTK_COMBO_BOX(pdialog
->overview
.production_combo
),
1736 gtk_list_store_clear(store
);
1739 = collect_eventually_buildable_targets(targets
, pdialog
->pcity
, FALSE
);
1740 name_and_sort_items(targets
, targets_used
, items
, FALSE
, pcity
);
1742 for (item
= 0; item
< targets_used
; item
++) {
1743 if (can_city_build_now(pcity
, &items
[item
].item
)) {
1745 struct sprite
* sprite
;
1746 struct universal target
= items
[item
].item
;
1749 if (VUT_UTYPE
== target
.kind
) {
1750 name
= utype_name_translation(target
.value
.utype
);
1751 sprite
= get_unittype_sprite(tileset
, target
.value
.utype
,
1752 direction8_invalid(), TRUE
);
1755 name
= improvement_name_translation(target
.value
.building
);
1756 sprite
= get_building_sprite(tileset
, target
.value
.building
);
1757 useless
= is_improvement_redundant(pcity
, target
.value
.building
);
1759 gtk_list_store_append(store
, &iter
);
1760 gtk_list_store_set(store
, &iter
, 0, sprite_get_pixbuf(sprite
),
1761 1, name
, 3, useless
,
1762 2, (gint
)cid_encode(items
[item
].item
),-1);
1766 /* work around GTK+ refresh bug. */
1767 gtk_widget_queue_resize(pdialog
->overview
.production_bar
);
1768 gtk_widget_queue_resize(pdialog
->production
.production_bar
);
1771 /****************************************************************
1772 Update list of improvements in city dialog
1773 *****************************************************************/
1774 static void city_dialog_update_improvement_list(struct city_dialog
*pdialog
)
1776 int total
, item
, targets_used
;
1777 struct universal targets
[MAX_NUM_PRODUCTION_TARGETS
];
1778 struct item items
[MAX_NUM_PRODUCTION_TARGETS
];
1779 GtkTreeModel
*model
;
1780 GtkListStore
*store
;
1783 gtk_tree_view_get_model(GTK_TREE_VIEW(pdialog
->overview
.improvement_list
));
1784 store
= GTK_LIST_STORE(model
);
1786 targets_used
= collect_already_built_targets(targets
, pdialog
->pcity
);
1787 name_and_sort_items(targets
, targets_used
, items
, FALSE
, pdialog
->pcity
);
1789 gtk_list_store_clear(store
);
1792 for (item
= 0; item
< targets_used
; item
++) {
1795 struct sprite
*sprite
;
1796 struct universal target
= items
[item
].item
;
1798 fc_assert_action(VUT_IMPROVEMENT
== target
.kind
, continue);
1799 /* This takes effects (like Adam Smith's) into account. */
1800 upkeep
= city_improvement_upkeep(pdialog
->pcity
, target
.value
.building
);
1801 sprite
= get_building_sprite(tileset
, target
.value
.building
);
1803 gtk_list_store_append(store
, &it
);
1804 gtk_list_store_set(store
, &it
,
1805 0, target
.value
.building
,
1806 1, sprite_get_pixbuf(sprite
),
1807 2, items
[item
].descr
,
1809 4, is_improvement_redundant(pdialog
->pcity
, target
.value
.building
),
1816 /****************************************************************
1817 Update list of supported units in city dialog
1818 *****************************************************************/
1819 static void city_dialog_update_supported_units(struct city_dialog
*pdialog
)
1821 struct unit_list
*units
;
1822 struct unit_node_vector
*nodes
;
1825 int free_unhappy
= get_city_bonus(pdialog
->pcity
, EFT_MAKE_CONTENT_MIL
);
1827 if (NULL
!= client
.conn
.playing
1828 && city_owner(pdialog
->pcity
) != client
.conn
.playing
) {
1829 units
= pdialog
->pcity
->client
.info_units_supported
;
1831 units
= pdialog
->pcity
->units_supported
;
1834 nodes
= &pdialog
->overview
.supported_units
;
1836 n
= unit_list_size(units
);
1837 m
= unit_node_vector_size(nodes
);
1839 gtk_table_resize(GTK_TABLE(pdialog
->overview
.supported_unit_table
),
1844 unit_node_vector_iterate(nodes
, elt
) {
1846 gtk_widget_destroy(elt
->cmd
);
1848 } unit_node_vector_iterate_end
;
1850 unit_node_vector_reserve(nodes
, n
);
1852 for (i
= m
; i
< n
; i
++) {
1853 GtkWidget
*cmd
, *pix
;
1854 struct unit_node node
;
1855 int unit_height
= tileset_unit_with_upkeep_height(tileset
);
1857 cmd
= gtk_button_new();
1860 gtk_button_set_relief(GTK_BUTTON(cmd
), GTK_RELIEF_NONE
);
1861 gtk_widget_add_events(cmd
,
1862 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
);
1864 pix
= gtk_pixcomm_new(tileset_full_tile_width(tileset
), unit_height
);
1867 gtk_container_add(GTK_CONTAINER(cmd
), pix
);
1869 gtk_table_attach_defaults(
1870 GTK_TABLE(pdialog
->overview
.supported_unit_table
),
1872 unit_node_vector_append(nodes
, node
);
1877 unit_list_iterate(units
, punit
) {
1878 struct unit_node
*pnode
;
1879 int happy_cost
= city_unit_unhappiness(punit
, &free_unhappy
);
1881 pnode
= unit_node_vector_get(nodes
, i
);
1883 GtkWidget
*cmd
, *pix
;
1888 gtk_pixcomm_freeze(GTK_PIXCOMM(pix
));
1889 put_unit_gpixmap(punit
, GTK_PIXCOMM(pix
));
1890 put_unit_gpixmap_city_overlays(punit
, GTK_PIXCOMM(pix
), punit
->upkeep
,
1892 gtk_pixcomm_thaw(GTK_PIXCOMM(pix
));
1894 g_signal_handlers_disconnect_matched(cmd
,
1895 G_SIGNAL_MATCH_FUNC
,
1896 0, 0, NULL
, supported_unit_callback
, NULL
);
1898 g_signal_handlers_disconnect_matched(cmd
,
1899 G_SIGNAL_MATCH_FUNC
,
1900 0, 0, NULL
, supported_unit_middle_callback
, NULL
);
1902 gtk_widget_set_tooltip_text(cmd
, unit_description(punit
));
1904 g_signal_connect(cmd
, "button_press_event",
1905 G_CALLBACK(supported_unit_callback
),
1906 GINT_TO_POINTER(punit
->id
));
1908 g_signal_connect(cmd
, "button_release_event",
1909 G_CALLBACK(supported_unit_middle_callback
),
1910 GINT_TO_POINTER(punit
->id
));
1912 if (city_owner(pdialog
->pcity
) != client
.conn
.playing
) {
1913 gtk_widget_set_sensitive(cmd
, FALSE
);
1915 gtk_widget_set_sensitive(cmd
, TRUE
);
1918 gtk_widget_show(pix
);
1919 gtk_widget_show(cmd
);
1922 } unit_list_iterate_end
;
1924 buf
= g_strdup_printf(_("Supported units %d"), n
);
1925 gtk_frame_set_label(GTK_FRAME(pdialog
->overview
.supported_units_frame
), buf
);
1929 /****************************************************************
1930 Update list of present units in city dialog
1931 *****************************************************************/
1932 static void city_dialog_update_present_units(struct city_dialog
*pdialog
)
1934 struct unit_list
*units
;
1935 struct unit_node_vector
*nodes
;
1939 if (NULL
!= client
.conn
.playing
1940 && city_owner(pdialog
->pcity
) != client
.conn
.playing
) {
1941 units
= pdialog
->pcity
->client
.info_units_present
;
1943 units
= pdialog
->pcity
->tile
->units
;
1946 nodes
= &pdialog
->overview
.present_units
;
1948 n
= unit_list_size(units
);
1949 m
= unit_node_vector_size(nodes
);
1951 gtk_table_resize(GTK_TABLE(pdialog
->overview
.present_unit_table
),
1956 unit_node_vector_iterate(nodes
, elt
) {
1958 gtk_widget_destroy(elt
->cmd
);
1960 } unit_node_vector_iterate_end
;
1962 unit_node_vector_reserve(nodes
, n
);
1964 for (i
= m
; i
< n
; i
++) {
1965 GtkWidget
*cmd
, *pix
;
1966 struct unit_node node
;
1968 cmd
= gtk_button_new();
1971 gtk_button_set_relief(GTK_BUTTON(cmd
), GTK_RELIEF_NONE
);
1972 gtk_widget_add_events(cmd
,
1973 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
);
1975 pix
= gtk_pixcomm_new(tileset_full_tile_width(tileset
),
1976 tileset_full_tile_height(tileset
));
1979 gtk_container_add(GTK_CONTAINER(cmd
), pix
);
1981 gtk_table_attach_defaults(
1982 GTK_TABLE(pdialog
->overview
.present_unit_table
),
1984 unit_node_vector_append(nodes
, node
);
1989 unit_list_iterate(units
, punit
) {
1990 struct unit_node
*pnode
;
1992 pnode
= unit_node_vector_get(nodes
, i
);
1994 GtkWidget
*cmd
, *pix
;
1999 put_unit_gpixmap(punit
, GTK_PIXCOMM(pix
));
2001 g_signal_handlers_disconnect_matched(cmd
,
2002 G_SIGNAL_MATCH_FUNC
,
2003 0, 0, NULL
, present_unit_callback
, NULL
);
2005 g_signal_handlers_disconnect_matched(cmd
,
2006 G_SIGNAL_MATCH_FUNC
,
2007 0, 0, NULL
, present_unit_middle_callback
, NULL
);
2009 gtk_widget_set_tooltip_text(cmd
, unit_description(punit
));
2011 g_signal_connect(cmd
, "button_press_event",
2012 G_CALLBACK(present_unit_callback
),
2013 GINT_TO_POINTER(punit
->id
));
2015 g_signal_connect(cmd
, "button_release_event",
2016 G_CALLBACK(present_unit_middle_callback
),
2017 GINT_TO_POINTER(punit
->id
));
2019 if (city_owner(pdialog
->pcity
) != client
.conn
.playing
) {
2020 gtk_widget_set_sensitive(cmd
, FALSE
);
2022 gtk_widget_set_sensitive(cmd
, TRUE
);
2025 gtk_widget_show(pix
);
2026 gtk_widget_show(cmd
);
2029 } unit_list_iterate_end
;
2031 buf
= g_strdup_printf(_("Present units %d"), n
);
2032 gtk_frame_set_label(GTK_FRAME(pdialog
->overview
.present_units_frame
), buf
);
2036 /****************************************************************
2037 Updates the sensitivity of the the prev and next buttons.
2038 this does not need pdialog as a parameter, since it iterates
2039 over all the open dialogs.
2040 note: we still need the sensitivity code in create_city_dialog()
2041 for the spied dialogs.
2042 *****************************************************************/
2043 static void city_dialog_update_prev_next(void)
2048 if (client_is_global_observer()) {
2049 return; /* Keep them insensitive as initially set */
2052 city_number
= city_list_size(client
.conn
.playing
->cities
);
2054 /* the first time, we see if all the city dialogs are open */
2055 dialog_list_iterate(dialog_list
, pdialog
) {
2056 if (city_owner(pdialog
->pcity
) == client
.conn
.playing
) {
2059 } dialog_list_iterate_end
;
2061 if (count
== city_number
) { /* all are open, shouldn't prev/next */
2062 dialog_list_iterate(dialog_list
, pdialog
) {
2063 gtk_widget_set_sensitive(pdialog
->prev_command
, FALSE
);
2064 gtk_widget_set_sensitive(pdialog
->next_command
, FALSE
);
2065 } dialog_list_iterate_end
;
2067 dialog_list_iterate(dialog_list
, pdialog
) {
2068 if (city_owner(pdialog
->pcity
) == client
.conn
.playing
) {
2069 gtk_widget_set_sensitive(pdialog
->prev_command
, TRUE
);
2070 gtk_widget_set_sensitive(pdialog
->next_command
, TRUE
);
2072 } dialog_list_iterate_end
;
2076 /****************************************************************
2077 User has clicked show units
2078 *****************************************************************/
2079 static void show_units_callback(GtkWidget
* w
, gpointer data
)
2081 struct city_dialog
*pdialog
= (struct city_dialog
*) data
;
2082 struct tile
*ptile
= pdialog
->pcity
->tile
;
2084 if (unit_list_size(ptile
->units
))
2085 unit_select_dialog_popup(ptile
);
2088 /****************************************************************
2089 Set city menu position
2090 *****************************************************************/
2091 static void city_menu_position(GtkMenu
*menu
, gint
*x
, gint
*y
,
2092 gboolean
*push_in
, gpointer data
)
2095 GtkRequisition requisition
;
2099 fc_assert_ret(GTK_IS_BUTTON(data
));
2101 widget
= GTK_WIDGET(data
);
2103 gtk_widget_get_child_requisition(GTK_WIDGET(menu
), &requisition
);
2105 gdk_window_get_origin(widget
->window
, &xpos
, &ypos
);
2107 xpos
+= widget
->allocation
.x
;
2108 ypos
+= widget
->allocation
.y
;
2115 /****************************************************************
2116 Destroy widget -callback
2117 *****************************************************************/
2118 static void destroy_func(GtkWidget
*w
, gpointer data
)
2120 gtk_widget_destroy(w
);
2123 /****************************************************************
2124 Pop-up menu to change attributes of supported units
2125 *****************************************************************/
2126 static gboolean
supported_unit_callback(GtkWidget
* w
, GdkEventButton
* ev
,
2129 GtkWidget
*menu
, *item
;
2130 struct city_dialog
*pdialog
;
2132 struct unit
*punit
=
2133 player_unit_by_number(client_player(), (size_t) data
);
2136 && NULL
!= (pcity
= game_city_by_number(punit
->homecity
))
2137 && NULL
!= (pdialog
= get_city_dialog(pcity
))) {
2139 if (ev
->type
!= GDK_BUTTON_PRESS
|| ev
->button
== 2 || ev
->button
== 3
2140 || !can_client_issue_orders()) {
2144 menu
= pdialog
->popup_menu
;
2146 gtk_menu_popdown(GTK_MENU(menu
));
2147 gtk_container_foreach(GTK_CONTAINER(menu
), destroy_func
, NULL
);
2149 item
= gtk_menu_item_new_with_mnemonic(_("Cen_ter"));
2150 g_signal_connect(item
, "activate",
2151 G_CALLBACK(unit_center_callback
),
2152 GINT_TO_POINTER(punit
->id
));
2153 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
2155 item
= gtk_menu_item_new_with_mnemonic(_("_Activate unit"));
2156 g_signal_connect(item
, "activate",
2157 G_CALLBACK(unit_activate_callback
),
2158 GINT_TO_POINTER(punit
->id
));
2159 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
2161 item
= gtk_menu_item_new_with_mnemonic(_("Activate unit, _close dialog"));
2162 g_signal_connect(item
, "activate",
2163 G_CALLBACK(supported_unit_activate_close_callback
),
2164 GINT_TO_POINTER(punit
->id
));
2165 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
2167 item
= gtk_menu_item_new_with_mnemonic(_("_Disband unit"));
2168 g_signal_connect(item
, "activate",
2169 G_CALLBACK(unit_disband_callback
),
2170 GINT_TO_POINTER(punit
->id
));
2171 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
2173 if (unit_has_type_flag(punit
, UTYF_UNDISBANDABLE
)) {
2174 gtk_widget_set_sensitive(item
, FALSE
);
2177 gtk_widget_show_all(menu
);
2179 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
,
2180 city_menu_position
, w
, ev
->button
, ev
->time
);
2187 /****************************************************************
2188 Pop-up menu to change attributes of units, ex. change homecity.
2189 *****************************************************************/
2190 static gboolean
present_unit_callback(GtkWidget
* w
, GdkEventButton
* ev
,
2193 GtkWidget
*menu
, *item
;
2194 struct city_dialog
*pdialog
;
2196 struct unit
*punit
=
2197 player_unit_by_number(client_player(), (size_t) data
);
2200 && NULL
!= (pcity
= tile_city(unit_tile(punit
)))
2201 && NULL
!= (pdialog
= get_city_dialog(pcity
))) {
2203 if (ev
->type
!= GDK_BUTTON_PRESS
|| ev
->button
== 2 || ev
->button
== 3
2204 || !can_client_issue_orders()) {
2208 menu
= pdialog
->popup_menu
;
2210 gtk_menu_popdown(GTK_MENU(menu
));
2211 gtk_container_foreach(GTK_CONTAINER(menu
), destroy_func
, NULL
);
2213 item
= gtk_menu_item_new_with_mnemonic(_("_Activate unit"));
2214 g_signal_connect(item
, "activate",
2215 G_CALLBACK(unit_activate_callback
),
2216 GINT_TO_POINTER(punit
->id
));
2217 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
2219 item
= gtk_menu_item_new_with_mnemonic(_("Activate unit, _close dialog"));
2220 g_signal_connect(item
, "activate",
2221 G_CALLBACK(present_unit_activate_close_callback
),
2222 GINT_TO_POINTER(punit
->id
));
2223 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
2225 item
= gtk_menu_item_new_with_mnemonic(_("_Load unit"));
2226 g_signal_connect(item
, "activate",
2227 G_CALLBACK(unit_load_callback
),
2228 GINT_TO_POINTER(punit
->id
));
2229 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
2231 if (!unit_can_load(punit
)) {
2232 gtk_widget_set_sensitive(item
, FALSE
);
2235 item
= gtk_menu_item_new_with_mnemonic(_("_Unload unit"));
2236 g_signal_connect(item
, "activate",
2237 G_CALLBACK(unit_unload_callback
),
2238 GINT_TO_POINTER(punit
->id
));
2239 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
2241 if (!can_unit_unload(punit
, unit_transport_get(punit
))
2242 || !can_unit_exist_at_tile(punit
, unit_tile(punit
))) {
2243 gtk_widget_set_sensitive(item
, FALSE
);
2246 item
= gtk_menu_item_new_with_mnemonic(_("_Sentry unit"));
2247 g_signal_connect(item
, "activate",
2248 G_CALLBACK(unit_sentry_callback
),
2249 GINT_TO_POINTER(punit
->id
));
2250 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
2252 if (punit
->activity
== ACTIVITY_SENTRY
2253 || !can_unit_do_activity(punit
, ACTIVITY_SENTRY
)) {
2254 gtk_widget_set_sensitive(item
, FALSE
);
2257 item
= gtk_menu_item_new_with_mnemonic(_("_Fortify unit"));
2258 g_signal_connect(item
, "activate",
2259 G_CALLBACK(unit_fortify_callback
),
2260 GINT_TO_POINTER(punit
->id
));
2261 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
2263 if (punit
->activity
== ACTIVITY_FORTIFYING
2264 || !can_unit_do_activity(punit
, ACTIVITY_FORTIFYING
)) {
2265 gtk_widget_set_sensitive(item
, FALSE
);
2268 item
= gtk_menu_item_new_with_mnemonic(_("_Disband unit"));
2269 g_signal_connect(item
, "activate",
2270 G_CALLBACK(unit_disband_callback
),
2271 GINT_TO_POINTER(punit
->id
));
2272 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
2274 if (unit_has_type_flag(punit
, UTYF_UNDISBANDABLE
)) {
2275 gtk_widget_set_sensitive(item
, FALSE
);
2278 item
= gtk_menu_item_new_with_mnemonic(_("Set _Home City"));
2279 g_signal_connect(item
, "activate",
2280 G_CALLBACK(unit_homecity_callback
),
2281 GINT_TO_POINTER(punit
->id
));
2282 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
2283 gtk_widget_set_sensitive(item
, can_unit_change_homecity_to(punit
, pcity
));
2285 item
= gtk_menu_item_new_with_mnemonic(_("U_pgrade unit"));
2286 g_signal_connect(item
, "activate",
2287 G_CALLBACK(unit_upgrade_callback
),
2288 GINT_TO_POINTER(punit
->id
));
2289 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
2291 if (!can_client_issue_orders()
2292 || NULL
== can_upgrade_unittype(client
.conn
.playing
,
2293 unit_type_get(punit
))) {
2294 gtk_widget_set_sensitive(item
, FALSE
);
2297 gtk_widget_show_all(menu
);
2299 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
,
2300 city_menu_position
, w
, ev
->button
, ev
->time
);
2305 /****************************************************************
2306 if user middle-clicked on a unit, activate it and close dialog
2307 *****************************************************************/
2308 static gboolean
present_unit_middle_callback(GtkWidget
* w
,
2309 GdkEventButton
* ev
,
2312 struct city_dialog
*pdialog
;
2314 struct unit
*punit
=
2315 player_unit_by_number(client_player(), (size_t) data
);
2318 && NULL
!= (pcity
= tile_city(unit_tile(punit
)))
2319 && NULL
!= (pdialog
= get_city_dialog(pcity
))
2320 && can_client_issue_orders()) {
2322 if (ev
->button
== 3) {
2323 unit_focus_set(punit
);
2324 } else if (ev
->button
== 2) {
2325 unit_focus_set(punit
);
2326 close_city_dialog(pdialog
);
2333 /****************************************************************
2334 if user middle-clicked on a unit, activate it and close dialog
2335 *****************************************************************/
2336 static gboolean
supported_unit_middle_callback(GtkWidget
* w
,
2337 GdkEventButton
* ev
,
2340 struct city_dialog
*pdialog
;
2342 struct unit
*punit
=
2343 player_unit_by_number(client_player(), (size_t) data
);
2346 && NULL
!= (pcity
= game_city_by_number(punit
->homecity
))
2347 && NULL
!= (pdialog
= get_city_dialog(pcity
))
2348 && can_client_issue_orders()) {
2350 if (ev
->button
== 3) {
2351 unit_focus_set(punit
);
2352 } else if (ev
->button
== 2) {
2353 unit_focus_set(punit
);
2354 close_city_dialog(pdialog
);
2361 /****************************************************************
2362 User has requested centering to unit
2363 *****************************************************************/
2364 static void unit_center_callback(GtkWidget
* w
, gpointer data
)
2366 struct unit
*punit
=
2367 player_unit_by_number(client_player(), (size_t)data
);
2369 if (NULL
!= punit
) {
2370 center_tile_mapcanvas(unit_tile(punit
));
2374 /****************************************************************
2375 User has requested unit activation
2376 *****************************************************************/
2377 static void unit_activate_callback(GtkWidget
* w
, gpointer data
)
2379 struct unit
*punit
=
2380 player_unit_by_number(client_player(), (size_t)data
);
2382 if (NULL
!= punit
) {
2383 unit_focus_set(punit
);
2387 /****************************************************************
2388 User has requested some supported unit to be activated and
2389 city dialog to be closed
2390 *****************************************************************/
2391 static void supported_unit_activate_close_callback(GtkWidget
* w
,
2394 struct unit
*punit
=
2395 player_unit_by_number(client_player(), (size_t)data
);
2397 if (NULL
!= punit
) {
2398 struct city
*pcity
=
2399 player_city_by_number(client_player(), punit
->homecity
);
2401 unit_focus_set(punit
);
2402 if (NULL
!= pcity
) {
2403 struct city_dialog
*pdialog
= get_city_dialog(pcity
);
2405 if (NULL
!= pdialog
) {
2406 close_city_dialog(pdialog
);
2412 /****************************************************************
2413 User has requested some present unit to be activated and
2414 city dialog to be closed
2415 *****************************************************************/
2416 static void present_unit_activate_close_callback(GtkWidget
* w
,
2419 struct unit
*punit
=
2420 player_unit_by_number(client_player(), (size_t)data
);
2422 if (NULL
!= punit
) {
2423 struct city
*pcity
= tile_city(unit_tile(punit
));
2425 unit_focus_set(punit
);
2426 if (NULL
!= pcity
) {
2427 struct city_dialog
*pdialog
= get_city_dialog(pcity
);
2429 if (NULL
!= pdialog
) {
2430 close_city_dialog(pdialog
);
2436 /****************************************************************
2437 User has requested unit to be loaded to transport
2438 *****************************************************************/
2439 static void unit_load_callback(GtkWidget
* w
, gpointer data
)
2441 struct unit
*punit
=
2442 player_unit_by_number(client_player(), (size_t)data
);
2444 if (NULL
!= punit
) {
2445 request_unit_load(punit
, NULL
, unit_tile(punit
));
2449 /****************************************************************
2450 User has requested unit to be unloaded from transport
2451 *****************************************************************/
2452 static void unit_unload_callback(GtkWidget
* w
, gpointer data
)
2454 struct unit
*punit
=
2455 player_unit_by_number(client_player(), (size_t)data
);
2457 if (NULL
!= punit
) {
2458 request_unit_unload(punit
);
2462 /****************************************************************
2463 User has requested unit to be sentried
2464 *****************************************************************/
2465 static void unit_sentry_callback(GtkWidget
* w
, gpointer data
)
2467 struct unit
*punit
=
2468 player_unit_by_number(client_player(), (size_t)data
);
2470 if (NULL
!= punit
) {
2471 request_unit_sentry(punit
);
2475 /****************************************************************
2476 User has requested unit to be fortified
2477 *****************************************************************/
2478 static void unit_fortify_callback(GtkWidget
* w
, gpointer data
)
2480 struct unit
*punit
=
2481 player_unit_by_number(client_player(), (size_t)data
);
2483 if (NULL
!= punit
) {
2484 request_unit_fortify(punit
);
2488 /****************************************************************
2489 User has requested unit to be disbanded
2490 *****************************************************************/
2491 static void unit_disband_callback(GtkWidget
* w
, gpointer data
)
2493 struct unit_list
*punits
;
2494 struct unit
*punit
=
2495 player_unit_by_number(client_player(), (size_t)data
);
2497 if (NULL
== punit
) {
2501 punits
= unit_list_new();
2502 unit_list_append(punits
, punit
);
2503 popup_disband_dialog(punits
);
2504 unit_list_destroy(punits
);
2507 /****************************************************************
2508 User has requested unit to change homecity to city where it
2510 *****************************************************************/
2511 static void unit_homecity_callback(GtkWidget
* w
, gpointer data
)
2513 struct unit
*punit
=
2514 player_unit_by_number(client_player(), (size_t)data
);
2516 if (NULL
!= punit
) {
2517 request_unit_change_homecity(punit
);
2521 /****************************************************************
2522 User has requested unit to be upgraded
2523 *****************************************************************/
2524 static void unit_upgrade_callback(GtkWidget
*w
, gpointer data
)
2526 struct unit_list
*punits
;
2527 struct unit
*punit
=
2528 player_unit_by_number(client_player(), (size_t)data
);
2530 if (NULL
== punit
) {
2534 punits
= unit_list_new();
2535 unit_list_append(punits
, punit
);
2536 popup_upgrade_dialog(punits
);
2537 unit_list_destroy(punits
);
2540 /*** Callbacks for citizen bar, map funcs that are not update ***/
2541 /****************************************************************
2542 Somebody clicked our list of citizens. If they clicked a specialist
2543 then change the type of him, else do nothing.
2544 *****************************************************************/
2545 static gboolean
citizens_callback(GtkWidget
* w
, GdkEventButton
* ev
,
2548 struct city_dialog
*pdialog
= data
;
2549 struct city
*pcity
= pdialog
->pcity
;
2550 int citnum
, tlen
, len
;
2552 if (!can_client_issue_orders()) {
2556 tlen
= tileset_small_sprite_width(tileset
);
2557 len
= (city_size_get(pcity
) - 1) * pdialog
->cwidth
+ tlen
;
2559 return FALSE
; /* no citizen that far to the right */
2561 citnum
= MIN(city_size_get(pcity
) - 1, ev
->x
/ pdialog
->cwidth
);
2563 city_rotate_specialist(pcity
, citnum
);
2568 /**************************************************************************
2569 User has pressed button on citymap
2570 **************************************************************************/
2571 static gboolean
button_down_citymap(GtkWidget
* w
, GdkEventButton
* ev
,
2574 struct city_dialog
*pdialog
= data
;
2575 int canvas_x
, canvas_y
, city_x
, city_y
;
2577 if (!can_client_issue_orders()) {
2581 canvas_x
= ev
->x
* (double)canvas_width
/ (double)CITYMAP_WIDTH
;
2582 canvas_y
= ev
->y
* (double)canvas_height
/ (double)CITYMAP_HEIGHT
;
2584 if (canvas_to_city_pos(&city_x
, &city_y
,
2585 city_map_radius_sq_get(pdialog
->pcity
),
2586 canvas_x
, canvas_y
)) {
2587 city_toggle_worker(pdialog
->pcity
, city_x
, city_y
);
2593 /****************************************************************
2594 Set map canvas to be drawn
2595 *****************************************************************/
2596 static void draw_map_canvas(struct city_dialog
*pdialog
)
2598 gtk_widget_queue_draw(pdialog
->overview
.map_canvas
.pixmap
);
2599 if (pdialog
->happiness
.map_canvas
.pixmap
) { /* in case of spy */
2600 gtk_widget_queue_draw(pdialog
->happiness
.map_canvas
.pixmap
);
2604 /********* Callbacks for Buy, Change, Sell, Worklist ************/
2605 /****************************************************************
2606 User has answered buy cost dialog
2607 *****************************************************************/
2608 static void buy_callback_response(GtkWidget
*w
, gint response
, gpointer data
)
2610 struct city_dialog
*pdialog
= data
;
2612 if (response
== GTK_RESPONSE_YES
) {
2613 city_buy_production(pdialog
->pcity
);
2615 gtk_widget_destroy(w
);
2618 /****************************************************************
2619 User has clicked buy-button
2620 *****************************************************************/
2621 static void buy_callback(GtkWidget
*w
, gpointer data
)
2624 struct city_dialog
*pdialog
= data
;
2625 const char *name
= city_production_name_translation(pdialog
->pcity
);
2626 int value
= city_production_buy_gold_cost(pdialog
->pcity
);
2629 if (!can_client_issue_orders()) {
2633 fc_snprintf(buf
, ARRAY_SIZE(buf
), PL_("Treasury contains %d gold.",
2634 "Treasury contains %d gold.",
2635 client_player()->economic
.gold
),
2636 client_player()->economic
.gold
);
2638 if (value
<= client_player()->economic
.gold
) {
2639 shell
= gtk_message_dialog_new(NULL
,
2640 GTK_DIALOG_DESTROY_WITH_PARENT
,
2641 GTK_MESSAGE_QUESTION
, GTK_BUTTONS_YES_NO
,
2642 /* TRANS: Last %s is pre-pluralised "Treasury contains %d gold." */
2643 PL_("Buy %s for %d gold?\n%s",
2644 "Buy %s for %d gold?\n%s", value
),
2646 setup_dialog(shell
, pdialog
->shell
);
2647 gtk_window_set_title(GTK_WINDOW(shell
), _("Buy It!"));
2648 gtk_dialog_set_default_response(GTK_DIALOG(shell
), GTK_RESPONSE_NO
);
2649 g_signal_connect(shell
, "response", G_CALLBACK(buy_callback_response
),
2651 gtk_window_present(GTK_WINDOW(shell
));
2653 shell
= gtk_message_dialog_new(NULL
,
2654 GTK_DIALOG_DESTROY_WITH_PARENT
,
2655 GTK_MESSAGE_INFO
, GTK_BUTTONS_CLOSE
,
2656 /* TRANS: Last %s is pre-pluralised "Treasury contains %d gold." */
2657 PL_("%s costs %d gold.\n%s",
2658 "%s costs %d gold.\n%s", value
),
2660 setup_dialog(shell
, pdialog
->shell
);
2661 gtk_window_set_title(GTK_WINDOW(shell
), _("Buy It!"));
2662 g_signal_connect(shell
, "response", G_CALLBACK(gtk_widget_destroy
),
2664 gtk_window_present(GTK_WINDOW(shell
));
2668 /****************************************************************************
2669 Callback for the dropdown production menu.
2670 ****************************************************************************/
2671 static void change_production_callback(GtkComboBox
*combo
,
2672 struct city_dialog
*pdialog
)
2676 if (can_client_issue_orders()
2677 && gtk_combo_box_get_active_iter(combo
, &iter
)) {
2679 struct universal univ
;
2681 gtk_tree_model_get(gtk_combo_box_get_model(combo
), &iter
, 2, &id
, -1);
2682 univ
= cid_production(id
);
2683 city_change_production(pdialog
->pcity
, &univ
);
2687 /****************************************************************
2688 User has clicked sell-button
2689 *****************************************************************/
2690 static void sell_callback(struct impr_type
*pimprove
, gpointer data
)
2693 struct city_dialog
*pdialog
= (struct city_dialog
*) data
;
2694 pdialog
->sell_id
= improvement_number(pimprove
);
2697 if (!can_client_issue_orders()) {
2701 if (test_player_sell_building_now(client
.conn
.playing
, pdialog
->pcity
,
2702 pimprove
) != TR_SUCCESS
) {
2706 price
= impr_sell_gold(pimprove
);
2707 shl
= gtk_message_dialog_new(NULL
,
2708 GTK_DIALOG_DESTROY_WITH_PARENT
,
2709 GTK_MESSAGE_QUESTION
,
2711 PL_("Sell %s for %d gold?",
2712 "Sell %s for %d gold?", price
),
2713 city_improvement_name_translation(pdialog
->pcity
, pimprove
), price
);
2714 setup_dialog(shl
, pdialog
->shell
);
2715 pdialog
->sell_shell
= shl
;
2717 gtk_window_set_title(GTK_WINDOW(shl
), _("Sell It!"));
2718 gtk_window_set_position(GTK_WINDOW(shl
), GTK_WIN_POS_CENTER_ON_PARENT
);
2720 g_signal_connect(shl
, "response",
2721 G_CALLBACK(sell_callback_response
), pdialog
);
2723 gtk_window_present(GTK_WINDOW(shl
));
2726 /****************************************************************
2727 User has responded to sell price dialog
2728 *****************************************************************/
2729 static void sell_callback_response(GtkWidget
*w
, gint response
, gpointer data
)
2731 struct city_dialog
*pdialog
= data
;
2733 if (response
== GTK_RESPONSE_YES
) {
2734 city_sell_improvement(pdialog
->pcity
, pdialog
->sell_id
);
2736 gtk_widget_destroy(w
);
2738 pdialog
->sell_shell
= NULL
;
2741 /****************************************************************
2742 this is here because it's closely related to the sell stuff
2743 *****************************************************************/
2744 static void impr_callback(GtkTreeView
*view
, GtkTreePath
*path
,
2745 GtkTreeViewColumn
*col
, gpointer data
)
2747 GtkTreeModel
*model
;
2749 GdkModifierType mask
;
2750 struct impr_type
*pimprove
;
2752 model
= gtk_tree_view_get_model(view
);
2754 if (!gtk_tree_model_get_iter(model
, &it
, path
)) {
2758 gtk_tree_model_get(model
, &it
, 0, &pimprove
, -1);
2759 gdk_window_get_pointer(NULL
, NULL
, NULL
, &mask
);
2761 if (!(mask
& GDK_CONTROL_MASK
)) {
2762 sell_callback(pimprove
, data
);
2764 if (is_great_wonder(pimprove
)) {
2765 popup_help_dialog_typed(improvement_name_translation(pimprove
), HELP_WONDER
);
2767 popup_help_dialog_typed(improvement_name_translation(pimprove
), HELP_IMPROVEMENT
);
2772 /******* Callbacks for stuff on the Misc. Settings page *********/
2773 /****************************************************************
2774 Called when Rename button pressed
2775 *****************************************************************/
2776 static void rename_callback(GtkWidget
*w
, gpointer data
)
2778 struct city_dialog
*pdialog
;
2780 pdialog
= (struct city_dialog
*) data
;
2782 pdialog
->rename_shell
= input_dialog_create(GTK_WINDOW(pdialog
->shell
),
2783 /* "shellrenamecity" */
2785 _("What should we rename the city to?"),
2786 city_name_get(pdialog
->pcity
),
2787 rename_popup_callback
, pdialog
);
2790 /****************************************************************
2791 Called when user has finished with "Rename City" popup
2792 *****************************************************************/
2793 static void rename_popup_callback(gpointer data
, gint response
,
2796 struct city_dialog
*pdialog
= data
;
2799 if (response
== GTK_RESPONSE_OK
) {
2800 city_rename(pdialog
->pcity
, input
);
2801 } /* else CANCEL or DELETE_EVENT */
2803 pdialog
->rename_shell
= NULL
;
2807 /****************************************************************
2808 Sets which page will be set on reopen of dialog
2809 *****************************************************************/
2810 static void misc_whichtab_callback(GtkWidget
* w
, gpointer data
)
2812 new_dialog_def_page
= GPOINTER_TO_INT(data
);
2815 /**************************************************************************
2816 City options callbacks
2817 **************************************************************************/
2818 static void cityopt_callback(GtkWidget
* w
, gpointer data
)
2820 struct city_dialog
*pdialog
= (struct city_dialog
*) data
;
2822 if (!can_client_issue_orders()) {
2826 if(!pdialog
->misc
.block_signal
){
2827 struct city
*pcity
= pdialog
->pcity
;
2828 bv_city_options new_options
;
2830 fc_assert(CITYO_LAST
== 3);
2832 BV_CLR_ALL(new_options
);
2833 if (GTK_TOGGLE_BUTTON(pdialog
->misc
.disband_on_settler
)->active
) {
2834 BV_SET(new_options
, CITYO_DISBAND
);
2836 if (GTK_TOGGLE_BUTTON(pdialog
->misc
.new_citizens_radio
[1])->active
) {
2837 BV_SET(new_options
, CITYO_NEW_EINSTEIN
);
2839 if (GTK_TOGGLE_BUTTON(pdialog
->misc
.new_citizens_radio
[2])->active
) {
2840 BV_SET(new_options
, CITYO_NEW_TAXMAN
);
2843 dsend_packet_city_options_req(&client
.conn
, pcity
->id
,new_options
);
2847 /**************************************************************************
2848 refresh the city options (auto_[land, air, sea, helicopter] and
2849 disband-is-size-1) in the misc page.
2850 **************************************************************************/
2851 static void set_cityopt_values(struct city_dialog
*pdialog
)
2853 struct city
*pcity
= pdialog
->pcity
;
2855 pdialog
->misc
.block_signal
= 1;
2857 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pdialog
->misc
.disband_on_settler
),
2858 is_city_option_set(pcity
, CITYO_DISBAND
));
2860 if (is_city_option_set(pcity
, CITYO_NEW_EINSTEIN
)) {
2861 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
2862 (pdialog
->misc
.new_citizens_radio
[1]), TRUE
);
2863 } else if (is_city_option_set(pcity
, CITYO_NEW_TAXMAN
)) {
2864 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
2865 (pdialog
->misc
.new_citizens_radio
[2]), TRUE
);
2867 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
2868 (pdialog
->misc
.new_citizens_radio
[0]), TRUE
);
2870 pdialog
->misc
.block_signal
= 0;
2873 /*************** Callbacks for: Close, Prev, Next. **************/
2874 /****************************************************************
2875 User has clicked rename city-button
2876 *****************************************************************/
2877 static void close_callback(GtkWidget
*w
, gpointer data
)
2879 close_city_dialog((struct city_dialog
*) data
);
2882 /****************************************************************
2883 User has closed rename city dialog
2884 *****************************************************************/
2885 static void city_destroy_callback(GtkWidget
*w
, gpointer data
)
2887 struct city_dialog
*pdialog
;
2889 pdialog
= (struct city_dialog
*) data
;
2891 gtk_widget_hide(pdialog
->shell
);
2893 if (game
.info
.citizen_nationality
) {
2894 citizens_dialog_close(pdialog
->pcity
);
2896 close_happiness_dialog(pdialog
->pcity
);
2897 close_cma_dialog(pdialog
->pcity
);
2899 /* Save size of the city dialog. */
2900 gui_options
.gui_gtk2_citydlg_xsize
2901 = CLIP(GUI_GTK2_CITYDLG_MIN_XSIZE
,
2902 pdialog
->shell
->allocation
.width
,
2903 GUI_GTK2_CITYDLG_MAX_XSIZE
);
2904 gui_options
.gui_gtk2_citydlg_ysize
2905 = CLIP(GUI_GTK2_CITYDLG_MIN_XSIZE
,
2906 pdialog
->shell
->allocation
.height
,
2907 GUI_GTK2_CITYDLG_MAX_XSIZE
);
2910 = gtk_notebook_get_current_page(GTK_NOTEBOOK(pdialog
->notebook
));
2912 if (pdialog
->popup_menu
) {
2913 gtk_widget_destroy(pdialog
->popup_menu
);
2916 dialog_list_remove(dialog_list
, pdialog
);
2918 unit_node_vector_free(&pdialog
->overview
.supported_units
);
2919 unit_node_vector_free(&pdialog
->overview
.present_units
);
2921 if (pdialog
->buy_shell
) {
2922 gtk_widget_destroy(pdialog
->buy_shell
);
2924 if (pdialog
->sell_shell
) {
2925 gtk_widget_destroy(pdialog
->sell_shell
);
2927 if (pdialog
->rename_shell
) {
2928 gtk_widget_destroy(pdialog
->rename_shell
);
2931 g_object_unref(pdialog
->map_canvas_store
);
2932 if (pdialog
->map_pixbuf_unscaled
) {
2933 g_object_unref(pdialog
->map_pixbuf_unscaled
);
2938 /* need to do this every time a new dialog is closed. */
2939 city_dialog_update_prev_next();
2942 /************************************************************************
2944 *************************************************************************/
2945 static void close_city_dialog(struct city_dialog
*pdialog
)
2947 gtk_widget_destroy(pdialog
->shell
);
2950 /************************************************************************
2951 Callback for the prev/next buttons. Switches to the previous/next
2953 *************************************************************************/
2954 static void switch_city_callback(GtkWidget
*w
, gpointer data
)
2956 struct city_dialog
*pdialog
= (struct city_dialog
*) data
;
2957 int i
, j
, dir
, size
;
2958 struct city
*new_pcity
= NULL
;
2960 if (client_is_global_observer()) {
2964 size
= city_list_size(client
.conn
.playing
->cities
);
2966 fc_assert_ret(city_dialogs_have_been_initialised
);
2967 fc_assert_ret(size
>= 1);
2968 fc_assert_ret(city_owner(pdialog
->pcity
) == client
.conn
.playing
);
2974 /* dir = 1 will advance to the city, dir = -1 will get previous */
2975 if (w
== pdialog
->next_command
) {
2977 } else if (w
== pdialog
->prev_command
) {
2981 fc_assert_ret(w
== pdialog
->next_command
2982 || w
== pdialog
->prev_command
);
2986 for (i
= 0; i
< size
; i
++) {
2987 if (pdialog
->pcity
== city_list_get(client
.conn
.playing
->cities
, i
)) {
2992 fc_assert_ret(i
< size
);
2994 for (j
= 1; j
< size
; j
++) {
2995 struct city
*other_pcity
= city_list_get(client
.conn
.playing
->cities
,
2996 (i
+ dir
* j
+ size
) % size
);
2997 struct city_dialog
*other_pdialog
= get_city_dialog(other_pcity
);
2999 fc_assert_ret(other_pdialog
!= pdialog
);
3000 if (!other_pdialog
) {
3001 new_pcity
= other_pcity
;
3007 /* Every other city has an open city dialog. */
3011 /* cleanup happiness dialog */
3012 if (game
.info
.citizen_nationality
) {
3013 citizens_dialog_close(pdialog
->pcity
);
3015 close_happiness_dialog(pdialog
->pcity
);
3017 pdialog
->pcity
= new_pcity
;
3019 /* reinitialize happiness, and cma dialogs */
3020 if (game
.info
.citizen_nationality
) {
3021 gtk_box_pack_start(GTK_BOX(pdialog
->happiness
.citizens
),
3022 citizens_dialog_display(pdialog
->pcity
),
3025 gtk_box_pack_start(GTK_BOX(pdialog
->happiness
.widget
),
3026 get_top_happiness_display(pdialog
->pcity
), TRUE
, TRUE
, 0);
3027 if (!client_is_observer()) {
3028 fc_assert(pdialog
->cma_editor
!= NULL
);
3029 pdialog
->cma_editor
->pcity
= new_pcity
;
3032 reset_city_worklist(pdialog
->production
.worklist
, pdialog
->pcity
);
3035 center_tile_mapcanvas(pdialog
->pcity
->tile
);
3037 if (!client_is_observer()) {
3038 set_cityopt_values(pdialog
); /* need not be in real_city_dialog_refresh */
3041 real_city_dialog_refresh(pdialog
->pcity
);
3043 /* recenter the city map(s) */
3044 city_dialog_map_recenter(pdialog
->overview
.map_canvas
.sw
);
3045 if (pdialog
->happiness
.map_canvas
.sw
) {
3046 city_dialog_map_recenter(pdialog
->happiness
.map_canvas
.sw
);