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.
7
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 ***********************************************************************/
13
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17
18 #include <gtk/gtk.h>
19
20 /* utility */
21 #include "fcintl.h"
22 #include "log.h"
23 #include "mem.h"
24 #include "support.h"
25
26 /* common */
27 #include "city.h"
28 #include "game.h"
29 #include "government.h"
30
31 /* client */
32 #include "text.h"
33 #include "tilespec.h"
34
35 /* client/gui-gtk-2.0 */
36 #include "graphics.h"
37 #include "gui_main.h"
38 #include "gui_stuff.h"
39 #include "happiness.h"
40 #include "mapview.h"
41
42 /* semi-arbitrary number that controls the width of the happiness widget */
43 #define HAPPINESS_PIX_WIDTH 30
44
45 #define PIXCOMM_WIDTH (HAPPINESS_PIX_WIDTH * tileset_small_sprite_width(tileset) \
46 / tileset_scale(tileset))
47 #define PIXCOMM_HEIGHT (tileset_small_sprite_height(tileset) \
48 / tileset_scale(tileset))
49
50 #define NUM_HAPPINESS_MODIFIERS 6
51
52 enum { CITIES, LUXURIES, BUILDINGS, NATIONALITY, UNITS, WONDERS };
53
54 struct happiness_dialog {
55 struct city *pcity;
56 GtkWidget *shell;
57 GtkWidget *cityname_label;
58 GtkWidget *hpixmaps[NUM_HAPPINESS_MODIFIERS];
59 GtkWidget *happiness_ebox[NUM_HAPPINESS_MODIFIERS];
60 GtkWidget *happiness_label[NUM_HAPPINESS_MODIFIERS];
61 GtkWidget *close;
62 };
63
64 #define SPECLIST_TAG dialog
65 #define SPECLIST_TYPE struct happiness_dialog
66 #include "speclist.h"
67
68 #define dialog_list_iterate(dialoglist, pdialog) \
69 TYPED_LIST_ITERATE(struct happiness_dialog, dialoglist, pdialog)
70 #define dialog_list_iterate_end LIST_ITERATE_END
71
72 static struct dialog_list *dialog_list;
73 static struct happiness_dialog *get_happiness_dialog(struct city *pcity);
74 static struct happiness_dialog *create_happiness_dialog(struct city *pcity);
75 static gboolean show_happiness_popup(GtkWidget *w,
76 GdkEventButton *ev,
77 gpointer data);
78 static gboolean show_happiness_button_release(GtkWidget *w,
79 GdkEventButton *ev,
80 gpointer data);
81
82 /****************************************************************
83 Create happiness dialog
84 *****************************************************************/
happiness_dialog_init(void)85 void happiness_dialog_init(void)
86 {
87 dialog_list = dialog_list_new();
88 }
89
90 /****************************************************************
91 Remove happiness dialog
92 *****************************************************************/
happiness_dialog_done(void)93 void happiness_dialog_done(void)
94 {
95 dialog_list_destroy(dialog_list);
96 }
97
98 /****************************************************************
99 Return happiness dialog for a city
100 *****************************************************************/
get_happiness_dialog(struct city * pcity)101 static struct happiness_dialog *get_happiness_dialog(struct city *pcity)
102 {
103 dialog_list_iterate(dialog_list, pdialog) {
104 if (pdialog->pcity == pcity) {
105 return pdialog;
106 }
107 } dialog_list_iterate_end;
108
109 return NULL;
110 }
111
112 /****************************************************************
113 Popup for the happiness display.
114 *****************************************************************/
show_happiness_popup(GtkWidget * w,GdkEventButton * ev,gpointer data)115 static gboolean show_happiness_popup(GtkWidget *w,
116 GdkEventButton *ev,
117 gpointer data)
118 {
119 struct happiness_dialog *pdialog = g_object_get_data(G_OBJECT(w),
120 "pdialog");
121
122 if (ev->button == 1) {
123 GtkWidget *p, *label, *frame;
124 char buf[1024];
125
126 switch (GPOINTER_TO_UINT(data)) {
127 case CITIES:
128 sz_strlcpy(buf, text_happiness_cities(pdialog->pcity));
129 break;
130 case LUXURIES:
131 sz_strlcpy(buf, text_happiness_luxuries(pdialog->pcity));
132 break;
133 case BUILDINGS:
134 sz_strlcpy(buf, text_happiness_buildings(pdialog->pcity));
135 break;
136 case NATIONALITY:
137 sz_strlcpy(buf, text_happiness_nationality(pdialog->pcity));
138 break;
139 case UNITS:
140 sz_strlcpy(buf, text_happiness_units(pdialog->pcity));
141 break;
142 case WONDERS:
143 sz_strlcpy(buf, text_happiness_wonders(pdialog->pcity));
144 break;
145 default:
146 return TRUE;
147 }
148
149 p = gtk_window_new(GTK_WINDOW_POPUP);
150 gtk_widget_set_name(p, "Freeciv");
151 gtk_container_set_border_width(GTK_CONTAINER(p), 2);
152 gtk_window_set_position(GTK_WINDOW(p), GTK_WIN_POS_MOUSE);
153
154 frame = gtk_frame_new(NULL);
155 gtk_container_add(GTK_CONTAINER(p), frame);
156
157 label = gtk_label_new(buf);
158 /* FIXME: there is no font option corresponding to this style name.
159 * Remove?: */
160 gtk_widget_set_name(label, "city_happiness_label");
161 gtk_misc_set_padding(GTK_MISC(label), 4, 4);
162 gtk_container_add(GTK_CONTAINER(frame), label);
163 gtk_widget_show_all(p);
164
165 gdk_pointer_grab(p->window, TRUE, GDK_BUTTON_RELEASE_MASK,
166 NULL, NULL, ev->time);
167 gtk_grab_add(p);
168
169 g_signal_connect_after(p, "button_release_event",
170 G_CALLBACK(show_happiness_button_release), NULL);
171 }
172
173 return TRUE;
174 }
175
176 /**************************************************************************
177 Clear the happiness popup.
178 **************************************************************************/
show_happiness_button_release(GtkWidget * w,GdkEventButton * ev,gpointer data)179 static gboolean show_happiness_button_release(GtkWidget *w,
180 GdkEventButton *ev,
181 gpointer data)
182 {
183 gtk_grab_remove(w);
184 gdk_pointer_ungrab(GDK_CURRENT_TIME);
185 gtk_widget_destroy(w);
186 return FALSE;
187 }
188
189 /**************************************************************************
190 Create the happiness notebook page.
191 **************************************************************************/
create_happiness_dialog(struct city * pcity)192 static struct happiness_dialog *create_happiness_dialog(struct city *pcity)
193 {
194 int i;
195 struct happiness_dialog *pdialog;
196 GtkWidget *hbox, *ebox, *cbox, *label, *table;
197
198 static const char *happiness_label_str[NUM_HAPPINESS_MODIFIERS] = {
199 N_("Cities:"),
200 N_("Luxuries:"),
201 N_("Buildings:"),
202 N_("Nationality:"),
203 N_("Units:"),
204 N_("Wonders:"),
205 };
206 static bool happiness_label_str_done;
207
208 pdialog = fc_malloc(sizeof(struct happiness_dialog));
209 pdialog->pcity = pcity;
210
211 pdialog->shell = gtk_vbox_new(FALSE, 0);
212
213 pdialog->cityname_label = gtk_frame_new(_("Happiness"));
214 gtk_box_pack_start(GTK_BOX(pdialog->shell),
215 pdialog->cityname_label, TRUE, TRUE, 0);
216
217 hbox = gtk_hbox_new(TRUE, 0);
218
219 table = gtk_table_new(NUM_HAPPINESS_MODIFIERS + 1, 2, FALSE);
220 gtk_table_set_col_spacing(GTK_TABLE(table), 0, 5);
221 gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 4);
222
223 intl_slist(ARRAY_SIZE(happiness_label_str), happiness_label_str,
224 &happiness_label_str_done);
225
226 gtk_container_add(GTK_CONTAINER(pdialog->cityname_label), hbox);
227
228 for (i = 0; i < NUM_HAPPINESS_MODIFIERS; i++) {
229 /* set spacing between lines of citizens*/
230 gtk_table_set_row_spacing(GTK_TABLE(table), i, 10);
231
232 /* happiness labels */
233 label = gtk_label_new(happiness_label_str[i]);
234 pdialog->happiness_label[i] = label;
235 gtk_widget_set_name(label, "city_label");
236 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
237
238 gtk_table_attach(GTK_TABLE(table), label, 0, 1, i, i + 1,
239 GTK_FILL, GTK_EXPAND | GTK_SHRINK, 0, 0);
240
241 /* list of citizens */
242 ebox = gtk_event_box_new();
243 g_object_set_data(G_OBJECT(ebox), "pdialog", pdialog);
244 g_signal_connect(ebox, "button_press_event",
245 G_CALLBACK(show_happiness_popup), GUINT_TO_POINTER(i));
246 pdialog->happiness_ebox[i] = ebox;
247
248 cbox = gtk_vbox_new(FALSE, 2);
249 pdialog->hpixmaps[i] = gtk_pixcomm_new(PIXCOMM_WIDTH, PIXCOMM_HEIGHT);
250 gtk_box_pack_start(GTK_BOX(cbox), pdialog->hpixmaps[i], FALSE, FALSE, 0);
251 gtk_misc_set_alignment(GTK_MISC(pdialog->hpixmaps[i]), 0, 0);
252
253 gtk_container_add(GTK_CONTAINER(ebox), cbox);
254
255 gtk_table_attach(GTK_TABLE(table), ebox, 1, 2, i, i + 1,
256 GTK_FILL, GTK_EXPAND | GTK_SHRINK, 0, 0);
257 }
258
259 /* TRANS: the width of this text defines the width of the city dialog. */
260 label = gtk_label_new(_("Additional information is available via left "
261 "click on the citizens."));
262 gtk_widget_set_name(label, "city_label");
263 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
264 gtk_table_attach(GTK_TABLE(table), label, 0, 2, NUM_HAPPINESS_MODIFIERS,
265 NUM_HAPPINESS_MODIFIERS + 1, GTK_FILL, 0, 0, 0);
266
267 gtk_widget_show_all(pdialog->shell);
268 dialog_list_prepend(dialog_list, pdialog);
269 refresh_happiness_dialog(pcity);
270
271 return pdialog;
272 }
273
274 /**************************************************************************
275 Refresh citizens pixcomm
276 **************************************************************************/
refresh_pixcomm(GtkPixcomm * dst,struct city * pcity,enum citizen_feeling index)277 static void refresh_pixcomm(GtkPixcomm *dst, struct city *pcity,
278 enum citizen_feeling index)
279 {
280 enum citizen_category categories[MAX_CITY_SIZE];
281 int i;
282 int num_citizens = get_city_citizen_types(pcity, index, categories);
283 int offset = MIN(tileset_small_sprite_width(tileset)
284 / tileset_scale(tileset), PIXCOMM_WIDTH / num_citizens);
285
286 gtk_pixcomm_freeze(dst);
287 gtk_pixcomm_clear(dst);
288
289 for (i = 0; i < num_citizens; i++) {
290 gtk_pixcomm_copyto(dst, get_citizen_sprite(tileset, categories[i], i, pcity),
291 i * offset, 0);
292 }
293
294 gtk_pixcomm_thaw(dst);
295 }
296
297 /**************************************************************************
298 Refresh whole happiness dialog
299 **************************************************************************/
refresh_happiness_dialog(struct city * pcity)300 void refresh_happiness_dialog(struct city *pcity)
301 {
302 int i;
303 struct happiness_dialog *pdialog = get_happiness_dialog(pcity);
304
305 for (i = 0; i < FEELING_LAST; i++) {
306 refresh_pixcomm(GTK_PIXCOMM(pdialog->hpixmaps[i]), pdialog->pcity, i);
307 }
308 }
309
310 /**************************************************************************
311 Close happiness dialog of given city
312 **************************************************************************/
close_happiness_dialog(struct city * pcity)313 void close_happiness_dialog(struct city *pcity)
314 {
315 struct happiness_dialog *pdialog = get_happiness_dialog(pcity);
316 if (pdialog == NULL) {
317 /* City which is being investigated doesn't contain happiness tab */
318 return;
319 }
320
321 gtk_widget_hide(pdialog->shell);
322
323 dialog_list_remove(dialog_list, pdialog);
324
325 gtk_widget_destroy(pdialog->shell);
326 free(pdialog);
327 }
328
329 /**************************************************************************
330 Create happiness dialog and get its widget
331 **************************************************************************/
get_top_happiness_display(struct city * pcity)332 GtkWidget *get_top_happiness_display(struct city *pcity)
333 {
334 return create_happiness_dialog(pcity)->shell;
335 }
336