Qt client - remove right click menu from end turn sidebar
[freeciv.git] / client / gui-gtk-2.0 / repodlgs.c
blob773d39afd8916e194c0e8ccb582bf74ef55b3748
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 <math.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
23 #include <gtk/gtk.h>
24 #include <gdk/gdkkeysyms.h>
26 /* utility */
27 #include "fcintl.h"
28 #include "log.h"
29 #include "shared.h"
30 #include "support.h"
32 /* common */
33 #include "fc_types.h" /* LINE_BREAK */
34 #include "game.h"
35 #include "government.h"
36 #include "packets.h"
37 #include "research.h"
38 #include "tech.h"
39 #include "unitlist.h"
41 /* client */
42 #include "chatline_common.h"
43 #include "client_main.h"
44 #include "climisc.h"
45 #include "control.h"
46 #include "mapview_common.h"
47 #include "options.h"
48 #include "packhand_gen.h"
49 #include "control.h"
50 #include "reqtree.h"
51 #include "text.h"
53 /* client/gui-gtk-2.0 */
54 #include "canvas.h"
55 #include "cityrep.h"
56 #include "dialogs.h"
57 #include "gui_main.h"
58 #include "gui_stuff.h"
59 #include "helpdlg.h"
60 #include "plrdlg.h"
62 #include "repodlgs.h"
65 /****************************************************************************
66 RESEARCH REPORT DIALOG
67 ****************************************************************************/
68 struct science_report {
69 struct gui_dialog *shell;
70 GtkComboBox *reachable_techs;
71 GtkComboBox *reachable_goals;
72 GtkWidget *button_show_all;
73 GtkLabel *main_label; /* Gets science_dialog_text(). */
74 GtkProgressBar *progress_bar;
75 GtkLabel *goal_label;
76 GtkLayout *drawing_area;
79 static GtkListStore *science_report_store_new(void);
80 static inline void science_report_store_set(GtkListStore *store,
81 GtkTreeIter *iter,
82 Tech_type_id tech);
83 static bool science_report_combo_get_active(GtkComboBox *combo,
84 Tech_type_id *tech,
85 const char **name);
86 static void science_report_combo_set_active(GtkComboBox *combo,
87 Tech_type_id tech);
88 static gboolean science_diagram_button_release_callback(GtkWidget *widget,
89 GdkEventButton *event,
90 gpointer data);
91 static void science_diagram_update(GtkWidget *widget, gpointer data);
92 static GtkWidget *science_diagram_new(void);
93 static void science_diagram_data(GtkWidget *widget, bool show_all);
94 static void science_diagram_center(GtkWidget *diagram, Tech_type_id tech);
95 static void science_report_redraw(struct science_report *preport);
96 static gint cmp_func(gconstpointer a_p, gconstpointer b_p);
97 static void science_report_update(struct science_report *preport);
98 static void science_report_current_callback(GtkComboBox *combo,
99 gpointer data);
100 static void science_report_show_all_callback(GtkComboBox *combo,
101 gpointer data);
102 static void science_report_goal_callback(GtkComboBox *combo, gpointer data);
103 static void science_report_init(struct science_report *preport);
104 static void science_report_free(struct science_report *preport);
106 static struct science_report science_report = { NULL, };
107 static bool science_report_no_combo_callback = FALSE;
109 /* Those values must match the function science_report_store_new(). */
110 enum science_report_columns {
111 SRD_COL_NAME,
112 SRD_COL_STEPS,
114 /* Not visible. */
115 SRD_COL_ID, /* Tech_type_id */
117 SRD_COL_NUM
120 /****************************************************************************
121 Create a science report list store.
122 ****************************************************************************/
123 static GtkListStore *science_report_store_new(void)
125 return gtk_list_store_new(SRD_COL_NUM,
126 G_TYPE_STRING, /* SRD_COL_NAME */
127 G_TYPE_INT, /* SRD_COL_STEPS */
128 G_TYPE_INT); /* SRD_COL_ID */
131 /****************************************************************************
132 Append a technology to the list store.
133 ****************************************************************************/
134 static inline void science_report_store_set(GtkListStore *store,
135 GtkTreeIter *iter,
136 Tech_type_id tech)
138 const struct research *presearch = research_get(client_player());
140 gtk_list_store_set(store, iter,
141 SRD_COL_NAME,
142 research_advance_name_translation(presearch, tech),
143 SRD_COL_STEPS,
144 research_goal_unknown_techs(presearch, tech),
145 SRD_COL_ID, tech,
146 -1);
149 /****************************************************************************
150 Get the active tech of the combo.
151 ****************************************************************************/
152 static bool science_report_combo_get_active(GtkComboBox *combo,
153 Tech_type_id *tech,
154 const char **name)
156 GtkTreeIter iter;
158 if (science_report_no_combo_callback
159 || !gtk_combo_box_get_active_iter(combo, &iter)) {
160 return FALSE;
163 gtk_tree_model_get(gtk_combo_box_get_model(combo), &iter,
164 SRD_COL_NAME, name,
165 SRD_COL_ID, tech,
166 -1);
167 return TRUE;
170 /****************************************************************************
171 Set the active tech of the combo.
172 ****************************************************************************/
173 static void science_report_combo_set_active(GtkComboBox *combo,
174 Tech_type_id tech)
176 ITree iter;
177 Tech_type_id iter_tech;
179 for (itree_begin(gtk_combo_box_get_model(combo), &iter);
180 !itree_end(&iter); itree_next(&iter)) {
181 itree_get(&iter, SRD_COL_ID, &iter_tech, -1);
182 if (iter_tech == tech) {
183 science_report_no_combo_callback = TRUE;
184 gtk_combo_box_set_active_iter(combo, &iter.it);
185 science_report_no_combo_callback = FALSE;
186 return;
189 log_error("%s(): Tech %d not found in the combo.", __FUNCTION__, tech);
192 /****************************************************************************
193 Change tech goal, research or open help dialog.
194 ****************************************************************************/
195 static gboolean science_diagram_button_release_callback(GtkWidget *widget,
196 GdkEventButton *event, gpointer data)
198 const struct research *presearch = research_get(client_player());
199 struct reqtree *reqtree = g_object_get_data(G_OBJECT(widget), "reqtree");
200 Tech_type_id tech = get_tech_on_reqtree(reqtree, event->x, event->y);
202 if (tech == A_NONE) {
203 return TRUE;
206 if (event->button == 3) {
207 /* RMB: get help */
208 popup_help_dialog_typed(research_advance_name_translation(presearch,
209 tech),
210 HELP_TECH);
211 } else {
212 if (event->button == 1 && can_client_issue_orders()) {
213 /* LMB: set research or research goal */
214 switch (research_invention_state(presearch, tech)) {
215 case TECH_PREREQS_KNOWN:
216 dsend_packet_player_research(&client.conn, tech);
217 break;
218 case TECH_UNKNOWN:
219 dsend_packet_player_tech_goal(&client.conn, tech);
220 break;
221 case TECH_KNOWN:
222 break;
226 return TRUE;
229 /****************************************************************************
230 Draw the invalidated portion of the reqtree.
231 ****************************************************************************/
232 static void science_diagram_update(GtkWidget *widget, gpointer data)
234 /* FIXME: this currently redraws everything! */
235 struct canvas canvas = {
236 .type = CANVAS_PIXMAP,
237 .v.pixmap = GTK_LAYOUT(widget)->bin_window
239 struct reqtree *reqtree = g_object_get_data(G_OBJECT(widget), "reqtree");
240 int width, height;
242 if (!tileset_is_fully_loaded()) {
243 return;
246 get_reqtree_dimensions(reqtree, &width, &height);
247 draw_reqtree(reqtree, &canvas, 0, 0, 0, 0, width, height);
250 /****************************************************************************
251 Return the drawing area widget of new technology diagram. Set in 'x' the
252 position of the current tech to center to it.
253 ****************************************************************************/
254 static GtkWidget *science_diagram_new(void)
256 GtkWidget *diagram;
258 diagram = gtk_layout_new(NULL, NULL);
259 gtk_widget_add_events(diagram,
260 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
261 | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK);
262 g_signal_connect(diagram, "expose-event",
263 G_CALLBACK(science_diagram_update), NULL);
264 g_signal_connect(diagram, "button-release-event",
265 G_CALLBACK(science_diagram_button_release_callback),
266 NULL);
268 return diagram;
271 /****************************************************************************
272 Recreate the req tree.
273 ****************************************************************************/
274 static void science_diagram_data(GtkWidget *widget, bool show_all)
276 struct reqtree *reqtree;
277 int width, height;
279 if (can_conn_edit(&client.conn)) {
280 /* Show all techs in editor mode, not only currently reachable ones */
281 reqtree = create_reqtree(NULL, TRUE);
282 } else {
283 /* Show only at some point reachable techs */
284 reqtree = create_reqtree(client_player(), show_all);
287 get_reqtree_dimensions(reqtree, &width, &height);
288 gtk_layout_set_size(GTK_LAYOUT(widget), width, height);
289 g_object_set_data_full(G_OBJECT(widget), "reqtree", reqtree,
290 (GDestroyNotify) destroy_reqtree);
293 /****************************************************************************
294 Set the diagram parent to point to 'tech' location.
295 ****************************************************************************/
296 static void science_diagram_center(GtkWidget *diagram, Tech_type_id tech)
298 GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(gtk_widget_get_parent(diagram));
299 struct reqtree *reqtree;
300 int x, y, width, height;
302 if (!GTK_IS_SCROLLED_WINDOW(sw)) {
303 return;
306 reqtree = g_object_get_data(G_OBJECT(diagram), "reqtree");
307 get_reqtree_dimensions(reqtree, &width, &height);
308 if (find_tech_on_reqtree(reqtree, tech, &x, &y, NULL, NULL)) {
309 GtkAdjustment *adjust = NULL;
310 gdouble value;
312 adjust = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(sw));
313 value = (adjust->lower + adjust->upper - adjust->page_size) / width * x;
314 gtk_adjustment_set_value(adjust, value);
315 gtk_adjustment_value_changed(adjust);
317 adjust = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw));
318 value = (adjust->lower + adjust->upper - adjust->page_size) / height * y;
319 gtk_adjustment_set_value(adjust, value);
320 gtk_adjustment_value_changed(adjust);
324 /****************************************************************************
325 Resize and redraw the requirement tree.
326 ****************************************************************************/
327 static void science_report_redraw(struct science_report *preport)
329 Tech_type_id researching;
331 fc_assert_ret(NULL != preport);
333 science_diagram_data(GTK_WIDGET(preport->drawing_area),
334 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
335 preport->button_show_all)));
337 if (client_has_player()) {
338 researching = research_get(client_player())->researching;
339 } else {
340 researching = A_UNSET;
342 science_diagram_center(GTK_WIDGET(preport->drawing_area), researching);
344 gtk_widget_queue_draw(GTK_WIDGET(preport->drawing_area));
347 /****************************************************************************
348 Utility for g_list_sort.
349 ****************************************************************************/
350 static gint cmp_func(gconstpointer a_p, gconstpointer b_p)
352 const gchar *a_str, *b_str;
353 gint a = GPOINTER_TO_INT(a_p), b = GPOINTER_TO_INT(b_p);
354 const struct research *presearch = research_get(client_player());
356 a_str = research_advance_name_translation(presearch, a);
357 b_str = research_advance_name_translation(presearch, b);
359 return fc_strcoll(a_str, b_str);
362 /****************************************************************************
363 Update a science report dialog.
364 ****************************************************************************/
365 static void science_report_update(struct science_report *preport)
367 GtkListStore *store;
368 GtkTreeIter iter;
369 GList *sorting_list, *item;
370 struct research *presearch = research_get(client_player());
371 const char *text;
372 double pct;
373 Tech_type_id tech;
375 fc_assert_ret(NULL != preport);
376 fc_assert_ret(NULL != presearch);
378 /* Disable callbacks. */
379 science_report_no_combo_callback = TRUE;
381 gtk_widget_queue_draw(GTK_WIDGET(preport->drawing_area));
383 gtk_label_set_text(preport->main_label, science_dialog_text());
385 /* Update the progress bar. */
386 text = get_science_target_text(&pct);
387 gtk_progress_bar_set_text(preport->progress_bar, text);
388 gtk_progress_bar_set_fraction(preport->progress_bar, pct);
389 /* Work around GTK+ refresh bug? */
390 gtk_widget_queue_resize(GTK_WIDGET(preport->progress_bar));
392 /* Update reachable techs. */
393 store = GTK_LIST_STORE(gtk_combo_box_get_model(preport->reachable_techs));
394 gtk_list_store_clear(store);
395 sorting_list = NULL;
396 if (A_UNSET == presearch->researching
397 || is_future_tech(presearch->researching)) {
398 gtk_list_store_append(store, &iter);
399 science_report_store_set(store, &iter, presearch->researching);
400 gtk_combo_box_set_active_iter(preport->reachable_techs, &iter);
403 /* Collect all techs which are reachable in the next step. */
404 advance_index_iterate(A_FIRST, i) {
405 if (TECH_PREREQS_KNOWN == presearch->inventions[i].state) {
406 sorting_list = g_list_prepend(sorting_list, GINT_TO_POINTER(i));
408 } advance_index_iterate_end;
410 /* Sort the list, append it to the store. */
411 sorting_list = g_list_sort(sorting_list, cmp_func);
412 for (item = sorting_list; NULL != item; item = g_list_next(item)) {
413 tech = GPOINTER_TO_INT(item->data);
414 gtk_list_store_append(store, &iter);
415 science_report_store_set(store, &iter, tech);
416 if (tech == presearch->researching) {
417 gtk_combo_box_set_active_iter(preport->reachable_techs, &iter);
421 /* Free, re-init. */
422 g_list_free(sorting_list);
423 sorting_list = NULL;
424 store = GTK_LIST_STORE(gtk_combo_box_get_model(preport->reachable_goals));
425 gtk_list_store_clear(store);
427 /* Update the tech goal. */
428 gtk_label_set_text(preport->goal_label,
429 get_science_goal_text(presearch->tech_goal));
431 if (A_UNSET == presearch->tech_goal) {
432 gtk_list_store_append(store, &iter);
433 science_report_store_set(store, &iter, A_UNSET);
434 gtk_combo_box_set_active_iter(preport->reachable_goals, &iter);
437 /* Collect all techs which are reachable in next 10 steps. */
438 advance_index_iterate(A_FIRST, i) {
439 if (research_invention_reachable(presearch, i)
440 && TECH_KNOWN != presearch->inventions[i].state
441 && (i == presearch->tech_goal
442 || 10 >= presearch->inventions[i].num_required_techs)) {
443 sorting_list = g_list_prepend(sorting_list, GINT_TO_POINTER(i));
445 } advance_index_iterate_end;
447 /* Sort the list, append it to the store. */
448 sorting_list = g_list_sort(sorting_list, cmp_func);
449 for (item = sorting_list; NULL != item; item = g_list_next(item)) {
450 tech = GPOINTER_TO_INT(item->data);
451 gtk_list_store_append(store, &iter);
452 science_report_store_set(store, &iter, tech);
453 if (tech == presearch->tech_goal) {
454 gtk_combo_box_set_active_iter(preport->reachable_goals, &iter);
458 /* Free. */
459 g_list_free(sorting_list);
461 /* Re-enable callbacks. */
462 science_report_no_combo_callback = FALSE;
465 /****************************************************************************
466 Actived item in the reachable techs combo box.
467 ****************************************************************************/
468 static void science_report_current_callback(GtkComboBox *combo,
469 gpointer data)
471 Tech_type_id tech;
472 const char *tech_name;
474 if (!science_report_combo_get_active(combo, &tech, &tech_name)) {
475 return;
478 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data))) {
479 popup_help_dialog_typed(tech_name, HELP_TECH);
480 } else if (can_client_issue_orders()) {
481 dsend_packet_player_research(&client.conn, tech);
483 /* Revert, or we will be not synchron with the server. */
484 science_report_combo_set_active(combo, research_get
485 (client_player())->researching);
488 /****************************************************************************
489 Show or hide unreachable techs.
490 ****************************************************************************/
491 static void science_report_show_all_callback(GtkComboBox *combo,
492 gpointer data)
494 struct science_report *preport = (struct science_report *) data;
496 science_report_redraw(preport);
499 /****************************************************************************
500 Actived item in the reachable goals combo box.
501 ****************************************************************************/
502 static void science_report_goal_callback(GtkComboBox *combo, gpointer data)
504 Tech_type_id tech;
505 const char *tech_name;
507 if (!science_report_combo_get_active(combo, &tech, &tech_name)) {
508 return;
511 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data))) {
512 popup_help_dialog_typed(tech_name, HELP_TECH);
513 } else if (can_client_issue_orders()) {
514 dsend_packet_player_tech_goal(&client.conn, tech);
516 /* Revert, or we will be not synchron with the server. */
517 science_report_combo_set_active(combo, research_get
518 (client_player())->tech_goal);
521 /****************************************************************************
522 Initialize a science report.
523 ****************************************************************************/
524 static void science_report_init(struct science_report *preport)
526 GtkWidget *frame, *table, *help_button, *show_all_button, *sw, *w;
527 GtkBox *vbox;
528 GtkListStore *store;
529 GtkCellRenderer *renderer;
531 fc_assert_ret(NULL != preport);
533 gui_dialog_new(&preport->shell, GTK_NOTEBOOK(top_notebook), NULL, TRUE);
534 /* TRANS: Research report title */
535 gui_dialog_set_title(preport->shell, _("Research"));
537 gui_dialog_add_button(preport->shell, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
538 gui_dialog_set_default_response(preport->shell, GTK_RESPONSE_CLOSE);
540 vbox = GTK_BOX(preport->shell->vbox);
542 w = gtk_label_new(NULL);
543 gtk_box_pack_start(vbox, w, FALSE, FALSE, 0);
544 preport->main_label = GTK_LABEL(w);
546 /* Current research target line. */
547 frame = gtk_frame_new(_("Researching"));
548 gtk_box_pack_start(vbox, frame, FALSE, FALSE, 0);
550 table = gtk_table_new(1, 6, TRUE);
551 gtk_table_set_col_spacings(GTK_TABLE(table), 4);
552 gtk_container_add(GTK_CONTAINER(frame), table);
554 help_button = gtk_check_button_new_with_label(_("Help"));
555 gtk_table_attach(GTK_TABLE(table), help_button, 5, 6, 0, 1, 0, 0, 0, 0);
557 store = science_report_store_new();
558 w = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
559 g_object_unref(G_OBJECT(store));
560 renderer = gtk_cell_renderer_text_new();
561 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w), renderer, TRUE);
562 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), renderer, "text",
563 SRD_COL_NAME, NULL);
564 gtk_widget_set_sensitive(w, can_client_issue_orders());
565 g_signal_connect(w, "changed", G_CALLBACK(science_report_current_callback),
566 help_button);
567 gtk_table_attach_defaults(GTK_TABLE(table), w, 0, 2, 0, 1);
568 preport->reachable_techs = GTK_COMBO_BOX(w);
570 w = gtk_progress_bar_new();
571 gtk_table_attach_defaults(GTK_TABLE(table), w, 2, 5, 0, 1);
572 gtk_widget_set_size_request(w, -1, 25);
573 preport->progress_bar = GTK_PROGRESS_BAR(w);
575 /* Research goal line. */
576 frame = gtk_frame_new( _("Goal"));
577 gtk_box_pack_start(vbox, frame, FALSE, FALSE, 0);
579 table = gtk_table_new(1, 6, TRUE);
580 gtk_table_set_col_spacings(GTK_TABLE(table), 4);
581 gtk_container_add(GTK_CONTAINER(frame),table);
583 store = science_report_store_new();
584 w = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
585 g_object_unref(G_OBJECT(store));
586 renderer = gtk_cell_renderer_text_new();
587 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w), renderer, TRUE);
588 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), renderer, "text",
589 SRD_COL_NAME, NULL);
590 renderer = gtk_cell_renderer_text_new();
591 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(w), renderer, FALSE);
592 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), renderer, "text",
593 SRD_COL_STEPS, NULL);
594 gtk_widget_set_sensitive(w, can_client_issue_orders());
595 g_signal_connect(w, "changed", G_CALLBACK(science_report_goal_callback),
596 help_button);
597 gtk_table_attach_defaults(GTK_TABLE(table), w, 0, 2, 0, 1);
598 preport->reachable_goals = GTK_COMBO_BOX(w);
600 w = gtk_label_new(NULL);
601 gtk_table_attach_defaults(GTK_TABLE(table), w, 2, 5, 0, 1);
602 gtk_widget_set_size_request(w, -1, 25);
603 preport->goal_label = GTK_LABEL(w);
605 /* Toggle unreachable button. */
606 /* TRANS: As in 'Show all (even not reachable) techs'. */
607 show_all_button = gtk_toggle_button_new_with_label(_("Show all"));
608 gtk_table_attach(GTK_TABLE(table), show_all_button, 5, 6, 0, 1, 0, 0, 0,
610 g_signal_connect(show_all_button, "toggled",
611 G_CALLBACK(science_report_show_all_callback), preport);
612 gtk_widget_set_sensitive(show_all_button, can_client_issue_orders()
613 && !client_is_global_observer());
614 preport->button_show_all = show_all_button;
616 /* Science diagram. */
617 sw = gtk_scrolled_window_new(NULL, NULL);
618 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
619 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
620 gtk_box_pack_start(vbox, sw, TRUE, TRUE, 0);
622 w = science_diagram_new();
623 gtk_container_add(GTK_CONTAINER(sw), w);
624 preport->drawing_area = GTK_LAYOUT(w);
626 science_report_update(preport);
627 gui_dialog_show_all(preport->shell);
629 /* This must be _after_ the dialog is drawn to really center it ... */
630 science_report_redraw(preport);
633 /****************************************************************************
634 Free a science report.
635 ****************************************************************************/
636 static void science_report_free(struct science_report *preport)
638 fc_assert_ret(NULL != preport);
640 gui_dialog_destroy(preport->shell);
641 fc_assert(NULL == preport->shell);
643 memset(preport, 0, sizeof(*preport));
646 /****************************************************************************
647 Create the science report is needed.
648 ****************************************************************************/
649 void science_report_dialog_popup(bool raise)
651 struct research *presearch = research_get(client_player());
653 if (NULL == science_report.shell) {
654 science_report_init(&science_report);
657 if (NULL != presearch
658 && A_UNSET == presearch->tech_goal
659 && A_UNSET == presearch->researching) {
660 gui_dialog_alert(science_report.shell);
661 } else {
662 gui_dialog_present(science_report.shell);
665 if (raise) {
666 gui_dialog_raise(science_report.shell);
670 /****************************************************************************
671 Closes the science report dialog.
672 ****************************************************************************/
673 void science_report_dialog_popdown(void)
675 if (NULL != science_report.shell) {
676 science_report_free(&science_report);
677 fc_assert(NULL == science_report.shell);
681 /****************************************************************************
682 Update the science report dialog.
683 ****************************************************************************/
684 void real_science_report_dialog_update(void)
686 if (NULL != science_report.shell) {
687 science_report_update(&science_report);
691 /****************************************************************************
692 Resize and redraw the requirement tree.
693 ****************************************************************************/
694 void science_report_dialog_redraw(void)
696 if (NULL != science_report.shell) {
697 science_report_redraw(&science_report);
702 /****************************************************************************
703 ECONOMY REPORT DIALOG
704 ****************************************************************************/
705 struct economy_report {
706 struct gui_dialog *shell;
707 GtkTreeView *tree_view;
708 GtkLabel *label;
711 static struct economy_report economy_report = { NULL, };
713 enum economy_report_response {
714 ERD_RES_SELL_REDUNDANT = 1,
715 ERD_RES_SELL_ALL,
716 ERD_RES_DISBAND_UNITS
719 /* Those values must match the functions economy_report_store_new() and
720 * economy_report_column_name(). */
721 enum economy_report_columns {
722 ERD_COL_SPRITE,
723 ERD_COL_NAME,
724 ERD_COL_REDUNDANT,
725 ERD_COL_COUNT,
726 ERD_COL_COST,
727 ERD_COL_TOTAL_COST,
729 /* Not visible. */
730 ERD_COL_IS_IMPROVEMENT,
731 ERD_COL_CID,
733 ERD_COL_NUM
736 /****************************************************************************
737 Create a new economy report list store.
738 ****************************************************************************/
739 static GtkListStore *economy_report_store_new(void)
741 return gtk_list_store_new(ERD_COL_NUM,
742 GDK_TYPE_PIXBUF, /* ERD_COL_SPRITE */
743 G_TYPE_STRING, /* ERD_COL_NAME */
744 G_TYPE_INT, /* ERD_COL_REDUNDANT */
745 G_TYPE_INT, /* ERD_COL_COUNT */
746 G_TYPE_INT, /* ERD_COL_COST */
747 G_TYPE_INT, /* ERD_COL_TOTAL_COST */
748 G_TYPE_BOOLEAN, /* ERD_COL_IS_IMPROVEMENT */
749 G_TYPE_INT, /* ERD_COL_UNI_KIND */
750 G_TYPE_INT); /* ERD_COL_UNI_VALUE_ID */
753 /****************************************************************************
754 Returns the title of the column (translated).
755 ****************************************************************************/
756 static const char *
757 economy_report_column_name(enum economy_report_columns col)
759 switch (col) {
760 case ERD_COL_SPRITE:
761 /* TRANS: Image header */
762 return _("Type");
763 case ERD_COL_NAME:
764 return Q_("?Building or Unit type:Name");
765 case ERD_COL_REDUNDANT:
766 return _("Redundant");
767 case ERD_COL_COUNT:
768 return _("Count");
769 case ERD_COL_COST:
770 return _("Cost");
771 case ERD_COL_TOTAL_COST:
772 /* TRANS: Upkeep total, count*cost. */
773 return _("U Total");
774 case ERD_COL_IS_IMPROVEMENT:
775 case ERD_COL_CID:
776 case ERD_COL_NUM:
777 break;
780 return NULL;
783 /****************************************************************************
784 Update the economy report dialog.
785 ****************************************************************************/
786 static void economy_report_update(struct economy_report *preport)
788 GtkTreeSelection *selection;
789 GtkTreeModel *model;
790 GtkListStore *store;
791 GtkTreeIter iter;
792 struct improvement_entry building_entries[B_LAST];
793 struct unit_entry unit_entries[U_LAST];
794 int entries_used, building_total, unit_total, tax, i;
795 char buf[256];
796 cid selected;
798 fc_assert_ret(NULL != preport);
800 /* Save the selection. */
801 selection = gtk_tree_view_get_selection(preport->tree_view);
802 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
803 gtk_tree_model_get(model, &iter, ERD_COL_CID, &selected, -1);
804 } else {
805 selected = -1;
808 model = gtk_tree_view_get_model(preport->tree_view);
809 store = GTK_LIST_STORE(model);
810 gtk_list_store_clear(store);
812 /* Buildings. */
813 get_economy_report_data(building_entries, &entries_used,
814 &building_total, &tax);
815 for (i = 0; i < entries_used; i++) {
816 struct improvement_entry *pentry = building_entries + i;
817 struct impr_type *pimprove = pentry->type;
818 struct sprite *sprite = get_building_sprite(tileset, pimprove);
819 cid id = cid_encode_building(pimprove);
821 gtk_list_store_append(store, &iter);
822 gtk_list_store_set(store, &iter,
823 ERD_COL_SPRITE, sprite_get_pixbuf(sprite),
824 ERD_COL_NAME, improvement_name_translation(pimprove),
825 ERD_COL_REDUNDANT, pentry->redundant,
826 ERD_COL_COUNT, pentry->count,
827 ERD_COL_COST, pentry->cost,
828 ERD_COL_TOTAL_COST, pentry->total_cost,
829 ERD_COL_IS_IMPROVEMENT, TRUE,
830 ERD_COL_CID, id,
831 -1);
832 if (selected == id) {
833 /* Restore the selection. */
834 gtk_tree_selection_select_iter(selection, &iter);
838 /* Units. */
839 get_economy_report_units_data(unit_entries, &entries_used, &unit_total);
840 for (i = 0; i < entries_used; i++) {
841 struct unit_entry *pentry = unit_entries + i;
842 struct unit_type *putype = pentry->type;
843 struct sprite *sprite = get_unittype_sprite(tileset, putype,
844 direction8_invalid(), TRUE);
845 cid id = cid_encode_unit(putype);
847 gtk_list_store_append(store, &iter);
848 gtk_list_store_set(store, &iter,
849 ERD_COL_SPRITE, sprite_get_pixbuf(sprite),
850 ERD_COL_NAME, utype_name_translation(putype),
851 ERD_COL_REDUNDANT, 0,
852 ERD_COL_COUNT, pentry->count,
853 ERD_COL_COST, pentry->cost,
854 ERD_COL_TOTAL_COST, pentry->total_cost,
855 ERD_COL_IS_IMPROVEMENT, FALSE,
856 ERD_COL_CID, id,
857 -1);
858 if (selected == id) {
859 /* Restore the selection. */
860 gtk_tree_selection_select_iter(selection, &iter);
864 /* Update the label. */
865 fc_snprintf(buf, sizeof(buf), _("Income: %d Total Costs: %d"),
866 tax, building_total + unit_total);
867 gtk_label_set_text(preport->label, buf);
870 /****************************************************************************
871 Issue a command on the economy report.
872 ****************************************************************************/
873 static void economy_report_command_callback(struct gui_dialog *pdialog,
874 int response,
875 gpointer data)
877 struct economy_report *preport = data;
878 GtkTreeSelection *selection = gtk_tree_view_get_selection(preport->tree_view);
879 GtkTreeModel *model;
880 GtkTreeIter iter;
881 GtkWidget *shell;
882 struct universal selected;
883 cid id;
884 char buf[256] = "";
886 switch (response) {
887 case ERD_RES_SELL_REDUNDANT:
888 case ERD_RES_SELL_ALL:
889 case ERD_RES_DISBAND_UNITS:
890 break;
891 default:
892 gui_dialog_destroy(pdialog);
893 return;
896 if (!can_client_issue_orders()
897 || !gtk_tree_selection_get_selected(selection, &model, &iter)) {
898 return;
901 gtk_tree_model_get(model, &iter, ERD_COL_CID, &id, -1);
902 selected = cid_decode(id);
904 switch (selected.kind) {
905 case VUT_IMPROVEMENT:
907 struct impr_type *pimprove = selected.value.building;
909 if (can_sell_building(pimprove)
910 && (ERD_RES_SELL_ALL == response
911 || (ERD_RES_SELL_REDUNDANT == response))) {
912 bool redundant = (ERD_RES_SELL_REDUNDANT == response);
913 gint count;
914 gtk_tree_model_get(model, &iter,
915 redundant ? ERD_COL_REDUNDANT : ERD_COL_COUNT,
916 &count, -1);
917 if (count == 0) {
918 break;
920 shell = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL
921 | GTK_DIALOG_DESTROY_WITH_PARENT,
922 GTK_MESSAGE_QUESTION,
923 GTK_BUTTONS_YES_NO,
924 redundant
925 /* TRANS: %s is an improvement */
926 ? _("Do you really wish to sell "
927 "every redundant %s (%d total)?")
928 /* TRANS: %s is an improvement */
929 : _("Do you really wish to sell "
930 "every %s (%d total)?"),
931 improvement_name_translation(pimprove),
932 count);
933 setup_dialog(shell, gui_dialog_get_toplevel(pdialog));
934 gtk_window_set_title(GTK_WINDOW(shell), _("Sell Improvements"));
936 if (GTK_RESPONSE_YES == gtk_dialog_run(GTK_DIALOG(shell))) {
937 sell_all_improvements(pimprove, redundant, buf, sizeof(buf));
939 gtk_widget_destroy(shell);
942 break;
943 case VUT_UTYPE:
945 if (ERD_RES_DISBAND_UNITS == response) {
946 struct unit_type *putype = selected.value.utype;
947 gint count;
948 gtk_tree_model_get(model, &iter, ERD_COL_COUNT, &count, -1);
950 shell = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL
951 | GTK_DIALOG_DESTROY_WITH_PARENT,
952 GTK_MESSAGE_QUESTION,
953 GTK_BUTTONS_YES_NO,
954 /* TRANS: %s is a unit */
955 _("Do you really wish to disband "
956 "every %s (%d total)?"),
957 utype_name_translation(putype),
958 count);
959 setup_dialog(shell, gui_dialog_get_toplevel(pdialog));
960 gtk_window_set_title(GTK_WINDOW(shell), _("Disband Units"));
962 if (GTK_RESPONSE_YES == gtk_dialog_run(GTK_DIALOG(shell))) {
963 disband_all_units(putype, FALSE, buf, sizeof(buf));
965 gtk_widget_destroy(shell);
968 break;
969 default:
970 log_error("Not supported type: %d.", selected.kind);
973 if ('\0' != buf[0]) {
974 shell = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
975 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
976 "%s", buf);
977 setup_dialog(shell, gui_dialog_get_toplevel(pdialog));
978 g_signal_connect(shell, "response", G_CALLBACK(gtk_widget_destroy),
979 NULL);
980 gtk_window_set_title(GTK_WINDOW(shell), _("Sell-Off: Results"));
981 gtk_window_present(GTK_WINDOW(shell));
985 /****************************************************************************
986 Called when a building or a unit type is selected in the economy list.
987 ****************************************************************************/
988 static void economy_report_selection_callback(GtkTreeSelection *selection,
989 gpointer data)
991 struct gui_dialog *pdialog = ((struct economy_report *)data)->shell;
992 GtkTreeModel *model;
993 GtkTreeIter iter;
995 if (can_client_issue_orders()
996 && gtk_tree_selection_get_selected(selection, &model, &iter)) {
997 struct universal selected;
998 cid id;
1000 gtk_tree_model_get(model, &iter, ERD_COL_CID, &id, -1);
1001 selected = cid_decode(id);
1002 switch (selected.kind) {
1003 case VUT_IMPROVEMENT:
1005 bool can_sell = can_sell_building(selected.value.building);
1006 gint redundant;
1007 gtk_tree_model_get(model, &iter, ERD_COL_REDUNDANT, &redundant, -1);
1009 gui_dialog_set_response_sensitive(pdialog, ERD_RES_SELL_REDUNDANT,
1010 can_sell && redundant > 0);
1011 gui_dialog_set_response_sensitive(pdialog, ERD_RES_SELL_ALL, can_sell);
1012 gui_dialog_set_response_sensitive(pdialog, ERD_RES_DISBAND_UNITS,
1013 FALSE);
1015 return;
1016 case VUT_UTYPE:
1017 gui_dialog_set_response_sensitive(pdialog, ERD_RES_SELL_REDUNDANT,
1018 FALSE);
1019 gui_dialog_set_response_sensitive(pdialog, ERD_RES_SELL_ALL, FALSE);
1020 gui_dialog_set_response_sensitive(pdialog, ERD_RES_DISBAND_UNITS,
1021 TRUE);
1022 return;
1023 default:
1024 log_error("Not supported type: %d.", selected.kind);
1025 break;
1029 gui_dialog_set_response_sensitive(pdialog, ERD_RES_SELL_REDUNDANT, FALSE);
1030 gui_dialog_set_response_sensitive(pdialog, ERD_RES_SELL_ALL, FALSE);
1031 gui_dialog_set_response_sensitive(pdialog, ERD_RES_DISBAND_UNITS, FALSE);
1034 /****************************************************************************
1035 Create a new economy report.
1036 ****************************************************************************/
1037 static void economy_report_init(struct economy_report *preport)
1039 GtkWidget *view, *sw, *align, *label, *button;
1040 GtkListStore *store;
1041 GtkTreeSelection *selection;
1042 GtkBox *vbox;
1043 const char *title;
1044 enum economy_report_columns i;
1046 fc_assert_ret(NULL != preport);
1048 gui_dialog_new(&preport->shell, GTK_NOTEBOOK(top_notebook), preport, TRUE);
1049 gui_dialog_set_title(preport->shell, _("Economy"));
1050 vbox = GTK_BOX(preport->shell->vbox);
1052 align = gtk_alignment_new(0.5, 0.0, 0.0, 1.0);
1053 gtk_box_pack_start(vbox, align, TRUE, TRUE, 0);
1055 sw = gtk_scrolled_window_new(NULL, NULL);
1056 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1057 GTK_SHADOW_ETCHED_IN);
1058 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1059 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1060 gtk_container_add(GTK_CONTAINER(align), sw);
1062 store = economy_report_store_new();
1063 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1064 g_object_unref(store);
1065 gtk_widget_set_name(view, "small_font");
1066 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(view));
1067 gtk_container_add(GTK_CONTAINER(sw), view);
1068 preport->tree_view = GTK_TREE_VIEW(view);
1070 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1071 g_signal_connect(selection, "changed",
1072 G_CALLBACK(economy_report_selection_callback), preport);
1074 for (i = 0; (title = economy_report_column_name(i)); i++) {
1075 GtkCellRenderer *renderer;
1076 GtkTreeViewColumn *col;
1077 GType type = gtk_tree_model_get_column_type(GTK_TREE_MODEL(store), i);
1079 if (GDK_TYPE_PIXBUF == type) {
1080 renderer = gtk_cell_renderer_pixbuf_new();
1081 col = gtk_tree_view_column_new_with_attributes(title, renderer,
1082 "pixbuf", i, NULL);
1083 #if 0
1084 } else if (G_TYPE_BOOLEAN == type) {
1085 renderer = gtk_cell_renderer_toggle_new();
1086 col = gtk_tree_view_column_new_with_attributes(title, renderer,
1087 "active", i, NULL);
1088 #endif
1089 } else {
1090 bool is_redundant = (i == ERD_COL_REDUNDANT);
1091 renderer = gtk_cell_renderer_text_new();
1092 if (is_redundant) {
1093 /* Special treatment: hide "Redundant" column for units */
1094 col = gtk_tree_view_column_new_with_attributes(title, renderer,
1095 "text", i,
1096 "visible",
1097 ERD_COL_IS_IMPROVEMENT,
1098 NULL);
1099 } else {
1100 col = gtk_tree_view_column_new_with_attributes(title, renderer,
1101 "text", i, NULL);
1105 if (i > 1) {
1106 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1107 gtk_tree_view_column_set_alignment(col, 1.0);
1110 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
1113 label = gtk_label_new(NULL);
1114 gtk_box_pack_start(vbox, label, FALSE, FALSE, 0);
1115 gtk_misc_set_padding(GTK_MISC(label), 5, 5);
1116 preport->label = GTK_LABEL(label);
1118 button = gui_dialog_add_button(preport->shell, _("Sell _Redundant"),
1119 ERD_RES_SELL_REDUNDANT);
1120 gtk_widget_set_sensitive(button, FALSE);
1122 button = gui_dialog_add_button(preport->shell, _("Sell _All"),
1123 ERD_RES_SELL_ALL);
1124 gtk_widget_set_sensitive(button, FALSE);
1126 button = gui_dialog_add_button(preport->shell, _("_Disband"),
1127 ERD_RES_DISBAND_UNITS);
1128 gtk_widget_set_sensitive(button, FALSE);
1130 gui_dialog_add_button(preport->shell, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
1132 gui_dialog_set_default_response(preport->shell, GTK_RESPONSE_CLOSE);
1133 gui_dialog_response_set_callback(preport->shell,
1134 economy_report_command_callback);
1136 gui_dialog_set_default_size(preport->shell, -1, 350);
1137 gui_dialog_show_all(preport->shell);
1139 economy_report_update(preport);
1141 gtk_tree_view_focus(GTK_TREE_VIEW(view));
1144 /****************************************************************************
1145 Free an economy report.
1146 ****************************************************************************/
1147 static void economy_report_free(struct economy_report *preport)
1149 fc_assert_ret(NULL != preport);
1151 gui_dialog_destroy(preport->shell);
1152 fc_assert(NULL == preport->shell);
1154 memset(preport, 0, sizeof(*preport));
1157 /****************************************************************************
1158 Create the economy report if needed.
1159 ****************************************************************************/
1160 void economy_report_dialog_popup(bool raise)
1162 if (NULL == economy_report.shell) {
1163 economy_report_init(&economy_report);
1166 gui_dialog_present(economy_report.shell);
1167 if (raise) {
1168 gui_dialog_raise(economy_report.shell);
1172 /****************************************************************************
1173 Close the economy report dialog.
1174 ****************************************************************************/
1175 void economy_report_dialog_popdown(void)
1177 if (NULL != economy_report.shell) {
1178 economy_report_free(&economy_report);
1182 /****************************************************************************
1183 Update the economy report dialog.
1184 ****************************************************************************/
1185 void real_economy_report_dialog_update(void)
1187 if (NULL != economy_report.shell) {
1188 economy_report_update(&economy_report);
1193 /****************************************************************************
1194 UNITS REPORT DIALOG
1195 ****************************************************************************/
1196 struct units_report {
1197 struct gui_dialog *shell;
1198 GtkTreeView *tree_view;
1201 static struct units_report units_report = { NULL, };
1203 enum units_report_response {
1204 URD_RES_NEAREST = 1,
1205 URD_RES_UPGRADE
1208 /* Those values must match the order of unit_report_columns[]. */
1209 enum units_report_columns {
1210 URD_COL_UTYPE_NAME,
1211 URD_COL_UPGRADABLE,
1212 URD_COL_N_UPGRADABLE,
1213 URD_COL_IN_PROGRESS,
1214 URD_COL_ACTIVE,
1215 URD_COL_SHIELD,
1216 URD_COL_FOOD,
1217 URD_COL_GOLD,
1219 /* Not visible. */
1220 URD_COL_TEXT_WEIGHT,
1221 URD_COL_UPG_VISIBLE,
1222 URD_COL_NUPG_VISIBLE,
1223 URD_COL_UTYPE_ID,
1225 URD_COL_NUM
1228 static const struct {
1229 GType type;
1230 const char *title;
1231 const char *tooltip;
1232 bool rightalign;
1233 int visible_col;
1234 } unit_report_columns[] = {
1235 { /* URD_COL_UTYPE_NAME */ G_TYPE_STRING, N_("Unit Type"),
1236 NULL, FALSE, -1 },
1237 { /* URD_COL_UPGRADABLE */ G_TYPE_BOOLEAN, N_("?Upgradable unit [short]:U"),
1238 N_("Upgradable"), TRUE, URD_COL_UPG_VISIBLE },
1239 { /* URD_COL_N_UPGRADABLE */ G_TYPE_INT, "" /* merge with previous col */,
1240 NULL, TRUE, URD_COL_NUPG_VISIBLE },
1241 /* TRANS: "In progress" abbreviation. */
1242 { /* URD_COL_IN_PROGRESS */ G_TYPE_INT, N_("In-Prog"),
1243 N_("In progress"), TRUE, -1 },
1244 { /* URD_COL_ACTIVE */ G_TYPE_INT, N_("Active"),
1245 NULL, TRUE, -1 },
1246 { /* URD_COL_SHIELD */ G_TYPE_INT, N_("Shield"),
1247 N_("Total shield upkeep"), TRUE, -1 },
1248 { /* URD_COL_FOOD */ G_TYPE_INT, N_("Food"),
1249 N_("Total food upkeep"), TRUE, -1 },
1250 { /* URD_COL_GOLD */ G_TYPE_INT, N_("Gold"),
1251 N_("Total gold upkeep"), TRUE, -1 },
1252 /* Columns in the model but not the view: */
1253 { /* URD_COL_TEXT_WEIGHT */ G_TYPE_INT, NULL /* ... */ },
1254 { /* URD_COL_UPG_VISIBLE */ G_TYPE_BOOLEAN, NULL /* ... */ },
1255 { /* URD_COL_NUPG_VISIBLE */ G_TYPE_BOOLEAN, NULL /* ... */ },
1256 { /* URD_COL_UTYPE_ID */ G_TYPE_INT, NULL /* ... */ }
1259 /****************************************************************************
1260 Create a new units report list store.
1261 ****************************************************************************/
1262 static GtkListStore *units_report_store_new(void)
1264 int i;
1265 GType cols[URD_COL_NUM];
1266 fc_assert(ARRAY_SIZE(unit_report_columns) == URD_COL_NUM);
1268 for (i=0; i<URD_COL_NUM; i++) {
1269 cols[i] = unit_report_columns[i].type;
1272 return gtk_list_store_newv(URD_COL_NUM, cols);
1275 /****************************************************************************
1276 Update the units report.
1277 ****************************************************************************/
1278 static void units_report_update(struct units_report *preport)
1280 struct urd_info {
1281 int active_count;
1282 int building_count;
1283 int upkeep[O_LAST];
1286 struct urd_info unit_array[utype_count()];
1287 struct urd_info unit_totals;
1288 struct urd_info *info;
1289 int total_upgradable_count = 0;
1290 GtkTreeSelection *selection;
1291 GtkTreeModel *model;
1292 GtkListStore *store;
1293 GtkTreeIter iter;
1294 Unit_type_id selected, utype_id;
1296 fc_assert_ret(NULL != preport);
1298 memset(unit_array, '\0', sizeof(unit_array));
1299 memset(&unit_totals, '\0', sizeof(unit_totals));
1301 /* Count units. */
1302 players_iterate(pplayer) {
1303 if (client_has_player() && pplayer != client_player()) {
1304 continue;
1307 unit_list_iterate(pplayer->units, punit) {
1308 info = unit_array + utype_index(unit_type_get(punit));
1310 if (0 != punit->homecity) {
1311 output_type_iterate(o) {
1312 info->upkeep[o] += punit->upkeep[o];
1313 } output_type_iterate_end;
1315 info->active_count++;
1316 } unit_list_iterate_end;
1317 city_list_iterate(pplayer->cities, pcity) {
1318 if (VUT_UTYPE == pcity->production.kind) {
1319 int num_units;
1320 info = unit_array + utype_index(pcity->production.value.utype);
1321 /* Account for build slots in city */
1322 (void) city_production_build_units(pcity, TRUE, &num_units);
1323 /* Unit is in progress even if it won't be done this turn */
1324 num_units = MAX(num_units, 1);
1325 info->building_count += num_units;
1327 } city_list_iterate_end;
1328 } players_iterate_end;
1330 /* Save selection. */
1331 selection = gtk_tree_view_get_selection(preport->tree_view);
1332 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
1333 gtk_tree_model_get(model, &iter, URD_COL_UTYPE_ID, &selected, -1);
1334 } else {
1335 selected = -1;
1338 /* Make the store. */
1339 model = gtk_tree_view_get_model(preport->tree_view);
1340 store = GTK_LIST_STORE(model);
1341 gtk_list_store_clear(store);
1343 unit_type_iterate(utype) {
1344 bool upgradable;
1346 utype_id = utype_index(utype);
1347 info = unit_array + utype_id;
1349 if (0 == info->active_count && 0 == info->building_count) {
1350 continue; /* We don't need a row for this type. */
1353 upgradable = client_has_player()
1354 && NULL != can_upgrade_unittype(client_player(), utype);
1356 gtk_list_store_append(store, &iter);
1357 gtk_list_store_set(store, &iter,
1358 URD_COL_UTYPE_NAME, utype_name_translation(utype),
1359 URD_COL_UPGRADABLE, upgradable,
1360 URD_COL_N_UPGRADABLE, 0, /* never displayed */
1361 URD_COL_IN_PROGRESS, info->building_count,
1362 URD_COL_ACTIVE, info->active_count,
1363 URD_COL_SHIELD, info->upkeep[O_SHIELD],
1364 URD_COL_FOOD, info->upkeep[O_FOOD],
1365 URD_COL_GOLD, info->upkeep[O_GOLD],
1366 URD_COL_TEXT_WEIGHT, PANGO_WEIGHT_NORMAL,
1367 URD_COL_UPG_VISIBLE, TRUE,
1368 URD_COL_NUPG_VISIBLE, FALSE,
1369 URD_COL_UTYPE_ID, utype_id,
1370 -1);
1371 if (selected == utype_id) {
1372 /* Restore the selection. */
1373 gtk_tree_selection_select_iter(selection, &iter);
1376 /* Update totals. */
1377 unit_totals.active_count += info->active_count;
1378 output_type_iterate(o) {
1379 unit_totals.upkeep[o] += info->upkeep[o];
1380 } output_type_iterate_end;
1381 unit_totals.building_count += info->building_count;
1382 if (upgradable) {
1383 total_upgradable_count += info->active_count;
1385 } unit_type_iterate_end;
1387 /* Add the total row. */
1388 gtk_list_store_append(store, &iter);
1389 gtk_list_store_set(store, &iter,
1390 URD_COL_UTYPE_NAME, _("Totals:"),
1391 URD_COL_UPGRADABLE, FALSE, /* never displayed */
1392 URD_COL_N_UPGRADABLE, total_upgradable_count,
1393 URD_COL_IN_PROGRESS, unit_totals.building_count,
1394 URD_COL_ACTIVE, unit_totals.active_count,
1395 URD_COL_SHIELD, unit_totals.upkeep[O_SHIELD],
1396 URD_COL_FOOD, unit_totals.upkeep[O_FOOD],
1397 URD_COL_GOLD, unit_totals.upkeep[O_GOLD],
1398 URD_COL_TEXT_WEIGHT, PANGO_WEIGHT_BOLD,
1399 URD_COL_UPG_VISIBLE, FALSE,
1400 URD_COL_NUPG_VISIBLE, TRUE,
1401 URD_COL_UTYPE_ID, U_LAST,
1402 -1);
1403 if (selected == U_LAST) {
1404 /* Restore the selection. */
1405 gtk_tree_selection_select_iter(selection, &iter);
1409 /****************************************************************************
1410 GtkTreeSelection "changed" signal handler.
1411 ****************************************************************************/
1412 static void units_report_selection_callback(GtkTreeSelection *selection,
1413 gpointer data)
1415 struct units_report *preport = data;
1416 GtkTreeModel *model;
1417 GtkTreeIter it;
1418 int active_count;
1419 struct unit_type *utype = NULL;
1421 if (gtk_tree_selection_get_selected(selection, &model, &it)) {
1422 int ut;
1424 gtk_tree_model_get(model, &it,
1425 URD_COL_ACTIVE, &active_count,
1426 URD_COL_UTYPE_ID, &ut,
1427 -1);
1428 if (0 < active_count) {
1429 utype = utype_by_number(ut);
1433 if (NULL == utype) {
1434 gui_dialog_set_response_sensitive(preport->shell, URD_RES_NEAREST,
1435 FALSE);
1436 gui_dialog_set_response_sensitive(preport->shell, URD_RES_UPGRADE,
1437 FALSE);
1438 } else {
1439 gui_dialog_set_response_sensitive(preport->shell, URD_RES_NEAREST, TRUE);
1440 gui_dialog_set_response_sensitive(preport->shell, URD_RES_UPGRADE,
1441 (can_client_issue_orders()
1442 && NULL != can_upgrade_unittype(client_player(), utype)));
1446 /****************************************************************************
1447 Returns the nearest unit of the type 'utype'.
1448 ****************************************************************************/
1449 static struct unit *find_nearest_unit(const struct unit_type *utype,
1450 struct tile *ptile)
1452 struct unit *best_candidate = NULL;
1453 int best_dist = FC_INFINITY, dist;
1455 players_iterate(pplayer) {
1456 if (client_has_player() && pplayer != client_player()) {
1457 continue;
1460 unit_list_iterate(pplayer->units, punit) {
1461 if (utype == unit_type_get(punit)
1462 && FOCUS_AVAIL == punit->client.focus_status
1463 && 0 < punit->moves_left
1464 && !punit->done_moving
1465 && !punit->ai_controlled) {
1466 dist = sq_map_distance(unit_tile(punit), ptile);
1467 if (dist < best_dist) {
1468 best_candidate = punit;
1469 best_dist = dist;
1472 } unit_list_iterate_end;
1473 } players_iterate_end;
1475 return best_candidate;
1478 /****************************************************************************
1479 Gui dialog handler.
1480 ****************************************************************************/
1481 static void units_report_command_callback(struct gui_dialog *pdialog,
1482 int response,
1483 gpointer data)
1485 struct units_report *preport = data;
1486 struct unit_type *utype = NULL;
1487 GtkTreeSelection *selection;
1488 GtkTreeModel *model;
1489 GtkTreeIter it;
1491 switch (response) {
1492 case URD_RES_NEAREST:
1493 case URD_RES_UPGRADE:
1494 break;
1495 default:
1496 gui_dialog_destroy(pdialog);
1497 return;
1500 /* Nearest & upgrade commands. */
1501 selection = gtk_tree_view_get_selection(preport->tree_view);
1502 if (gtk_tree_selection_get_selected(selection, &model, &it)) {
1503 int ut;
1505 gtk_tree_model_get(model, &it, URD_COL_UTYPE_ID, &ut, -1);
1506 utype = utype_by_number(ut);
1509 if (response == URD_RES_NEAREST) {
1510 struct tile *ptile;
1511 struct unit *punit;
1513 ptile = get_center_tile_mapcanvas();
1514 if ((punit = find_nearest_unit(utype, ptile))) {
1515 center_tile_mapcanvas(unit_tile(punit));
1517 if (ACTIVITY_IDLE == punit->activity
1518 || ACTIVITY_SENTRY == punit->activity) {
1519 if (can_unit_do_activity(punit, ACTIVITY_IDLE)) {
1520 unit_focus_set_and_select(punit);
1524 } else if (can_client_issue_orders()) {
1525 GtkWidget *shell;
1526 struct unit_type *upgrade = can_upgrade_unittype(client_player(), utype);
1527 char buf[1024];
1528 int price = unit_upgrade_price(client_player(), utype, upgrade);
1530 fc_snprintf(buf, ARRAY_SIZE(buf), PL_("Treasury contains %d gold.",
1531 "Treasury contains %d gold.",
1532 client_player()->economic.gold),
1533 client_player()->economic.gold);
1535 shell = gtk_message_dialog_new(NULL,
1536 GTK_DIALOG_MODAL
1537 | GTK_DIALOG_DESTROY_WITH_PARENT,
1538 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
1539 /* TRANS: Last %s is pre-pluralised
1540 * "Treasury contains %d gold." */
1541 PL_("Upgrade as many %s to %s as possible "
1542 "for %d gold each?\n%s",
1543 "Upgrade as many %s to %s as possible "
1544 "for %d gold each?\n%s", price),
1545 utype_name_translation(utype),
1546 utype_name_translation(upgrade),
1547 price, buf);
1548 setup_dialog(shell, gui_dialog_get_toplevel(preport->shell));
1550 gtk_window_set_title(GTK_WINDOW(shell), _("Upgrade Obsolete Units"));
1552 if (GTK_RESPONSE_YES == gtk_dialog_run(GTK_DIALOG(shell))) {
1553 dsend_packet_unit_type_upgrade(&client.conn, utype_number(utype));
1556 gtk_widget_destroy(shell);
1560 /****************************************************************************
1561 Create a units report.
1562 ****************************************************************************/
1563 static void units_report_init(struct units_report *preport)
1565 GtkWidget *view, *sw, *align, *button;
1566 GtkListStore *store;
1567 GtkTreeSelection *selection;
1568 GtkBox *vbox;
1569 GtkTreeViewColumn *col = NULL;
1570 enum units_report_columns i;
1572 fc_assert_ret(NULL != preport);
1574 gui_dialog_new(&preport->shell, GTK_NOTEBOOK(top_notebook), preport, TRUE);
1575 gui_dialog_set_title(preport->shell, _("Units"));
1576 vbox = GTK_BOX(preport->shell->vbox);
1578 align = gtk_alignment_new(0.5, 0.0, 0.0, 1.0);
1579 gtk_box_pack_start(vbox, align, TRUE, TRUE, 0);
1581 sw = gtk_scrolled_window_new(NULL,NULL);
1582 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1583 GTK_SHADOW_ETCHED_IN);
1584 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1585 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1586 gtk_container_add(GTK_CONTAINER(align), sw);
1588 store = units_report_store_new();
1589 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1590 g_object_unref(store);
1591 gtk_widget_set_name(view, "small_font");
1592 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(view));
1593 gtk_container_add(GTK_CONTAINER(sw), view);
1594 preport->tree_view = GTK_TREE_VIEW(view);
1596 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1597 g_signal_connect(selection, "changed",
1598 G_CALLBACK(units_report_selection_callback), preport);
1600 for (i = 0; unit_report_columns[i].title != NULL; i++) {
1601 GtkCellRenderer *renderer;
1603 if (strlen(unit_report_columns[i].title) > 0) {
1604 GtkWidget *header = gtk_label_new(Q_(unit_report_columns[i].title));
1605 if (unit_report_columns[i].tooltip) {
1606 gtk_widget_set_tooltip_text(header,
1607 Q_(unit_report_columns[i].tooltip));
1609 gtk_widget_show(header);
1610 col = gtk_tree_view_column_new();
1611 gtk_tree_view_column_set_widget(col, header);
1612 if (unit_report_columns[i].rightalign) {
1613 gtk_tree_view_column_set_alignment(col, 1.0);
1615 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
1616 } /* else add new renderer to previous TreeViewColumn */
1618 fc_assert(col != NULL);
1619 if (G_TYPE_BOOLEAN == unit_report_columns[i].type) {
1620 renderer = gtk_cell_renderer_toggle_new();
1621 gtk_tree_view_column_pack_start(col, renderer, FALSE);
1622 gtk_tree_view_column_add_attribute(col, renderer, "active", i);
1623 } else {
1624 renderer = gtk_cell_renderer_text_new();
1625 gtk_tree_view_column_pack_start(col, renderer, TRUE);
1626 gtk_tree_view_column_add_attribute(col, renderer, "text", i);
1627 gtk_tree_view_column_add_attribute(col, renderer,
1628 "weight", URD_COL_TEXT_WEIGHT);
1631 if (unit_report_columns[i].visible_col >= 0) {
1632 gtk_tree_view_column_add_attribute(col, renderer, "visible",
1633 unit_report_columns[i].visible_col);
1636 if (unit_report_columns[i].rightalign) {
1637 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1641 button = gui_dialog_add_stockbutton(preport->shell, GTK_STOCK_FIND,
1642 _("Find _Nearest"), URD_RES_NEAREST);
1643 gtk_widget_set_sensitive(button, FALSE);
1645 button = gui_dialog_add_button(preport->shell, _("_Upgrade"),
1646 URD_RES_UPGRADE);
1647 gtk_widget_set_sensitive(button, FALSE);
1649 gui_dialog_add_button(preport->shell, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
1651 gui_dialog_set_default_response(preport->shell, GTK_RESPONSE_CLOSE);
1652 gui_dialog_response_set_callback(preport->shell,
1653 units_report_command_callback);
1655 gui_dialog_set_default_size(preport->shell, -1, 350);
1656 gui_dialog_show_all(preport->shell);
1658 units_report_update(preport);
1659 gtk_tree_view_focus(GTK_TREE_VIEW(view));
1662 /****************************************************************************
1663 Free an units report.
1664 ****************************************************************************/
1665 static void units_report_free(struct units_report *preport)
1667 fc_assert_ret(NULL != preport);
1669 gui_dialog_destroy(preport->shell);
1670 fc_assert(NULL == preport->shell);
1672 memset(preport, 0, sizeof(*preport));
1675 /****************************************************************************
1676 Create the units report if needed.
1677 ****************************************************************************/
1678 void units_report_dialog_popup(bool raise)
1680 if (NULL == units_report.shell) {
1681 units_report_init(&units_report);
1684 gui_dialog_present(units_report.shell);
1685 if (raise) {
1686 gui_dialog_raise(units_report.shell);
1690 /****************************************************************************
1691 Closes the units report dialog.
1692 ****************************************************************************/
1693 void units_report_dialog_popdown(void)
1695 if (NULL != units_report.shell) {
1696 units_report_free(&units_report);
1697 fc_assert(NULL == units_report.shell);
1701 /****************************************************************************
1702 Update the units report dialog.
1703 ****************************************************************************/
1704 void real_units_report_dialog_update(void)
1706 if (NULL != units_report.shell) {
1707 units_report_update(&units_report);
1712 /****************************************************************************
1713 FINAL REPORT DIALOG
1714 ****************************************************************************/
1715 struct endgame_report {
1716 struct gui_dialog *shell;
1717 GtkTreeView *tree_view;
1718 GtkListStore *store;
1719 int player_count;
1720 int players_received;
1723 enum endgame_report_columns {
1724 FRD_COL_PLAYER,
1725 FRD_COL_NATION,
1726 FRD_COL_SCORE,
1728 FRD_COL_NUM
1731 static struct endgame_report endgame_report = { NULL, };
1733 /****************************************************************************
1734 Returns the title of the column (translated).
1735 ****************************************************************************/
1736 static const char *
1737 endgame_report_column_name(enum endgame_report_columns col)
1739 switch (col) {
1740 case FRD_COL_PLAYER:
1741 return _("Player\n");
1742 case FRD_COL_NATION:
1743 return _("Nation\n");
1744 case FRD_COL_SCORE:
1745 return _("Score\n");
1746 case FRD_COL_NUM:
1747 break;
1750 return NULL;
1753 /****************************************************************************
1754 Fill a final report with statistics for each player.
1755 ****************************************************************************/
1756 static void endgame_report_update(struct endgame_report *preport,
1757 const struct packet_endgame_report *packet)
1759 const size_t col_num = packet->category_num + FRD_COL_NUM;
1760 GType col_types[col_num];
1761 GtkListStore *store;
1762 GtkTreeViewColumn *col;
1763 int i;
1765 fc_assert_ret(NULL != preport);
1767 /* Remove the old columns. */
1768 while ((col = gtk_tree_view_get_column(preport->tree_view, 0))) {
1769 gtk_tree_view_remove_column(preport->tree_view, col);
1772 /* Create the new model. */
1773 col_types[FRD_COL_PLAYER] = G_TYPE_STRING;
1774 col_types[FRD_COL_NATION] = GDK_TYPE_PIXBUF;
1775 col_types[FRD_COL_SCORE] = G_TYPE_INT;
1776 for (i = FRD_COL_NUM; i < col_num; i++) {
1777 col_types[i] = G_TYPE_INT;
1779 store = gtk_list_store_newv(col_num, col_types);
1780 gtk_tree_view_set_model(preport->tree_view, GTK_TREE_MODEL(store));
1781 g_object_unref(G_OBJECT(store));
1783 /* Create the new columns. */
1784 for (i = 0; i < col_num; i++) {
1785 GtkCellRenderer *renderer;
1786 const char *title;
1787 const char *attribute;
1789 if (GDK_TYPE_PIXBUF == col_types[i]) {
1790 renderer = gtk_cell_renderer_pixbuf_new();
1791 attribute = "pixbuf";
1792 } else {
1793 renderer = gtk_cell_renderer_text_new();
1794 attribute = "text";
1797 if (i < FRD_COL_NUM) {
1798 title = endgame_report_column_name(i);
1799 } else {
1800 title = packet->category_name[i - FRD_COL_NUM];
1803 col = gtk_tree_view_column_new_with_attributes(Q_(title), renderer,
1804 attribute, i, NULL);
1805 gtk_tree_view_append_column(preport->tree_view, col);
1806 if (GDK_TYPE_PIXBUF != col_types[i]) {
1807 gtk_tree_view_column_set_sort_column_id(col, i);
1811 preport->store = store;
1812 preport->player_count = packet->player_num;
1813 preport->players_received = 0;
1816 /****************************************************************************
1817 Handle endgame report information about one player.
1818 ****************************************************************************/
1819 void endgame_report_dialog_player(const struct packet_endgame_player *packet)
1821 /* Fill the model with player stats. */
1822 struct endgame_report *preport = &endgame_report;
1823 const struct player *pplayer = player_by_number(packet->player_id);
1824 GtkTreeIter iter;
1825 int i;
1827 gtk_list_store_append(preport->store, &iter);
1828 gtk_list_store_set(preport->store, &iter,
1829 FRD_COL_PLAYER, player_name(pplayer),
1830 FRD_COL_NATION, get_flag(nation_of_player(pplayer)),
1831 FRD_COL_SCORE, packet->score,
1832 -1);
1833 for (i = 0; i < packet->category_num; i++) {
1834 gtk_list_store_set(preport->store, &iter,
1835 i + FRD_COL_NUM, packet->category_score[i],
1836 -1);
1839 preport->players_received++;
1841 if (preport->players_received == preport->player_count) {
1842 gui_dialog_present(preport->shell);
1846 /****************************************************************************
1847 Prepare a final report.
1848 ****************************************************************************/
1849 static void endgame_report_init(struct endgame_report *preport)
1851 GtkWidget *sw, *view;
1853 fc_assert_ret(NULL != preport);
1855 gui_dialog_new(&preport->shell, GTK_NOTEBOOK(top_notebook), NULL, TRUE);
1856 gui_dialog_set_title(preport->shell, _("Score"));
1858 gui_dialog_set_default_size(preport->shell, 700, 420);
1860 /* Setup the layout. */
1861 sw = gtk_scrolled_window_new(NULL, NULL);
1862 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1863 GTK_SHADOW_ETCHED_IN);
1864 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1865 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1866 gtk_box_pack_start(GTK_BOX(preport->shell->vbox), sw, TRUE, TRUE, 0);
1868 view = gtk_tree_view_new();
1869 gtk_widget_set_name(view, "small_font");
1870 gtk_container_add(GTK_CONTAINER(sw), view);
1871 preport->tree_view = GTK_TREE_VIEW(view);
1873 gui_dialog_show_all(preport->shell);
1876 /****************************************************************************
1877 Start building a dialog with player statistics at endgame.
1878 ****************************************************************************/
1879 void endgame_report_dialog_start(const struct packet_endgame_report *packet)
1881 if (NULL == endgame_report.shell) {
1882 endgame_report_init(&endgame_report);
1884 endgame_report_update(&endgame_report, packet);