1 /**********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
15 #include <fc_config.h>
24 #include <gdk/gdkkeysyms.h>
33 #include "fc_types.h" /* LINE_BREAK */
35 #include "government.h"
42 #include "chatline_common.h"
43 #include "client_main.h"
46 #include "mapview_common.h"
48 #include "packhand_gen.h"
53 /* client/gui-gtk-2.0 */
58 #include "gui_stuff.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
;
76 GtkLayout
*drawing_area
;
79 static GtkListStore
*science_report_store_new(void);
80 static inline void science_report_store_set(GtkListStore
*store
,
83 static bool science_report_combo_get_active(GtkComboBox
*combo
,
86 static void science_report_combo_set_active(GtkComboBox
*combo
,
88 static gboolean
science_diagram_button_release_callback(GtkWidget
*widget
,
89 GdkEventButton
*event
,
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
,
100 static void science_report_show_all_callback(GtkComboBox
*combo
,
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
{
115 SRD_COL_ID
, /* Tech_type_id */
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
,
138 const struct research
*presearch
= research_get(client_player());
140 gtk_list_store_set(store
, iter
,
142 research_advance_name_translation(presearch
, tech
),
144 research_goal_unknown_techs(presearch
, tech
),
149 /****************************************************************************
150 Get the active tech of the combo.
151 ****************************************************************************/
152 static bool science_report_combo_get_active(GtkComboBox
*combo
,
158 if (science_report_no_combo_callback
159 || !gtk_combo_box_get_active_iter(combo
, &iter
)) {
163 gtk_tree_model_get(gtk_combo_box_get_model(combo
), &iter
,
170 /****************************************************************************
171 Set the active tech of the combo.
172 ****************************************************************************/
173 static void science_report_combo_set_active(GtkComboBox
*combo
,
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
;
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
) {
206 if (event
->button
== 3) {
208 popup_help_dialog_typed(research_advance_name_translation(presearch
,
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
);
219 dsend_packet_player_tech_goal(&client
.conn
, tech
);
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");
242 if (!tileset_is_fully_loaded()) {
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)
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
),
271 /****************************************************************************
272 Recreate the req tree.
273 ****************************************************************************/
274 static void science_diagram_data(GtkWidget
*widget
, bool show_all
)
276 struct reqtree
*reqtree
;
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
);
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
)) {
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
;
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
;
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
)
369 GList
*sorting_list
, *item
;
370 struct research
*presearch
= research_get(client_player());
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
);
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
);
422 g_list_free(sorting_list
);
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
);
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
,
472 const char *tech_name
;
474 if (!science_report_combo_get_active(combo
, &tech
, &tech_name
)) {
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
,
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
)
505 const char *tech_name
;
507 if (!science_report_combo_get_active(combo
, &tech
, &tech_name
)) {
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
;
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",
564 gtk_widget_set_sensitive(w
, can_client_issue_orders());
565 g_signal_connect(w
, "changed", G_CALLBACK(science_report_current_callback
),
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",
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
),
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
);
662 gui_dialog_present(science_report
.shell
);
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
;
711 static struct economy_report economy_report
= { NULL
, };
713 enum economy_report_response
{
714 ERD_RES_SELL_REDUNDANT
= 1,
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
{
730 ERD_COL_IS_IMPROVEMENT
,
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 ****************************************************************************/
757 economy_report_column_name(enum economy_report_columns col
)
761 /* TRANS: Image header */
764 return Q_("?Building or Unit type:Name");
765 case ERD_COL_REDUNDANT
:
766 return _("Redundant");
771 case ERD_COL_TOTAL_COST
:
772 /* TRANS: Upkeep total, count*cost. */
774 case ERD_COL_IS_IMPROVEMENT
:
783 /****************************************************************************
784 Update the economy report dialog.
785 ****************************************************************************/
786 static void economy_report_update(struct economy_report
*preport
)
788 GtkTreeSelection
*selection
;
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
;
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);
808 model
= gtk_tree_view_get_model(preport
->tree_view
);
809 store
= GTK_LIST_STORE(model
);
810 gtk_list_store_clear(store
);
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
,
832 if (selected
== id
) {
833 /* Restore the selection. */
834 gtk_tree_selection_select_iter(selection
, &iter
);
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
,
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
,
877 struct economy_report
*preport
= data
;
878 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(preport
->tree_view
);
882 struct universal selected
;
887 case ERD_RES_SELL_REDUNDANT
:
888 case ERD_RES_SELL_ALL
:
889 case ERD_RES_DISBAND_UNITS
:
892 gui_dialog_destroy(pdialog
);
896 if (!can_client_issue_orders()
897 || !gtk_tree_selection_get_selected(selection
, &model
, &iter
)) {
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
);
914 gtk_tree_model_get(model
, &iter
,
915 redundant
? ERD_COL_REDUNDANT
: ERD_COL_COUNT
,
920 shell
= gtk_message_dialog_new(NULL
, GTK_DIALOG_MODAL
921 | GTK_DIALOG_DESTROY_WITH_PARENT
,
922 GTK_MESSAGE_QUESTION
,
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
),
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
);
945 if (ERD_RES_DISBAND_UNITS
== response
) {
946 struct unit_type
*putype
= selected
.value
.utype
;
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
,
954 /* TRANS: %s is a unit */
955 _("Do you really wish to disband "
956 "every %s (%d total)?"),
957 utype_name_translation(putype
),
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
);
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
,
977 setup_dialog(shell
, gui_dialog_get_toplevel(pdialog
));
978 g_signal_connect(shell
, "response", G_CALLBACK(gtk_widget_destroy
),
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
,
991 struct gui_dialog
*pdialog
= ((struct economy_report
*)data
)->shell
;
995 if (can_client_issue_orders()
996 && gtk_tree_selection_get_selected(selection
, &model
, &iter
)) {
997 struct universal selected
;
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
);
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
,
1017 gui_dialog_set_response_sensitive(pdialog
, ERD_RES_SELL_REDUNDANT
,
1019 gui_dialog_set_response_sensitive(pdialog
, ERD_RES_SELL_ALL
, FALSE
);
1020 gui_dialog_set_response_sensitive(pdialog
, ERD_RES_DISBAND_UNITS
,
1024 log_error("Not supported type: %d.", selected
.kind
);
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
;
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
,
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
,
1090 bool is_redundant
= (i
== ERD_COL_REDUNDANT
);
1091 renderer
= gtk_cell_renderer_text_new();
1093 /* Special treatment: hide "Redundant" column for units */
1094 col
= gtk_tree_view_column_new_with_attributes(title
, renderer
,
1097 ERD_COL_IS_IMPROVEMENT
,
1100 col
= gtk_tree_view_column_new_with_attributes(title
, renderer
,
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"),
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
);
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 /****************************************************************************
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,
1208 /* Those values must match the order of unit_report_columns[]. */
1209 enum units_report_columns
{
1212 URD_COL_N_UPGRADABLE
,
1213 URD_COL_IN_PROGRESS
,
1220 URD_COL_TEXT_WEIGHT
,
1221 URD_COL_UPG_VISIBLE
,
1222 URD_COL_NUPG_VISIBLE
,
1228 static const struct {
1231 const char *tooltip
;
1234 } unit_report_columns
[] = {
1235 { /* URD_COL_UTYPE_NAME */ G_TYPE_STRING
, N_("Unit Type"),
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"),
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)
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
)
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
;
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
));
1302 players_iterate(pplayer
) {
1303 if (client_has_player() && pplayer
!= client_player()) {
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
) {
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);
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
) {
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
,
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
;
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
,
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
,
1415 struct units_report
*preport
= data
;
1416 GtkTreeModel
*model
;
1419 struct unit_type
*utype
= NULL
;
1421 if (gtk_tree_selection_get_selected(selection
, &model
, &it
)) {
1424 gtk_tree_model_get(model
, &it
,
1425 URD_COL_ACTIVE
, &active_count
,
1426 URD_COL_UTYPE_ID
, &ut
,
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
,
1436 gui_dialog_set_response_sensitive(preport
->shell
, URD_RES_UPGRADE
,
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
,
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()) {
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
;
1472 } unit_list_iterate_end
;
1473 } players_iterate_end
;
1475 return best_candidate
;
1478 /****************************************************************************
1480 ****************************************************************************/
1481 static void units_report_command_callback(struct gui_dialog
*pdialog
,
1485 struct units_report
*preport
= data
;
1486 struct unit_type
*utype
= NULL
;
1487 GtkTreeSelection
*selection
;
1488 GtkTreeModel
*model
;
1492 case URD_RES_NEAREST
:
1493 case URD_RES_UPGRADE
:
1496 gui_dialog_destroy(pdialog
);
1500 /* Nearest & upgrade commands. */
1501 selection
= gtk_tree_view_get_selection(preport
->tree_view
);
1502 if (gtk_tree_selection_get_selected(selection
, &model
, &it
)) {
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
) {
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()) {
1526 struct unit_type
*upgrade
= can_upgrade_unittype(client_player(), utype
);
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
,
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
),
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
;
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
);
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"),
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
);
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 /****************************************************************************
1714 ****************************************************************************/
1715 struct endgame_report
{
1716 struct gui_dialog
*shell
;
1717 GtkTreeView
*tree_view
;
1718 GtkListStore
*store
;
1720 int players_received
;
1723 enum endgame_report_columns
{
1731 static struct endgame_report endgame_report
= { NULL
, };
1733 /****************************************************************************
1734 Returns the title of the column (translated).
1735 ****************************************************************************/
1737 endgame_report_column_name(enum endgame_report_columns col
)
1740 case FRD_COL_PLAYER
:
1741 return _("Player\n");
1742 case FRD_COL_NATION
:
1743 return _("Nation\n");
1745 return _("Score\n");
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
;
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
;
1787 const char *attribute
;
1789 if (GDK_TYPE_PIXBUF
== col_types
[i
]) {
1790 renderer
= gtk_cell_renderer_pixbuf_new();
1791 attribute
= "pixbuf";
1793 renderer
= gtk_cell_renderer_text_new();
1797 if (i
< FRD_COL_NUM
) {
1798 title
= endgame_report_column_name(i
);
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
);
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
,
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
],
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
);