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