"same than" -> "same as".
[freeciv.git] / client / gui-gtk-3.0 / happiness.c
blobd5d47f3a5de3596a04ee18a54b42e1e59c01f673
1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <gtk/gtk.h>
20 /* utility */
21 #include "fcintl.h"
22 #include "log.h"
23 #include "mem.h"
24 #include "support.h"
26 /* common */
27 #include "city.h"
28 #include "game.h"
29 #include "government.h"
31 /* client */
32 #include "text.h"
33 #include "tilespec.h"
35 /* client/gui-gtk-3.0 */
36 #include "graphics.h"
37 #include "gui_main.h"
38 #include "gui_stuff.h"
39 #include "happiness.h"
40 #include "mapview.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 {
53 struct city *pcity;
54 GtkWidget *shell;
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];
59 GtkWidget *close;
62 #define SPECLIST_TAG dialog
63 #define SPECLIST_TYPE struct happiness_dialog
64 #include "speclist.h"
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,
73 bool low_dlg);
74 static gboolean show_happiness_popup(GtkWidget *w,
75 GdkEventButton *ev,
76 gpointer data);
77 static gboolean show_happiness_button_release(GtkWidget *w,
78 GdkEventButton *ev,
79 gpointer data);
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) {
104 return pdialog;
106 } dialog_list_iterate_end;
108 return NULL;
111 /****************************************************************
112 Popup for the happiness display.
113 *****************************************************************/
114 static gboolean show_happiness_popup(GtkWidget *w,
115 GdkEventButton *ev,
116 gpointer data)
118 struct happiness_dialog *pdialog = g_object_get_data(G_OBJECT(w),
119 "pdialog");
121 if (ev->button == 1) {
122 GtkWidget *p, *label, *frame;
123 char buf[1024];
125 switch (GPOINTER_TO_UINT(data)) {
126 case CITIES:
127 sz_strlcpy(buf, text_happiness_cities(pdialog->pcity));
128 break;
129 case LUXURIES:
130 sz_strlcpy(buf, text_happiness_luxuries(pdialog->pcity));
131 break;
132 case BUILDINGS:
133 sz_strlcpy(buf, text_happiness_buildings(pdialog->pcity));
134 break;
135 case NATIONALITY:
136 sz_strlcpy(buf, text_happiness_nationality(pdialog->pcity));
137 break;
138 case UNITS:
139 sz_strlcpy(buf, text_happiness_units(pdialog->pcity));
140 break;
141 case WONDERS:
142 sz_strlcpy(buf, text_happiness_wonders(pdialog->pcity));
143 break;
144 default:
145 return TRUE;
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,
167 ev->time);
168 gtk_grab_add(p);
170 g_signal_connect_after(p, "button_release_event",
171 G_CALLBACK(show_happiness_button_release), NULL);
174 return TRUE;
177 /**************************************************************************
178 Clear the happiness popup.
179 **************************************************************************/
180 static gboolean show_happiness_button_release(GtkWidget *w,
181 GdkEventButton *ev,
182 gpointer data)
184 gtk_grab_remove(w);
185 gdk_device_ungrab(ev->device, ev->time);
186 gtk_widget_destroy(w);
187 return FALSE;
190 /**************************************************************************
191 Create the happiness notebook page.
192 **************************************************************************/
193 static struct happiness_dialog *create_happiness_dialog(struct city *pcity,
194 bool low_dlg)
196 int i;
197 struct happiness_dialog *pdialog;
198 GtkWidget *ebox, *label, *table;
199 char buf[700];
201 static const char *happiness_label_str[NUM_HAPPINESS_MODIFIERS] = {
202 N_("Cities:"),
203 N_("Luxuries:"),
204 N_("Buildings:"),
205 N_("Nationality:"),
206 N_("Units:"),
207 N_("Wonders:"),
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++) {
231 GtkWidget *img;
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);
278 return pdialog;
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];
288 int i;
289 int num_citizens = get_city_citizen_types(pcity, index, categories);
290 int offset = MIN(tileset_small_sprite_width(tileset), FEELING_WIDTH / num_citizens);
291 cairo_t *cr;
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,
298 i * offset, 0);
299 cairo_rectangle(cr, i * offset, 0, offset, FEELING_HEIGHT);
300 cairo_fill(cr);
303 cairo_destroy(cr);
306 /**************************************************************************
307 Refresh whole happiness dialog
308 **************************************************************************/
309 void refresh_happiness_dialog(struct city *pcity)
311 int i;
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);
325 int i;
327 if (pdialog == NULL) {
328 /* City which is being investigated doesn't contain happiness tab */
329 return;
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]);
342 free(pdialog);
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;