Do not return NULL as boolean from wonder_is_lost() nor wonder_is_built()
[freeciv.git] / client / gui-gtk-2.0 / unitselect.c
blobcdeb48d221278dab65948e7ccfe5bbe0ba5fb49c
1 /*****************************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 *****************************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <gtk/gtk.h>
20 /* utility */
21 #include "fcintl.h"
23 /* common */
24 #include "fc_types.h"
25 #include "game.h"
26 #include "player.h"
27 #include "unit.h"
28 #include "unitlist.h"
29 #include "unittype.h"
31 /* client */
32 #include "client_main.h"
33 #include "control.h"
34 #include "goto.h"
35 #include "tilespec.h"
36 #include "unitselect_common.h"
38 /* client/gui-gtk-2.0 */
39 #include "graphics.h"
40 #include "gui_stuff.h"
41 #include "gui_main.h"
43 #include "unitselect.h"
45 /* Activate this to get more columns (see below) */
46 #undef DEBUG_USDLG
48 enum usdlg_column_types {
49 COL_PIXBUF,
50 COL_TEXT,
51 COL_INT
54 enum usdlg_row_types {
55 ROW_UNITTYPE,
56 ROW_ACTIVITY,
57 ROW_UNIT,
58 ROW_UNIT_TRANSPORTED
61 /* Basic data (Unit, description, count) */
62 #define USDLG_COLUMNS_DEFAULT 3
63 /* Additional data; shown if DEBUG_USDLG */
64 #define USDLG_COL_UTID USDLG_COLUMNS_DEFAULT + 0 /* Unit type ID */
65 #define USDLG_COL_UID USDLG_COLUMNS_DEFAULT + 1 /* Unit ID */
66 #define USDLG_COL_LOCATION USDLG_COLUMNS_DEFAULT + 2 /* Unit location */
67 #define USDLG_COL_ACTIVITY USDLG_COLUMNS_DEFAULT + 3 /* Unit activity */
68 #define USDLG_COL_ROW_TYPE USDLG_COLUMNS_DEFAULT + 4 /* Row type */
69 #define USDLG_COLUMNS_DEBUG USDLG_COLUMNS_DEFAULT + 5
70 /* Layout options; never shown */
71 #define USDLG_COL_STYLE USDLG_COLUMNS_DEBUG + 0
72 #define USDLG_COL_WEIGHT USDLG_COLUMNS_DEBUG + 1
73 #define USDLG_COLUMNS_ALL USDLG_COLUMNS_DEBUG + 2
75 #ifdef DEBUG_USDLG
76 #define USDLG_COLUMNS_SHOW USDLG_COLUMNS_DEBUG
77 #else
78 #define USDLG_COLUMNS_SHOW USDLG_COLUMNS_DEFAULT
79 #endif /* DEBUG */
81 enum usdlg_column_types usdlg_col_types[USDLG_COLUMNS_ALL] = {
82 COL_PIXBUF, /* Unit */
83 COL_TEXT, /* Description */
84 COL_INT, /* Count */
85 COL_INT, /* Debug: unit type */
86 COL_INT, /* Debug: unit ID */
87 COL_INT, /* Debug: location */
88 COL_INT, /* Debug: activity */
89 COL_INT, /* Debug: row type */
90 COL_INT, /* Layout: style */
91 COL_INT /* Layout: width */
94 static const char *usdlg_col_titles[USDLG_COLUMNS_ALL] = {
95 N_("Unit"),
96 N_("Description"),
97 N_("Count"),
98 "[Unittype]", /* Only for debug, no translation! */
99 "[Unit ID]",
100 "[Location]",
101 "[Activity]",
102 "[Row type]",
103 "[Style]",
104 "[Width]"
107 enum usdlg_cmd {
108 USDLG_CMD_SELECT,
109 USDLG_CMD_DESELECT,
110 USDLG_CMD_READY,
111 USDLG_CMD_SENTRY,
112 USDLG_CMD_CENTER,
113 USDLG_CMD_FOCUS,
114 USDLG_CMD_LAST
117 struct unit_select_dialog {
118 struct tile *ptile;
119 int unit_id_focus;
121 GtkWidget *shell;
122 GtkWidget *notebook;
124 struct {
125 GtkTreeStore *store;
126 GtkWidget *view;
127 GtkTreePath *path;
128 } units;
130 struct {
131 GtkTreeStore *store;
132 GtkWidget *page;
133 GtkWidget *view;
134 GtkTreePath *path;
136 GtkWidget *cmd[USDLG_CMD_LAST];
137 } tabs[SELLOC_COUNT];
140 /* The unit selection dialog; should only be used in usdlg_get(). */
141 static struct unit_select_dialog *unit_select_dlg = NULL;
143 static struct unit_select_dialog *usdlg_get(bool create);
144 static struct unit_select_dialog *usdlg_create(void);
145 static void usdlg_destroy(void);
146 static void usdlg_destroy_callback(GtkObject *object, gpointer data);
147 static void usdlg_tile(struct unit_select_dialog *pdialog,
148 struct tile *ptile);
149 static void usdlg_refresh(struct unit_select_dialog *pdialog);
151 static void usdlg_tab_select(struct unit_select_dialog *pdialog,
152 const char *title,
153 enum unit_select_location_mode loc);
154 static GtkTreeStore *usdlg_tab_store_new(void);
155 static bool usdlg_tab_update(struct unit_select_dialog *pdialog,
156 struct usdata_hash *ushash,
157 enum unit_select_location_mode loc);
158 static void usdlg_tab_append_utype(GtkTreeStore *store,
159 enum unit_select_location_mode loc,
160 struct unit_type *putype,
161 GtkTreeIter *it);
162 static void usdlg_tab_append_activity(GtkTreeStore *store,
163 enum unit_select_location_mode loc,
164 const struct unit_type *putype,
165 enum unit_activity act,
166 int count, GtkTreeIter *it,
167 GtkTreeIter *parent);
168 static void usdlg_tab_append_units(struct unit_select_dialog *pdialog,
169 enum unit_select_location_mode loc,
170 enum unit_activity act,
171 const struct unit *punit,
172 bool transported, GtkTreeIter *it,
173 GtkTreeIter *parent);
175 static void usdlg_cmd_ready(GtkObject *object, gpointer data);
176 static void usdlg_cmd_sentry(GtkObject *object, gpointer data);
177 static void usdlg_cmd_select(GtkObject *object, gpointer data);
178 static void usdlg_cmd_deselect(GtkObject *object, gpointer data);
179 static void usdlg_cmd_exec(GtkObject *object, gpointer mode_data,
180 enum usdlg_cmd cmd);
181 static void usdlg_cmd_exec_unit(struct unit *punit, enum usdlg_cmd cmd);
182 static void usdlg_cmd_center(GtkObject *object, gpointer data);
183 static void usdlg_cmd_focus(GtkObject *object, gpointer data);
184 static void usdlg_cmd_focus_real(GtkTreeView *view);
185 static void usdlg_cmd_row_activated(GtkTreeView *view, GtkTreePath *path,
186 GtkTreeViewColumn *col, gpointer data);
187 static void usdlg_cmd_cursor_changed(GtkTreeView *view, gpointer data);
190 /*****************************************************************************
191 Popup the unit selection dialog.
192 *****************************************************************************/
193 void unit_select_dialog_popup_main(struct tile *ptile, bool create)
195 struct unit_select_dialog *pdialog;
197 /* Create the dialog if it is requested. */
198 pdialog = usdlg_get(create);
200 /* Present the unit selection dialog if it exists. */
201 if (pdialog) {
202 /* Show all. */
203 gtk_widget_show_all(GTK_WIDGET(pdialog->shell));
204 /* Update tile. */
205 usdlg_tile(pdialog, ptile);
206 /* Refresh data and hide unused tabs. */
207 usdlg_refresh(pdialog);
211 /*****************************************************************************
212 Popdown the unit selection dialog.
213 *****************************************************************************/
214 void unit_select_dialog_popdown(void)
216 usdlg_destroy();
219 /*****************************************************************************
220 Get the current unit selection dialog. Create it if needed and 'create' is
221 TRUE.
222 *****************************************************************************/
223 static struct unit_select_dialog *usdlg_get(bool create)
225 if (unit_select_dlg) {
226 /* Return existing dialog. */
227 return unit_select_dlg;
228 } else if (create) {
229 /* Create new dialog. */
230 unit_select_dlg = usdlg_create();
231 return unit_select_dlg;
232 } else {
233 /* Nothing. */
234 return NULL;
238 /*****************************************************************************
239 Create a new unit selection dialog.
240 *****************************************************************************/
241 static struct unit_select_dialog *usdlg_create(void)
243 GtkWidget *hbox, *vbox;
244 GtkWidget *close_cmd;
245 struct unit_select_dialog *pdialog;
247 /* Create a container for the dialog. */
248 pdialog = fc_calloc(1, sizeof(*pdialog));
250 /* No tile defined. */
251 pdialog->ptile = NULL;
253 /* Create the dialog. */
254 pdialog->shell = gtk_dialog_new_with_buttons(_("Unit selection"), NULL, 0,
255 NULL);
256 setup_dialog(pdialog->shell, toplevel);
257 g_signal_connect(pdialog->shell, "destroy",
258 G_CALLBACK(usdlg_destroy_callback), pdialog);
259 gtk_window_set_position(GTK_WINDOW(pdialog->shell), GTK_WIN_POS_MOUSE);
260 gtk_widget_realize(pdialog->shell);
262 vbox = GTK_DIALOG(pdialog->shell)->vbox;
263 hbox = gtk_hbox_new(TRUE, 0);
264 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
266 /* Notebook. */
267 pdialog->notebook = gtk_notebook_new();
268 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(pdialog->notebook),
269 GTK_POS_BOTTOM);
270 gtk_box_pack_start(GTK_BOX(vbox), pdialog->notebook, TRUE, TRUE, 0);
272 /* Append pages. */
273 usdlg_tab_select(pdialog, _("_Units"), SELLOC_UNITS);
274 usdlg_tab_select(pdialog, _("_Tile"), SELLOC_TILE);
275 usdlg_tab_select(pdialog, _("C_ontinent"), SELLOC_CONT);
276 usdlg_tab_select(pdialog, _("_Land"), SELLOC_LAND);
277 usdlg_tab_select(pdialog, _("_Sea"), SELLOC_SEA);
278 usdlg_tab_select(pdialog, _("_Both"), SELLOC_BOTH);
279 usdlg_tab_select(pdialog, _("_World"), SELLOC_WORLD);
281 /* Buttons. */
282 close_cmd = gtk_dialog_add_button(GTK_DIALOG(pdialog->shell),
283 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
284 gtk_dialog_set_default_response(GTK_DIALOG(pdialog->shell),
285 GTK_RESPONSE_CLOSE);
286 g_signal_connect(close_cmd, "clicked",
287 G_CALLBACK(usdlg_destroy_callback), pdialog);
289 return pdialog;
292 /*****************************************************************************
293 Destroy a unit selection dialog.
294 *****************************************************************************/
295 static void usdlg_destroy(void)
297 if (unit_select_dlg) {
298 gtk_widget_destroy(GTK_WIDGET(unit_select_dlg->shell));
299 free(unit_select_dlg);
301 unit_select_dlg = NULL;
304 /*****************************************************************************
305 Callback for the destruction of the dialog.
306 *****************************************************************************/
307 static void usdlg_destroy_callback(GtkObject *object, gpointer data)
309 usdlg_destroy();
312 /*****************************************************************************
313 Set the reference tile.
314 *****************************************************************************/
315 static void usdlg_tile(struct unit_select_dialog *pdialog,
316 struct tile *ptile)
318 if (!pdialog) {
319 return;
322 /* Check for a valid tile. */
323 if (ptile != NULL) {
324 pdialog->ptile = ptile;
325 } else if (pdialog->ptile == NULL) {
326 struct unit *punit = head_of_units_in_focus();
328 if (punit) {
329 pdialog->ptile = unit_tile(punit);
330 center_tile_mapcanvas(pdialog->ptile);
331 } else {
332 pdialog->ptile = get_center_tile_mapcanvas();
337 /*****************************************************************************
338 Refresh the dialog.
339 *****************************************************************************/
340 static void usdlg_refresh(struct unit_select_dialog *pdialog)
342 struct usdata_hash *ushash = NULL;
343 enum unit_select_location_mode loc;
345 if (!pdialog) {
346 return;
349 /* Sort units into the hash. */
350 ushash = usdlg_data_new(pdialog->ptile);
351 /* Update all tabs. */
352 for (loc = unit_select_location_mode_begin();
353 loc != unit_select_location_mode_end();
354 loc = unit_select_location_mode_next(loc)) {
355 bool show = usdlg_tab_update(pdialog, ushash, loc);
357 if (!show) {
358 gtk_widget_hide(pdialog->tabs[loc].page);
359 } else {
360 gtk_widget_show(pdialog->tabs[loc].page);
362 if (pdialog->tabs[loc].path) {
363 gtk_tree_view_expand_row(GTK_TREE_VIEW(pdialog->tabs[loc].view),
364 pdialog->tabs[loc].path,FALSE);
365 gtk_tree_view_set_cursor(GTK_TREE_VIEW(pdialog->tabs[loc].view),
366 pdialog->tabs[loc].path, NULL, FALSE);
367 gtk_tree_path_free(pdialog->tabs[loc].path);
368 pdialog->tabs[loc].path = NULL;
372 /* Destroy the hash. */
373 usdlg_data_destroy(ushash);
376 /*****************************************************************************
377 +--------------------------------+
378 | +-----------------+----------+ |
379 | | (unit list) | select | |
380 | | | deselect | |
381 | | | | |
382 | | | center | |
383 | | | focus | |
384 | +-----------------+----------+ |
385 | | tabs | ... | |
386 | close |
387 +--------------------------------+
388 *****************************************************************************/
389 static void usdlg_tab_select(struct unit_select_dialog *pdialog,
390 const char *title,
391 enum unit_select_location_mode loc)
393 GtkWidget *page, *label, *hbox, *vbox, *bbox, *view, *sw;
394 GtkTreeStore *store;
395 static bool titles_done;
396 int i;
398 page = gtk_vbox_new(FALSE, 0);
399 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
400 pdialog->tabs[loc].page = page;
402 label = gtk_label_new_with_mnemonic(title);
403 gtk_notebook_append_page(GTK_NOTEBOOK(pdialog->notebook), page, label);
405 hbox = gtk_hbox_new(FALSE, 0);
406 gtk_box_pack_start(GTK_BOX(page), hbox, TRUE, TRUE, 0);
408 store = usdlg_tab_store_new();
409 pdialog->tabs[loc].store = store;
411 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
412 pdialog->tabs[loc].view = view;
413 g_object_unref(store);
415 g_signal_connect(view, "row-activated", G_CALLBACK(usdlg_cmd_row_activated),
416 (gpointer *)loc);
417 g_signal_connect(view, "cursor-changed",
418 G_CALLBACK(usdlg_cmd_cursor_changed), (gpointer *)loc);
420 /* Translate titles. */
421 intl_slist(ARRAY_SIZE(usdlg_col_titles), usdlg_col_titles, &titles_done);
423 for (i = 0; i < USDLG_COLUMNS_SHOW; i++) {
424 GtkTreeViewColumn *column = NULL;
425 GtkCellRenderer *renderer = NULL;
427 switch (usdlg_col_types[i]) {
428 case COL_PIXBUF:
429 renderer = gtk_cell_renderer_pixbuf_new();
430 column = gtk_tree_view_column_new_with_attributes(
431 usdlg_col_titles[i], renderer, "pixbuf", i, NULL);
432 gtk_tree_view_column_set_expand(column, FALSE);
433 break;
434 case COL_TEXT:
435 renderer = gtk_cell_renderer_text_new();
436 column = gtk_tree_view_column_new_with_attributes(
437 usdlg_col_titles[i], renderer, "text", i,
438 "style", USDLG_COL_STYLE, "weight", USDLG_COL_WEIGHT, NULL);
439 gtk_tree_view_column_set_expand(column, TRUE);
440 break;
441 case COL_INT:
442 renderer = gtk_cell_renderer_text_new();
443 column = gtk_tree_view_column_new_with_attributes(
444 usdlg_col_titles[i], renderer, "text", i,
445 "style", USDLG_COL_STYLE, "weight", USDLG_COL_WEIGHT, NULL);
446 g_object_set(renderer, "xalign", 1.0, NULL);
447 gtk_tree_view_column_set_alignment(column, 1.0);
448 gtk_tree_view_column_set_expand(column, FALSE);
449 break;
452 fc_assert_ret(column != NULL);
453 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
456 sw = gtk_scrolled_window_new(NULL, NULL);
457 gtk_widget_set_size_request(sw, -1, 300);
458 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
459 GTK_SHADOW_ETCHED_IN);
460 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
461 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
462 gtk_container_add(GTK_CONTAINER(sw), view);
463 gtk_box_pack_start(GTK_BOX(hbox), sw, TRUE, TRUE, 0);
465 vbox = gtk_vbox_new(FALSE, 10);
466 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, TRUE, 0);
468 /* button box 1: ready, sentry */
469 bbox = gtk_vbox_new(FALSE, 0);
470 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
472 pdialog->tabs[loc].cmd[USDLG_CMD_READY]
473 = gtk_button_new_with_mnemonic(_("Ready"));
474 gtk_box_pack_start(GTK_BOX(bbox), pdialog->tabs[loc].cmd[USDLG_CMD_READY],
475 FALSE, TRUE, 0);
476 g_signal_connect(pdialog->tabs[loc].cmd[USDLG_CMD_READY], "clicked",
477 G_CALLBACK(usdlg_cmd_ready), (gpointer *)loc);
478 gtk_widget_set_sensitive(
479 GTK_WIDGET(pdialog->tabs[loc].cmd[USDLG_CMD_READY]), FALSE);
481 pdialog->tabs[loc].cmd[USDLG_CMD_SENTRY]
482 = gtk_button_new_with_mnemonic(_("Sentry"));
483 gtk_box_pack_start(GTK_BOX(bbox), pdialog->tabs[loc].cmd[USDLG_CMD_SENTRY],
484 FALSE, TRUE, 0);
485 g_signal_connect(pdialog->tabs[loc].cmd[USDLG_CMD_SENTRY], "clicked",
486 G_CALLBACK(usdlg_cmd_sentry), (gpointer *)loc);
487 gtk_widget_set_sensitive(
488 GTK_WIDGET(pdialog->tabs[loc].cmd[USDLG_CMD_SENTRY]), FALSE);
490 /* button box 2: select, deselect */
491 bbox = gtk_vbox_new(FALSE, 0);
492 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
494 pdialog->tabs[loc].cmd[USDLG_CMD_SELECT]
495 = gtk_button_new_with_mnemonic(_("_Select"));
496 gtk_box_pack_start(GTK_BOX(bbox), pdialog->tabs[loc].cmd[USDLG_CMD_SELECT],
497 FALSE, TRUE, 0);
498 g_signal_connect(pdialog->tabs[loc].cmd[USDLG_CMD_SELECT], "clicked",
499 G_CALLBACK(usdlg_cmd_select), (gpointer *)loc);
500 gtk_widget_set_sensitive(
501 GTK_WIDGET(pdialog->tabs[loc].cmd[USDLG_CMD_SELECT]), FALSE);
503 pdialog->tabs[loc].cmd[USDLG_CMD_DESELECT]
504 = gtk_button_new_with_mnemonic(_("_Deselect"));
505 gtk_box_pack_start(GTK_BOX(bbox),
506 pdialog->tabs[loc].cmd[USDLG_CMD_DESELECT], FALSE, TRUE,
508 g_signal_connect(pdialog->tabs[loc].cmd[USDLG_CMD_DESELECT], "clicked",
509 G_CALLBACK(usdlg_cmd_deselect), (gpointer *)loc);
510 gtk_widget_set_sensitive(
511 GTK_WIDGET(pdialog->tabs[loc].cmd[USDLG_CMD_DESELECT]), FALSE);
513 /* button box 3: center, focus */
514 bbox = gtk_vbox_new(FALSE, 0);
515 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
517 pdialog->tabs[loc].cmd[USDLG_CMD_CENTER]
518 = gtk_button_new_with_mnemonic(_("C_enter"));
519 gtk_box_pack_start(GTK_BOX(bbox), pdialog->tabs[loc].cmd[USDLG_CMD_CENTER],
520 FALSE, TRUE, 0);
521 g_signal_connect(pdialog->tabs[loc].cmd[USDLG_CMD_CENTER], "clicked",
522 G_CALLBACK(usdlg_cmd_center), (gpointer *)loc);
523 gtk_widget_set_sensitive(
524 GTK_WIDGET(pdialog->tabs[loc].cmd[USDLG_CMD_CENTER]), FALSE);
526 pdialog->tabs[loc].cmd[USDLG_CMD_FOCUS]
527 = gtk_button_new_with_mnemonic(_("_Focus"));
528 gtk_box_pack_start(GTK_BOX(bbox), pdialog->tabs[loc].cmd[USDLG_CMD_FOCUS],
529 FALSE, TRUE, 0);
530 g_signal_connect(pdialog->tabs[loc].cmd[USDLG_CMD_FOCUS], "clicked",
531 G_CALLBACK(usdlg_cmd_focus), (gpointer *)loc);
532 gtk_widget_set_sensitive(
533 GTK_WIDGET(pdialog->tabs[loc].cmd[USDLG_CMD_FOCUS]), FALSE);
536 /*****************************************************************************
537 Create a player dialog store.
538 *****************************************************************************/
539 static GtkTreeStore *usdlg_tab_store_new(void)
541 GtkTreeStore *store;
542 GType model_types[USDLG_COLUMNS_ALL];
543 int i;
545 for (i = 0; i < USDLG_COLUMNS_ALL; i++) {
546 switch (usdlg_col_types[i]) {
547 case COL_PIXBUF:
548 model_types[i] = GDK_TYPE_PIXBUF;
549 break;
550 case COL_TEXT:
551 model_types[i] = G_TYPE_STRING;
552 break;
553 case COL_INT:
554 model_types[i] = G_TYPE_INT;
555 break;
559 store = gtk_tree_store_newv(i, model_types);
561 return store;
564 /*****************************************************************************
565 Update on tab of the dialog.
566 *****************************************************************************/
567 static bool usdlg_tab_update(struct unit_select_dialog *pdialog,
568 struct usdata_hash *ushash,
569 enum unit_select_location_mode loc)
571 bool show = FALSE;
572 GtkTreeStore *store;
574 fc_assert_ret_val(ushash, FALSE);
575 fc_assert_ret_val(pdialog != NULL, FALSE);
577 store = pdialog->tabs[loc].store;
579 /* clear current store. */
580 gtk_tree_store_clear(GTK_TREE_STORE(store));
582 /* Iterate over all unit types. */
583 if (loc == SELLOC_UNITS) {
584 /* Special case - show all units on this tile in their transports. */
585 unit_type_iterate(utype) {
586 struct usdata *data;
588 usdata_hash_lookup(ushash, utype_index(utype), &data);
590 if (!data) {
591 continue;
594 activity_type_iterate(act) {
595 if (unit_list_size(data->units[loc][act]) == 0) {
596 continue;
599 unit_list_iterate(data->units[loc][act], punit) {
600 GtkTreeIter it_unit;
602 usdlg_tab_append_units(pdialog, loc, act, punit, FALSE,
603 &it_unit, NULL);
604 } unit_list_iterate_end;
606 /* Show this tab. */
607 show = TRUE;
608 } activity_type_iterate_end;
609 } unit_type_iterate_end;
610 } else {
611 unit_type_iterate(utype) {
612 struct usdata *data;
613 bool first = TRUE;
614 GtkTreeIter it_utype;
615 GtkTreePath *path;
616 int count = 0;
618 usdata_hash_lookup(ushash, utype_index(utype), &data);
620 if (!data) {
621 continue;
624 activity_type_iterate(act) {
625 GtkTreeIter it_act;
627 if (unit_list_size(data->units[loc][act]) == 0) {
628 continue;
631 /* Level 1: Display unit type. */
632 if (first) {
633 usdlg_tab_append_utype(GTK_TREE_STORE(store), loc, data->utype,
634 &it_utype);
635 first = FALSE;
638 /* Level 2: Display unit activities. */
639 usdlg_tab_append_activity(GTK_TREE_STORE(store), loc, data->utype,
640 act, unit_list_size(data->units[loc][act]),
641 &it_act, &it_utype);
643 /* Level 3: Display all units with this activitiy
644 * (and transported units in further level(s)). */
645 unit_list_iterate(data->units[loc][act], punit) {
646 GtkTreeIter it_unit;
648 usdlg_tab_append_units(pdialog, loc, act, punit, FALSE,
649 &it_unit, &it_act);
650 } unit_list_iterate_end;
652 count += unit_list_size(data->units[loc][act]);
654 /* Update sum of units with this type. */
655 gtk_tree_store_set(GTK_TREE_STORE(store), &it_utype, 2, count, -1);
657 /* Expand to the activities. */
658 path
659 = gtk_tree_model_get_path(GTK_TREE_MODEL(pdialog->tabs[loc].store),
660 &it_utype);
661 gtk_tree_view_expand_row(GTK_TREE_VIEW(pdialog->tabs[loc].view), path,
662 FALSE);
663 gtk_tree_path_free(path);
665 /* Show this tab. */
666 show = TRUE;
667 } activity_type_iterate_end;
668 } unit_type_iterate_end;
671 return show;
674 /*****************************************************************************
675 Append the data for one unit type.
676 *****************************************************************************/
677 static void usdlg_tab_append_utype(GtkTreeStore *store,
678 enum unit_select_location_mode loc,
679 struct unit_type *putype,
680 GtkTreeIter *it)
682 GdkPixbuf *pix;
683 char buf[128];
685 fc_assert_ret(store != NULL);
686 fc_assert_ret(putype != NULL);
688 /* Add this item. */
689 gtk_tree_store_append(GTK_TREE_STORE(store), it, NULL);
691 /* Create a icon */
692 pix = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
693 tileset_full_tile_width(tileset),
694 tileset_full_tile_height(tileset));
697 struct canvas canvas_store;
699 canvas_store.type = CANVAS_PIXBUF;
700 canvas_store.v.pixbuf = pix;
702 gdk_pixbuf_fill(pix, 0x00000000);
703 put_unittype(putype, &canvas_store, 1.0, 0, 0);
706 /* The name of the unit. */
707 fc_snprintf(buf, sizeof(buf), "%s", utype_name_translation(putype));
709 /* Add it to the tree. */
710 gtk_tree_store_set(GTK_TREE_STORE(store), it,
711 0, pix, /* Unit pixmap */
712 1, buf, /* Text */
713 2, -1, /* will be set later */ /* Number of units */
714 3, utype_index(putype), /* Unit type ID */
715 /* 4: not set */ /* Unit ID */
716 5, loc, /* Unit location */
717 /* 6: not set */ /* Unit activity */
718 7, ROW_UNITTYPE, /* Row type */
719 8, PANGO_STYLE_NORMAL, /* Style */
720 9, PANGO_WEIGHT_BOLD, /* Weight */
721 -1);
722 g_object_unref(pix);
725 /*****************************************************************************
726 Append the unit activity.
727 *****************************************************************************/
728 static void usdlg_tab_append_activity(GtkTreeStore *store,
729 enum unit_select_location_mode loc,
730 const struct unit_type *putype,
731 enum unit_activity act,
732 int count, GtkTreeIter *it,
733 GtkTreeIter *parent)
735 char buf[128] = "";
737 fc_assert_ret(store != NULL);
738 fc_assert_ret(putype != NULL);
740 /* Add this item. */
741 gtk_tree_store_append(GTK_TREE_STORE(store), it, parent);
743 /* The activity. */
744 fc_snprintf(buf, sizeof(buf), "%s", get_activity_text(act));
746 /* Add it to the tree. */
747 gtk_tree_store_set(GTK_TREE_STORE(store), it,
748 /* 0: not set */ /* Unit pixmap */
749 1, buf, /* Text */
750 2, count, /* Number of units */
751 3, utype_index(putype), /* Unit type ID */
752 /* 4: not set */ /* Unit ID */
753 5, loc, /* Unit location */
754 6, act, /* Unit activity */
755 7, ROW_ACTIVITY, /* Row type */
756 8, PANGO_STYLE_NORMAL, /* Style */
757 9, PANGO_WEIGHT_NORMAL, /* Weight */
758 -1);
761 /*****************************************************************************
762 Append units (recursively).
763 *****************************************************************************/
764 static void usdlg_tab_append_units(struct unit_select_dialog *pdialog,
765 enum unit_select_location_mode loc,
766 enum unit_activity act,
767 const struct unit *punit,
768 bool transported, GtkTreeIter *it,
769 GtkTreeIter *parent)
771 char buf[248] = "", buf2[248] = "";
772 GdkPixbuf *pix;
773 struct city *phome;
774 enum usdlg_row_types row = ROW_UNIT;
775 int style = PANGO_STYLE_NORMAL;
776 int weight = PANGO_WEIGHT_NORMAL;
777 GtkTreeStore *store;
779 fc_assert_ret(pdialog != NULL);
780 fc_assert_ret(punit != NULL);
782 store = pdialog->tabs[loc].store;
785 /* Add this item. */
786 gtk_tree_store_append(GTK_TREE_STORE(store), it, parent);
788 /* Unit pixmap */
789 pix = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
790 tileset_full_tile_width(tileset),
791 tileset_full_tile_height(tileset));
794 struct canvas canvas_store;
796 canvas_store.type = CANVAS_PIXBUF;
797 canvas_store.v.pixbuf = pix;
799 gdk_pixbuf_fill(pix, 0x00000000);
800 put_unit(punit, &canvas_store, 1.0, 0, 0);
803 phome = game_city_by_number(punit->homecity);
804 if (phome) {
805 fc_snprintf(buf2, sizeof(buf2), "%s", city_name_get(phome));
806 } else if (unit_owner(punit) == client_player()
807 || client_is_global_observer()) {
808 /* TRANS: used in place of unit home city name */
809 sz_strlcpy(buf2, _("no home city"));
810 } else {
811 /* TRANS: used in place of unit home city name */
812 sz_strlcpy(buf2, _("unknown"));
814 #ifdef FREECIV_DEBUG
815 /* Strings only used in debug builds, don't bother with i18n */
816 fc_snprintf(buf, sizeof(buf), "%s [Unit ID %d]\n(%s)\nCoordinates: (%d,%d)",
817 unit_name_translation(punit), punit->id, buf2,
818 TILE_XY(unit_tile(punit)));
820 struct unit *ptrans = unit_transport_get(punit);
822 if (ptrans) {
823 cat_snprintf(buf, sizeof(buf), "\nTransported by unit ID %d",
824 ptrans->id);
827 #else /* FREECIV_DEBUG */
828 /* TRANS: unit type and home city, e.g. "Transport\n(New Orleans)" */
829 fc_snprintf(buf, sizeof(buf), _("%s\n(%s)"), unit_name_translation(punit),
830 buf2);
831 #endif /* FREECIV_DEBUG */
833 if (transported) {
834 weight = PANGO_WEIGHT_NORMAL;
835 style = PANGO_STYLE_ITALIC;
836 row = ROW_UNIT_TRANSPORTED;
839 /* Add it to the tree. */
840 gtk_tree_store_set(GTK_TREE_STORE(store), it,
841 0, pix, /* Unit pixmap */
842 1, buf, /* Text */
843 2, 1, /* Number of units */
844 3, utype_index(unit_type_get(punit)), /* Unit type ID */
845 4, punit->id, /* Unit ID */
846 5, loc, /* Unit location */
847 6, act, /* Unit activity */
848 7, row, /* Row type */
849 8, style, /* Style */
850 9, weight, /* Weight */
851 -1);
852 g_object_unref(pix);
854 if (get_transporter_occupancy(punit) > 0) {
855 unit_list_iterate(unit_transport_cargo(punit), pcargo) {
856 GtkTreeIter it_cargo;
858 usdlg_tab_append_units(pdialog, loc, act, pcargo, TRUE, &it_cargo, it);
859 } unit_list_iterate_end;
862 if (!transported && unit_is_in_focus(punit)) {
863 pdialog->tabs[loc].path
864 = gtk_tree_model_get_path(GTK_TREE_MODEL(store), it);
868 /*****************************************************************************
869 Callback for the ready button.
870 *****************************************************************************/
871 static void usdlg_cmd_ready(GtkObject *object, gpointer data)
873 usdlg_cmd_exec(object, data, USDLG_CMD_READY);
876 /*****************************************************************************
877 Callback for the sentry button.
878 *****************************************************************************/
879 static void usdlg_cmd_sentry(GtkObject *object, gpointer data)
881 usdlg_cmd_exec(object, data, USDLG_CMD_SENTRY);
884 /*****************************************************************************
885 Callback for the select button.
886 *****************************************************************************/
887 static void usdlg_cmd_select(GtkObject *object, gpointer data)
889 usdlg_cmd_exec(object, data, USDLG_CMD_SELECT);
892 /*****************************************************************************
893 Callback for the deselect button.
894 *****************************************************************************/
895 static void usdlg_cmd_deselect(GtkObject *object, gpointer data)
897 usdlg_cmd_exec(object, data, USDLG_CMD_DESELECT);
900 /*****************************************************************************
901 Main function for the callbacks.
902 *****************************************************************************/
903 static void usdlg_cmd_exec(GtkObject *object, gpointer mode_data,
904 enum usdlg_cmd cmd)
906 enum unit_select_location_mode loc_mode = (enum unit_select_location_mode) mode_data;
907 GtkTreeView *view;
908 GtkTreeSelection *selection;
909 GtkTreeModel *model;
910 GtkTreeIter it;
911 gint row;
912 struct unit_select_dialog *pdialog = usdlg_get(FALSE);
914 fc_assert_ret(pdialog != NULL);
915 fc_assert_ret(unit_select_location_mode_is_valid(loc_mode));
917 if (!can_client_change_view() || !can_client_control()) {
918 return;
921 view = GTK_TREE_VIEW(pdialog->tabs[loc_mode].view);
922 selection = gtk_tree_view_get_selection(view);
924 if (!gtk_tree_selection_get_selected(selection, &model, &it)) {
925 log_debug("No selection");
926 return;
928 gtk_tree_model_get(model, &it, USDLG_COL_ROW_TYPE, &row, -1);
930 switch (row) {
931 case ROW_UNITTYPE:
933 gint loc, utid;
934 struct usdata_hash *ushash;
935 struct usdata *data;
937 gtk_tree_model_get(model, &it, USDLG_COL_LOCATION, &loc,
938 USDLG_COL_UTID, &utid, -1);
940 /* We can't be sure that all units still exists - recalc the data. */
941 ushash = usdlg_data_new(pdialog->ptile);
943 usdata_hash_lookup(ushash, utid, &data);
944 if (data != NULL) {
945 activity_type_iterate(act) {
946 if (unit_list_size(data->units[loc][act]) == 0) {
947 continue;
950 unit_list_iterate(data->units[loc][act], punit) {
951 usdlg_cmd_exec_unit(punit, cmd);
952 } unit_list_iterate_end;
953 } activity_type_iterate_end;
956 /* Destroy the hash. */
957 usdlg_data_destroy(ushash);
959 break;
960 case ROW_ACTIVITY:
962 gint loc, act, utid;
963 struct usdata_hash *ushash;
964 struct usdata *data;
966 gtk_tree_model_get(model, &it, USDLG_COL_ACTIVITY, &act,
967 USDLG_COL_LOCATION, &loc, USDLG_COL_UTID, &utid, -1);
969 /* We can't be sure that all units still exists - recalc the data. */
970 ushash = usdlg_data_new(pdialog->ptile);
972 usdata_hash_lookup(ushash, utid, &data);
973 if (data != NULL
974 && unit_list_size(data->units[loc][act]) != 0) {
975 unit_list_iterate(data->units[loc][act], punit) {
976 usdlg_cmd_exec_unit(punit, cmd);
977 } unit_list_iterate_end;
980 /* Destroy the hash. */
981 usdlg_data_destroy(ushash);
983 break;
984 case ROW_UNIT:
985 case ROW_UNIT_TRANSPORTED:
987 gint uid;
988 struct unit *punit;
990 gtk_tree_model_get(model, &it, USDLG_COL_UID, &uid, -1);
992 punit = game_unit_by_number(uid);
994 if (!punit) {
995 log_debug("Unit vanished (Unit ID %d)!", uid);
996 return;
999 usdlg_cmd_exec_unit(punit, cmd);
1001 break;
1004 /* Update focus. */
1005 unit_focus_update();
1006 /* Refresh dialog. */
1007 usdlg_refresh(pdialog);
1010 /*****************************************************************************
1011 Update one unit (select/deselect/ready/sentry).
1012 *****************************************************************************/
1013 static void usdlg_cmd_exec_unit(struct unit *punit, enum usdlg_cmd cmd)
1015 fc_assert_ret(punit);
1017 switch (cmd) {
1018 case USDLG_CMD_SELECT:
1019 if (!unit_is_in_focus(punit)) {
1020 unit_focus_add(punit);
1022 break;
1023 case USDLG_CMD_DESELECT:
1024 if (unit_is_in_focus(punit)) {
1025 unit_focus_remove(punit);
1027 break;
1028 case USDLG_CMD_READY:
1029 if (punit->activity != ACTIVITY_IDLE) {
1030 request_new_unit_activity(punit, ACTIVITY_IDLE);
1032 break;
1033 case USDLG_CMD_SENTRY:
1034 if (punit->activity != ACTIVITY_SENTRY) {
1035 request_new_unit_activity(punit, ACTIVITY_SENTRY);
1037 break;
1038 case USDLG_CMD_CENTER:
1039 case USDLG_CMD_FOCUS:
1040 /* Nothing here. It is done in its own functions. */
1041 break;
1042 case USDLG_CMD_LAST:
1043 /* Should never happen. */
1044 fc_assert_ret(cmd != USDLG_CMD_LAST);
1045 break;
1049 /*****************************************************************************
1050 Callback for the center button.
1051 *****************************************************************************/
1052 static void usdlg_cmd_center(GtkObject *object, gpointer data)
1054 enum unit_select_location_mode loc = (enum unit_select_location_mode) data;
1055 GtkTreeView *view;
1056 GtkTreeSelection *selection;
1057 GtkTreeModel *model;
1058 GtkTreeIter it;
1059 gint row;
1060 struct unit_select_dialog *pdialog = usdlg_get(FALSE);
1062 fc_assert_ret(pdialog != NULL);
1063 fc_assert_ret(unit_select_location_mode_is_valid(loc));
1065 view = GTK_TREE_VIEW(pdialog->tabs[loc].view);
1066 selection = gtk_tree_view_get_selection(view);
1068 if (!gtk_tree_selection_get_selected(selection, &model, &it)) {
1069 log_debug("No selection");
1070 return;
1072 gtk_tree_model_get(model, &it, USDLG_COL_ROW_TYPE, &row, -1);
1074 if (row == ROW_UNIT || row == ROW_UNIT_TRANSPORTED) {
1075 gint uid;
1076 struct unit *punit;
1078 gtk_tree_model_get(model, &it, USDLG_COL_UID, &uid, -1);
1080 punit = player_unit_by_number(client_player(), uid);
1081 if (punit) {
1082 center_tile_mapcanvas(unit_tile(punit));
1087 /*****************************************************************************
1088 Callback for the focus button.
1089 *****************************************************************************/
1090 static void usdlg_cmd_focus(GtkObject *object, gpointer data)
1092 enum unit_select_location_mode loc = (enum unit_select_location_mode) data;
1093 struct unit_select_dialog *pdialog = usdlg_get(FALSE);
1095 fc_assert_ret(pdialog != NULL);
1096 fc_assert_ret(unit_select_location_mode_is_valid(loc));
1098 usdlg_cmd_focus_real(GTK_TREE_VIEW(pdialog->tabs[loc].view));
1101 /*****************************************************************************
1102 Callback if a row is activated.
1103 *****************************************************************************/
1104 static void usdlg_cmd_row_activated(GtkTreeView *view, GtkTreePath *path,
1105 GtkTreeViewColumn *col, gpointer data)
1107 usdlg_cmd_focus_real(view);
1110 /*****************************************************************************
1111 Focus to the currently selected unit.
1112 *****************************************************************************/
1113 static void usdlg_cmd_focus_real(GtkTreeView *view)
1115 GtkTreeSelection *selection = gtk_tree_view_get_selection(view);
1116 GtkTreeModel *model;
1117 GtkTreeIter it;
1118 gint row;
1120 if (!can_client_change_view() || !can_client_control()) {
1121 return;
1124 if (!gtk_tree_selection_get_selected(selection, &model, &it)) {
1125 log_debug("No selection");
1126 return;
1128 gtk_tree_model_get(model, &it, USDLG_COL_ROW_TYPE, &row, -1);
1130 if (row == ROW_UNIT || row == ROW_UNIT_TRANSPORTED) {
1131 gint uid;
1132 struct unit *punit;
1134 gtk_tree_model_get(model, &it, USDLG_COL_UID, &uid, -1);
1136 punit = player_unit_by_number(client_player(), uid);
1137 if (punit && unit_owner(punit) == client_player()) {
1138 unit_focus_set(punit);
1139 usdlg_destroy();
1144 /*****************************************************************************
1145 Callback if the row is changed.
1146 *****************************************************************************/
1147 static void usdlg_cmd_cursor_changed(GtkTreeView *view, gpointer data)
1149 enum unit_select_location_mode loc = (enum unit_select_location_mode) data;
1150 GtkTreeSelection *selection;
1151 GtkTreeModel *model;
1152 GtkTreeIter it;
1153 gint row, uid;
1154 struct unit_select_dialog *pdialog = usdlg_get(FALSE);
1155 struct unit *punit;
1156 bool cmd_status[USDLG_CMD_LAST];
1157 int cmd_id;
1159 fc_assert_ret(pdialog != NULL);
1160 fc_assert_ret(unit_select_location_mode_is_valid(loc));
1162 selection = gtk_tree_view_get_selection(view);
1163 if (!gtk_tree_selection_get_selected(selection, &model, &it)) {
1164 log_debug("No selection");
1165 return;
1167 gtk_tree_model_get(model, &it, USDLG_COL_ROW_TYPE, &row, USDLG_COL_UID,
1168 &uid, -1);
1170 switch (row) {
1171 case ROW_UNITTYPE:
1172 case ROW_ACTIVITY:
1173 /* Button status for rows unittype and activity:
1174 * player observer
1175 * ready TRUE FALSE
1176 * sentry TRUE FALSE
1177 * select TRUE FALSE
1178 * deselect TRUE FALSE
1179 * center FALSE FALSE
1180 * focus FALSE FALSE */
1181 if (can_client_change_view() && can_client_control()) {
1182 cmd_status[USDLG_CMD_READY] = TRUE;
1183 cmd_status[USDLG_CMD_SENTRY] = TRUE;
1184 cmd_status[USDLG_CMD_SELECT] = TRUE;
1185 cmd_status[USDLG_CMD_DESELECT] = TRUE;
1186 } else {
1187 cmd_status[USDLG_CMD_READY] = FALSE;
1188 cmd_status[USDLG_CMD_SENTRY] = FALSE;
1189 cmd_status[USDLG_CMD_SELECT] = FALSE;
1190 cmd_status[USDLG_CMD_DESELECT] = FALSE;
1193 cmd_status[USDLG_CMD_CENTER] = FALSE;
1194 cmd_status[USDLG_CMD_FOCUS] = FALSE;
1195 break;
1196 case ROW_UNIT:
1197 case ROW_UNIT_TRANSPORTED:
1198 /* Button status for rows unit and unit (transported):
1199 * player observer
1200 * ready !IDLE FALSE
1201 * sentry !SENTRY FALSE
1202 * select !FOCUS FALSE
1203 * deselect FOCUS FALSE
1204 * center TRUE TRUE
1205 * focus !FOCUS FALSE */
1206 punit = player_unit_by_number(client_player(), uid);
1208 if (punit && can_client_change_view() && can_client_control()) {
1209 if (punit->activity == ACTIVITY_IDLE) {
1210 cmd_status[USDLG_CMD_READY] = FALSE;
1211 } else {
1212 cmd_status[USDLG_CMD_READY] = TRUE;
1215 if (punit->activity == ACTIVITY_SENTRY) {
1216 cmd_status[USDLG_CMD_SENTRY] = FALSE;
1217 } else {
1218 cmd_status[USDLG_CMD_SENTRY] = TRUE;
1221 if (!unit_is_in_focus(punit)) {
1222 cmd_status[USDLG_CMD_SELECT] = TRUE;
1223 cmd_status[USDLG_CMD_DESELECT] = FALSE;
1224 cmd_status[USDLG_CMD_FOCUS] = TRUE;
1225 } else {
1226 cmd_status[USDLG_CMD_SELECT] = FALSE;
1227 cmd_status[USDLG_CMD_DESELECT] = TRUE;
1228 cmd_status[USDLG_CMD_FOCUS] = FALSE;
1230 } else {
1231 cmd_status[USDLG_CMD_READY] = FALSE;
1232 cmd_status[USDLG_CMD_SENTRY] = FALSE;
1234 cmd_status[USDLG_CMD_SELECT] = FALSE;
1235 cmd_status[USDLG_CMD_DESELECT] = FALSE;
1237 cmd_status[USDLG_CMD_FOCUS] = FALSE;
1240 cmd_status[USDLG_CMD_CENTER] = TRUE;
1241 break;
1244 /* Set widget status. */
1245 for (cmd_id = 0; cmd_id < USDLG_CMD_LAST; cmd_id++) {
1246 gtk_widget_set_sensitive(GTK_WIDGET(pdialog->tabs[loc].cmd[cmd_id]),
1247 cmd_status[cmd_id]);