Qt client - remove right click menu from end turn sidebar
[freeciv.git] / client / gui-gtk-2.0 / gotodlg.c
blob1b9e8bf2021ba7fbad931a3d8b57cb78776afd29
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 <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
22 #include <gtk/gtk.h>
23 #include <gdk/gdkkeysyms.h>
25 /* utility */
26 #include "astring.h"
27 #include "fcintl.h"
28 #include "log.h"
29 #include "support.h"
31 /* common */
32 #include "game.h"
33 #include "map.h"
34 #include "packets.h"
35 #include "player.h"
36 #include "unit.h"
37 #include "unitlist.h"
39 /* client */
40 #include "client_main.h"
41 #include "control.h"
42 #include "goto.h"
43 #include "options.h"
44 #include "text.h"
46 /* clien/gui-gtk-2.0 */
47 #include "plrdlg.h"
48 #include "dialogs.h"
49 #include "gui_main.h"
50 #include "gui_stuff.h"
51 #include "mapview.h"
53 #include "gotodlg.h"
56 static GtkWidget *dshell = NULL;
57 static GtkWidget *view;
58 static GtkWidget *source;
59 static GtkWidget *all_toggle;
60 static GtkListStore *goto_list_store;
61 static GtkTreeSelection *goto_list_selection;
62 struct tile *original_tile;
64 static void update_goto_dialog(GtkToggleButton *button);
65 static void update_source_label(void);
66 static void refresh_airlift_column(void);
67 static void refresh_airlift_button(void);
68 static void goto_selection_callback(GtkTreeSelection *selection, gpointer data);
70 static struct city *get_selected_city(void);
72 enum {
73 CMD_AIRLIFT = 1, CMD_GOTO
76 enum {
77 GD_COL_CITY_ID = 0, /* Not shown if not compiled with --enable-debug. */
78 GD_COL_CITY_NAME,
79 GD_COL_FLAG,
80 GD_COL_NATION,
81 GD_COL_AIRLIFT,
83 GD_COL_NUM
86 /**************************************************************************
87 User has responded to goto dialog
88 **************************************************************************/
89 static void goto_cmd_callback(GtkWidget *dlg, gint arg)
91 switch (arg) {
92 case GTK_RESPONSE_CANCEL:
93 center_tile_mapcanvas(original_tile);
94 break;
96 case CMD_AIRLIFT:
98 struct city *pdestcity = get_selected_city();
100 if (pdestcity) {
101 unit_list_iterate(get_units_in_focus(), punit) {
102 if (unit_can_airlift_to(punit, pdestcity)) {
103 request_unit_airlift(punit, pdestcity);
105 } unit_list_iterate_end;
108 break;
110 case CMD_GOTO:
112 struct city *pdestcity = get_selected_city();
114 if (pdestcity) {
115 unit_list_iterate(get_units_in_focus(), punit) {
116 send_goto_tile(punit, pdestcity->tile);
117 } unit_list_iterate_end;
120 break;
122 default:
123 break;
126 gtk_widget_destroy(dlg);
127 dshell = NULL;
131 /**************************************************************************
132 Create goto -dialog for gotoing or airlifting unit
133 **************************************************************************/
134 static void create_goto_dialog(void)
136 GtkWidget *sw, *label, *frame, *vbox;
137 GtkCellRenderer *rend;
138 GtkTreeViewColumn *col;
140 dshell = gtk_dialog_new_with_buttons(_("Goto/Airlift Unit"),
141 NULL,
143 GTK_STOCK_CANCEL,
144 GTK_RESPONSE_CANCEL,
145 _("Air_lift"),
146 CMD_AIRLIFT,
147 _("_Goto"),
148 CMD_GOTO,
149 NULL);
150 setup_dialog(dshell, toplevel);
151 gtk_window_set_position(GTK_WINDOW(dshell), GTK_WIN_POS_MOUSE);
152 gtk_dialog_set_default_response(GTK_DIALOG(dshell), CMD_GOTO);
153 g_signal_connect(dshell, "destroy",
154 G_CALLBACK(gtk_widget_destroyed), &dshell);
155 g_signal_connect(dshell, "response",
156 G_CALLBACK(goto_cmd_callback), NULL);
158 source = gtk_label_new("" /* filled in later */);
159 gtk_label_set_line_wrap(GTK_LABEL(source), TRUE);
160 gtk_label_set_justify(GTK_LABEL(source), GTK_JUSTIFY_CENTER);
161 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dshell)->vbox),
162 source, FALSE, FALSE, 0);
164 label = g_object_new(GTK_TYPE_LABEL,
165 "use-underline", TRUE,
166 "label", _("Select destination ci_ty"),
167 "xalign", 0.0,
168 "yalign", 0.5,
169 NULL);
170 frame = gtk_frame_new("");
171 gtk_frame_set_label_widget(GTK_FRAME(frame), label);
172 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dshell)->vbox),
173 frame, TRUE, TRUE, 0);
175 vbox = gtk_vbox_new(FALSE, 6);
176 gtk_container_add(GTK_CONTAINER(frame), vbox);
178 goto_list_store = gtk_list_store_new(GD_COL_NUM, G_TYPE_INT, G_TYPE_STRING,
179 GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
180 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(goto_list_store),
181 GD_COL_CITY_NAME, GTK_SORT_ASCENDING);
183 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(goto_list_store));
184 g_object_unref(goto_list_store);
185 goto_list_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
186 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), TRUE);
187 gtk_tree_view_set_search_column(GTK_TREE_VIEW(view), GD_COL_CITY_NAME);
188 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(view), TRUE);
190 /* Set the mnemonic in the frame label to focus the city list */
191 gtk_label_set_mnemonic_widget(GTK_LABEL(label), view);
193 #ifdef DEBUG
194 rend = gtk_cell_renderer_text_new();
195 col = gtk_tree_view_column_new_with_attributes(_("Id"), rend,
196 "text", GD_COL_CITY_ID, NULL);
197 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
198 gtk_tree_view_column_set_sort_column_id(col, GD_COL_CITY_ID);
199 #endif /* DEBUG */
201 rend = gtk_cell_renderer_text_new();
202 col = gtk_tree_view_column_new_with_attributes(_("City"), rend,
203 "text", GD_COL_CITY_NAME, NULL);
204 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
205 gtk_tree_view_column_set_sort_column_id(col, GD_COL_CITY_NAME);
207 rend = gtk_cell_renderer_pixbuf_new();
208 col = gtk_tree_view_column_new_with_attributes(NULL, rend,
209 "pixbuf", GD_COL_FLAG, NULL);
210 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
212 rend = gtk_cell_renderer_text_new();
213 col = gtk_tree_view_column_new_with_attributes(_("Nation"), rend,
214 "text", GD_COL_NATION, NULL);
215 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
216 gtk_tree_view_column_set_sort_column_id(col, GD_COL_NATION);
218 rend = gtk_cell_renderer_text_new();
219 col = gtk_tree_view_column_new_with_attributes(_("Airlift"), rend,
220 "text", GD_COL_AIRLIFT, NULL);
221 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
222 gtk_tree_view_column_set_sort_column_id(col, GD_COL_AIRLIFT);
224 sw = gtk_scrolled_window_new(NULL, NULL);
225 gtk_container_add(GTK_CONTAINER(sw), view);
226 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
227 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
228 gtk_widget_set_size_request(sw, -1, 200);
230 gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
232 all_toggle = gtk_check_button_new_with_mnemonic(_("Show _All Cities"));
233 gtk_box_pack_start(GTK_BOX(vbox), all_toggle, FALSE, FALSE, 0);
235 g_signal_connect(all_toggle, "toggled", G_CALLBACK(update_goto_dialog), NULL);
237 g_signal_connect(goto_list_selection, "changed",
238 G_CALLBACK(goto_selection_callback), NULL);
240 gtk_widget_show_all(GTK_DIALOG(dshell)->vbox);
241 gtk_widget_show_all(GTK_DIALOG(dshell)->action_area);
244 original_tile = get_center_tile_mapcanvas();
246 update_source_label();
247 update_goto_dialog(GTK_TOGGLE_BUTTON(all_toggle));
248 gtk_tree_view_focus(GTK_TREE_VIEW(view));
251 /****************************************************************
252 popup the dialog
253 *****************************************************************/
254 void popup_goto_dialog(void)
256 if (!can_client_issue_orders() || get_num_units_in_focus() == 0) {
257 return;
260 if (!dshell) {
261 create_goto_dialog();
264 gtk_window_present(GTK_WINDOW(dshell));
267 /**************************************************************************
268 Return currently selected city
269 **************************************************************************/
270 static struct city *get_selected_city(void)
272 GtkTreeModel *model;
273 GtkTreeIter it;
274 int city_id;
276 if (!gtk_tree_selection_get_selected(goto_list_selection, NULL, &it)) {
277 return NULL;
280 model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
282 gtk_tree_model_get(model, &it, GD_COL_CITY_ID, &city_id, -1);
284 return game_city_by_number(city_id);
287 /**************************************************************************
288 Appends the list of the city owned by the player in the goto dialog.
289 **************************************************************************/
290 static bool list_store_append_player_cities(GtkListStore *store,
291 const struct player *pplayer)
293 GtkTreeIter it;
294 struct nation_type *pnation = nation_of_player(pplayer);
295 const char *nation = nation_adjective_translation(pnation);
296 GdkPixbuf *pixbuf;
298 if (city_list_size(pplayer->cities) == 0) {
299 return FALSE;
302 pixbuf = get_flag(pnation);
304 city_list_iterate(pplayer->cities, pcity) {
305 gtk_list_store_append(store, &it);
306 gtk_list_store_set(store, &it,
307 GD_COL_CITY_ID, pcity->id,
308 GD_COL_CITY_NAME, city_name_get(pcity),
309 GD_COL_FLAG, pixbuf,
310 GD_COL_NATION, nation,
311 /* GD_COL_AIRLIFT is populated later */
312 -1);
313 } city_list_iterate_end;
314 g_object_unref(pixbuf);
316 return TRUE;
319 /**************************************************************************
320 Refresh the label that shows where the selected unit(s) currently are
321 (and the relevant cities' airlift capacities, if relevant).
322 **************************************************************************/
323 static void update_source_label(void)
325 /* Arbitrary limit to stop the label getting ridiculously long */
326 static const int max_cities = 10;
327 struct {
328 const struct city *city;
329 struct unit_list *units;
330 } cities[max_cities];
331 int ncities = 0;
332 bool too_many = FALSE;
333 bool no_city = FALSE; /* any units not in a city? */
334 struct astring strs[max_cities];
335 int nstrs;
336 char *last_str;
337 const char *descriptions[max_cities+1];
338 int i;
340 /* Sanity check: if no units selected, give up */
341 if (unit_list_size(get_units_in_focus()) == 0) {
342 gtk_label_set_text(GTK_LABEL(source), _("No units selected."));
343 return;
346 /* Divide selected units up into a list of unique cities */
347 unit_list_iterate(get_units_in_focus(), punit) {
348 const struct city *pcity = tile_city(unit_tile(punit));
349 if (pcity) {
350 /* Inefficient, but it's not a long list */
351 for (i = 0; i < ncities; i++) {
352 if (cities[i].city == pcity) {
353 unit_list_append(cities[i].units, punit);
354 break;
357 if (i == ncities) {
358 if (ncities < max_cities) {
359 cities[ncities].city = pcity;
360 cities[ncities].units = unit_list_new();
361 unit_list_append(cities[ncities].units, punit);
362 ncities++;
363 } else {
364 too_many = TRUE;
365 break;
368 } else {
369 no_city = TRUE;
371 } unit_list_iterate_end;
373 /* Describe the individual cities. */
374 for (i = 0; i < ncities; i++) {
375 const char *air_text = get_airlift_text(cities[i].units, NULL);
377 astr_init(&strs[i]);
378 if (air_text != NULL) {
379 astr_add(&strs[i],
380 /* TRANS: goto/airlift dialog. "Paris (airlift: 2/4)".
381 * A set of these appear in an "and"-separated list. */
382 _("%s (airlift: %s)"),
383 city_name_get(cities[i].city), air_text);
384 } else {
385 astr_add(&strs[i], "%s", city_name_get(cities[i].city));
387 descriptions[i] = astr_str(&strs[i]);
388 unit_list_destroy(cities[i].units);
390 if (too_many) {
391 /* TRANS: goto/airlift dialog. Too many cities to list, some omitted.
392 * Appears at the end of an "and"-separated list. */
393 descriptions[ncities] = last_str = fc_strdup(Q_("?gotodlg:more"));
394 nstrs = ncities+1;
395 } else if (no_city) {
396 /* TRANS: goto/airlift dialog. For units not currently in a city.
397 * Appears at the end of an "and"-separated list. */
398 descriptions[ncities] = last_str = fc_strdup(Q_("?gotodlg:no city"));
399 nstrs = ncities+1;
400 } else {
401 last_str = NULL;
402 nstrs = ncities;
405 /* Finally, update the label. */
407 struct astring label = ASTRING_INIT, list = ASTRING_INIT;
408 astr_set(&label,
409 /* TRANS: goto/airlift dialog. Current location of units; %s is an
410 * "and"-separated list of cities and associated info */
411 _("Currently in: %s"),
412 astr_build_and_list(&list, descriptions, nstrs));
413 astr_free(&list);
414 gtk_label_set_text(GTK_LABEL(source), astr_str(&label));
415 astr_free(&label);
418 /* Clear up. */
419 for (i = 0; i < ncities; i++) {
420 astr_free(&strs[i]);
422 free(last_str); /* might have been NULL */
425 /**************************************************************************
426 Refresh city list (in response to "all cities" checkbox changing).
427 **************************************************************************/
428 static void update_goto_dialog(GtkToggleButton *button)
430 bool nonempty = FALSE;
432 gtk_list_store_clear(goto_list_store);
434 if (!client_has_player()) {
435 /* Case global observer. */
436 return;
439 if (gtk_toggle_button_get_active(button)) {
440 players_iterate(pplayer) {
441 nonempty |= list_store_append_player_cities(goto_list_store, pplayer);
442 } players_iterate_end;
443 } else {
444 nonempty |= list_store_append_player_cities(goto_list_store, client_player());
447 refresh_airlift_column();
449 if (!nonempty) {
450 /* No selection causes callbacks to fire, causing also Airlift button
451 * to update. Do it here. */
452 refresh_airlift_button();
456 /**************************************************************************
457 Refresh airlift column in city list (without tearing everything down).
458 **************************************************************************/
459 static void refresh_airlift_column(void)
461 GtkTreeIter iter;
462 bool valid;
464 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(goto_list_store), &iter);
465 while (valid) {
466 int city_id;
467 const struct city *pcity;
468 const char *air_text;
470 gtk_tree_model_get(GTK_TREE_MODEL(goto_list_store), &iter,
471 GD_COL_CITY_ID, &city_id, -1);
472 pcity = game_city_by_number(city_id);
473 fc_assert_ret(pcity != NULL);
474 air_text = get_airlift_text(get_units_in_focus(), pcity);
475 gtk_list_store_set(GTK_LIST_STORE(goto_list_store), &iter,
476 GD_COL_AIRLIFT, air_text ? air_text : "-", -1);
477 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(goto_list_store), &iter);
481 /**************************************************************************
482 Refresh the state of the "Airlift" button for the currently selected
483 unit(s) and city.
484 **************************************************************************/
485 static void refresh_airlift_button(void)
487 struct city *pdestcity = get_selected_city();
489 if (NULL != pdestcity) {
490 bool can_airlift = FALSE;
492 /* Allow action if any of the selected units can airlift. */
493 unit_list_iterate(get_units_in_focus(), punit) {
494 if (unit_can_airlift_to(punit, pdestcity)) {
495 can_airlift = TRUE;
496 break;
498 } unit_list_iterate_end;
500 if (can_airlift) {
501 gtk_dialog_set_response_sensitive(GTK_DIALOG(dshell),
502 CMD_AIRLIFT, TRUE);
503 return;
506 gtk_dialog_set_response_sensitive(GTK_DIALOG(dshell), CMD_AIRLIFT, FALSE);
509 /**************************************************************************
510 Update goto dialog. button tells if cities of all players or just
511 client's player should be listed.
512 **************************************************************************/
513 static void goto_selection_callback(GtkTreeSelection *selection,
514 gpointer data)
516 struct city *pdestcity = get_selected_city();
518 if (NULL != pdestcity) {
519 center_tile_mapcanvas(city_tile(pdestcity));
521 refresh_airlift_button();
524 /**************************************************************************
525 Called when the set of units in focus has changed; updates airlift info
526 **************************************************************************/
527 void goto_dialog_focus_units_changed(void)
529 /* Is the dialog currently being displayed? */
530 if (dshell) {
531 /* Location of current units and ability to airlift may have changed */
532 update_source_label();
533 refresh_airlift_column();
534 refresh_airlift_button();