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>
29 #include "government.h"
35 /* client/gui-gtk-3.0 */
38 #include "gui_stuff.h"
39 #include "happiness.h"
42 /* semi-arbitrary number that controls the width of the happiness widget */
43 #define HAPPINESS_PIX_WIDTH 30
45 #define FEELING_WIDTH (HAPPINESS_PIX_WIDTH * tileset_small_sprite_width(tileset))
46 #define FEELING_HEIGHT (tileset_small_sprite_height(tileset))
48 #define NUM_HAPPINESS_MODIFIERS 6
50 enum { CITIES
, LUXURIES
, BUILDINGS
, NATIONALITY
, UNITS
, WONDERS
};
52 struct happiness_dialog
{
55 GtkWidget
*cityname_label
;
56 cairo_surface_t
*feeling_surfaces
[NUM_HAPPINESS_MODIFIERS
];
57 GtkWidget
*happiness_ebox
[NUM_HAPPINESS_MODIFIERS
];
58 GtkWidget
*happiness_label
[NUM_HAPPINESS_MODIFIERS
];
62 #define SPECLIST_TAG dialog
63 #define SPECLIST_TYPE struct happiness_dialog
66 #define dialog_list_iterate(dialoglist, pdialog) \
67 TYPED_LIST_ITERATE(struct happiness_dialog, dialoglist, pdialog)
68 #define dialog_list_iterate_end LIST_ITERATE_END
70 static struct dialog_list
*dialog_list
;
71 static struct happiness_dialog
*get_happiness_dialog(struct city
*pcity
);
72 static struct happiness_dialog
*create_happiness_dialog(struct city
*pcity
,
74 static gboolean
show_happiness_popup(GtkWidget
*w
,
77 static gboolean
show_happiness_button_release(GtkWidget
*w
,
81 /****************************************************************
82 Create happiness dialog
83 *****************************************************************/
84 void happiness_dialog_init()
86 dialog_list
= dialog_list_new();
89 /****************************************************************
90 Remove happiness dialog
91 *****************************************************************/
92 void happiness_dialog_done()
94 dialog_list_destroy(dialog_list
);
97 /****************************************************************
98 Return happiness dialog for a city
99 *****************************************************************/
100 static struct happiness_dialog
*get_happiness_dialog(struct city
*pcity
)
102 dialog_list_iterate(dialog_list
, pdialog
) {
103 if (pdialog
->pcity
== pcity
) {
106 } dialog_list_iterate_end
;
111 /****************************************************************
112 Popup for the happiness display.
113 *****************************************************************/
114 static gboolean
show_happiness_popup(GtkWidget
*w
,
118 struct happiness_dialog
*pdialog
= g_object_get_data(G_OBJECT(w
),
121 if (ev
->button
== 1) {
122 GtkWidget
*p
, *label
, *frame
;
125 switch (GPOINTER_TO_UINT(data
)) {
127 sz_strlcpy(buf
, text_happiness_cities(pdialog
->pcity
));
130 sz_strlcpy(buf
, text_happiness_luxuries(pdialog
->pcity
));
133 sz_strlcpy(buf
, text_happiness_buildings(pdialog
->pcity
));
136 sz_strlcpy(buf
, text_happiness_nationality(pdialog
->pcity
));
139 sz_strlcpy(buf
, text_happiness_units(pdialog
->pcity
));
142 sz_strlcpy(buf
, text_happiness_wonders(pdialog
->pcity
));
148 p
= gtk_window_new(GTK_WINDOW_POPUP
);
149 gtk_widget_set_name(p
, "Freeciv");
150 gtk_container_set_border_width(GTK_CONTAINER(p
), 2);
151 gtk_window_set_position(GTK_WINDOW(p
), GTK_WIN_POS_MOUSE
);
153 frame
= gtk_frame_new(NULL
);
154 gtk_container_add(GTK_CONTAINER(p
), frame
);
156 label
= gtk_label_new(buf
);
157 gtk_widget_set_name(label
, "city_happiness_label");
158 gtk_widget_set_margin_left(label
, 4);
159 gtk_widget_set_margin_right(label
, 4);
160 gtk_widget_set_margin_top(label
, 4);
161 gtk_widget_set_margin_bottom(label
, 4);
162 gtk_container_add(GTK_CONTAINER(frame
), label
);
163 gtk_widget_show_all(p
);
165 gdk_device_grab(ev
->device
, gtk_widget_get_window(p
),
166 GDK_OWNERSHIP_NONE
, TRUE
, GDK_BUTTON_RELEASE_MASK
, NULL
,
170 g_signal_connect_after(p
, "button_release_event",
171 G_CALLBACK(show_happiness_button_release
), NULL
);
177 /**************************************************************************
178 Clear the happiness popup.
179 **************************************************************************/
180 static gboolean
show_happiness_button_release(GtkWidget
*w
,
185 gdk_device_ungrab(ev
->device
, ev
->time
);
186 gtk_widget_destroy(w
);
190 /**************************************************************************
191 Create the happiness notebook page.
192 **************************************************************************/
193 static struct happiness_dialog
*create_happiness_dialog(struct city
*pcity
,
197 struct happiness_dialog
*pdialog
;
198 GtkWidget
*ebox
, *label
, *table
;
201 static const char *happiness_label_str
[NUM_HAPPINESS_MODIFIERS
] = {
209 static bool happiness_label_str_done
;
211 pdialog
= fc_malloc(sizeof(struct happiness_dialog
));
212 pdialog
->pcity
= pcity
;
214 pdialog
->shell
= gtk_grid_new();
215 gtk_orientable_set_orientation(GTK_ORIENTABLE(pdialog
->shell
),
216 GTK_ORIENTATION_VERTICAL
);
218 pdialog
->cityname_label
= gtk_frame_new(_("Happiness"));
219 gtk_container_add(GTK_CONTAINER(pdialog
->shell
), pdialog
->cityname_label
);
221 table
= gtk_grid_new();
222 g_object_set(table
, "margin", 4, NULL
);
223 gtk_grid_set_row_spacing(GTK_GRID(table
), 10);
225 intl_slist(ARRAY_SIZE(happiness_label_str
), happiness_label_str
,
226 &happiness_label_str_done
);
228 gtk_container_add(GTK_CONTAINER(pdialog
->cityname_label
), table
);
230 for (i
= 0; i
< NUM_HAPPINESS_MODIFIERS
; i
++) {
233 /* set spacing between lines of citizens*/
235 /* happiness labels */
236 label
= gtk_label_new(happiness_label_str
[i
]);
237 pdialog
->happiness_label
[i
] = label
;
238 gtk_widget_set_name(label
, "city_label");
239 gtk_widget_set_halign(label
, GTK_ALIGN_START
);
240 gtk_widget_set_valign(label
, GTK_ALIGN_CENTER
);
242 gtk_grid_attach(GTK_GRID(table
), label
, 0, i
, 1, 1);
244 /* list of citizens */
245 ebox
= gtk_event_box_new();
246 gtk_widget_set_margin_left(ebox
, 5);
247 gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox
), FALSE
);
248 g_object_set_data(G_OBJECT(ebox
), "pdialog", pdialog
);
249 g_signal_connect(ebox
, "button_press_event",
250 G_CALLBACK(show_happiness_popup
), GUINT_TO_POINTER(i
));
251 pdialog
->happiness_ebox
[i
] = ebox
;
253 pdialog
->feeling_surfaces
[i
] = cairo_image_surface_create(CAIRO_FORMAT_ARGB32
,
254 FEELING_WIDTH
, FEELING_HEIGHT
);
255 img
= gtk_image_new_from_surface(pdialog
->feeling_surfaces
[i
]);
256 gtk_container_add(GTK_CONTAINER(ebox
), img
);
257 gtk_widget_set_halign(img
, GTK_ALIGN_START
);
258 gtk_widget_set_valign(img
, GTK_ALIGN_START
);
260 gtk_grid_attach(GTK_GRID(table
), ebox
, 1, i
, 1, 1);
263 /* TRANS: the width of this text defines the width of the city dialog.
264 * '%s' is either space or newline depending on screen real estate. */
265 fc_snprintf(buf
, sizeof(buf
),
266 _("Additional information is available%svia left "
267 "click on the citizens."), low_dlg
? "\n" : " ");
268 label
= gtk_label_new(buf
);
269 gtk_widget_set_name(label
, "city_label");
270 gtk_widget_set_halign(label
, GTK_ALIGN_START
);
271 gtk_widget_set_valign(label
, GTK_ALIGN_CENTER
);
272 gtk_grid_attach(GTK_GRID(table
), label
, 0, NUM_HAPPINESS_MODIFIERS
, 2, 1);
274 gtk_widget_show_all(pdialog
->shell
);
275 dialog_list_prepend(dialog_list
, pdialog
);
276 refresh_happiness_dialog(pcity
);
281 /**************************************************************************
282 Refresh citizens surface
283 **************************************************************************/
284 static void refresh_feeling_surface(cairo_surface_t
*dst
, struct city
*pcity
,
285 enum citizen_feeling index
)
287 enum citizen_category categories
[MAX_CITY_SIZE
];
289 int num_citizens
= get_city_citizen_types(pcity
, index
, categories
);
290 int offset
= MIN(tileset_small_sprite_width(tileset
), FEELING_WIDTH
/ num_citizens
);
293 cr
= cairo_create(dst
);
295 for (i
= 0; i
< num_citizens
; i
++) {
296 cairo_set_source_surface(cr
,
297 get_citizen_sprite(tileset
, categories
[i
], i
, pcity
)->surface
,
299 cairo_rectangle(cr
, i
* offset
, 0, offset
, FEELING_HEIGHT
);
306 /**************************************************************************
307 Refresh whole happiness dialog
308 **************************************************************************/
309 void refresh_happiness_dialog(struct city
*pcity
)
312 struct happiness_dialog
*pdialog
= get_happiness_dialog(pcity
);
314 for (i
= 0; i
< FEELING_LAST
; i
++) {
315 refresh_feeling_surface(pdialog
->feeling_surfaces
[i
], pdialog
->pcity
, i
);
319 /**************************************************************************
320 Close happiness dialog of given city
321 **************************************************************************/
322 void close_happiness_dialog(struct city
*pcity
)
324 struct happiness_dialog
*pdialog
= get_happiness_dialog(pcity
);
327 if (pdialog
== NULL
) {
328 /* City which is being investigated doesn't contain happiness tab */
332 gtk_widget_hide(pdialog
->shell
);
334 dialog_list_remove(dialog_list
, pdialog
);
336 gtk_widget_destroy(pdialog
->shell
);
338 for (i
= 0; i
< NUM_HAPPINESS_MODIFIERS
; i
++) {
339 cairo_surface_destroy(pdialog
->feeling_surfaces
[i
]);
345 /**************************************************************************
346 Create happiness dialog and get its widget
347 **************************************************************************/
348 GtkWidget
*get_top_happiness_display(struct city
*pcity
, bool low_dlg
)
350 return create_happiness_dialog(pcity
, low_dlg
)->shell
;