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 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include <gtk/gtk.h>
24 #include <gdk/gdkkeysyms.h>
25 
26 /* common */
27 #include "citizens.h"
28 #include "city.h"
29 #include "player.h"
30 
31 /* client/gui-gtk-3.0 */
32 #include "gui_stuff.h"
33 #include "plrdlg.h"
34 
35 #include "citizensinfo.h"
36 
37 
38 static struct citizens_dialog *citizens_dialog_get(const struct city *pcity);
39 static struct citizens_dialog
40   *citizens_dialog_create(const struct city *pcity);
41 static GtkTreeStore *citizens_dialog_store_new(void);
42 static int citizens_dialog_default_sort_column(void);
43 static void citizens_dialog_row(GtkTreeStore *store, GtkTreeIter *it,
44                                 const struct city *pcity,
45                                 const struct player_slot *pslot);
46 
47 static const char *col_nation(const struct city *pcity,
48                               const struct player_slot *pslot);
49 static const char *col_citizens(const struct city *pcity,
50                                 const struct player_slot *pslot);
51 
52 /*****************************************************************************
53   The layout of the citizens display.
54 *****************************************************************************/
55 static struct citizens_column {
56   bool show;
57   enum player_dlg_column_type type;
58   const char *title;
59   const char *(*func)(const struct city *, const struct player_slot *);
60                                        /* if type = COL_*TEXT */
61   const char *tagname;                 /* for save_options */
62 } citizens_cols[] = {
63   {TRUE, COL_RIGHT_TEXT, N_("#"), col_citizens, "citizens"},
64   {TRUE, COL_FLAG, N_("Flag"), NULL, "flag"},
65   {TRUE, COL_TEXT, N_("Nation"), col_nation, "nation"}
66 };
67 static const int num_citizens_cols = ARRAY_SIZE(citizens_cols);
68 #define CITIZENS_DLG_COL_STYLE       (0 + num_citizens_cols)
69 #define CITIZENS_DLG_COL_WEIGHT      (1 + num_citizens_cols)
70 #define CITIZENS_DLG_COL_ID          (2 + num_citizens_cols)
71 
72 /*****************************************************************************
73   The citizens dialog.
74 *****************************************************************************/
75 struct citizens_dialog {
76   const struct city *pcity;
77   GtkWidget *shell;
78   GtkTreeStore *store;
79   GtkWidget *list;
80   GtkTreeModel *sort;
81 };
82 
83 #define SPECLIST_TAG dialog
84 #define SPECLIST_TYPE struct citizens_dialog
85 #include "speclist.h"
86 
87 #define dialog_list_iterate(dialoglist, pdialog) \
88     TYPED_LIST_ITERATE(struct citizens_dialog, dialoglist, pdialog)
89 #define dialog_list_iterate_end  LIST_ITERATE_END
90 
91 static struct dialog_list *dialog_list;
92 
93 /*****************************************************************************
94   The name of the player's nation for the plrdlg.
95 *****************************************************************************/
col_nation(const struct city * pcity,const struct player_slot * pslot)96 static const char *col_nation(const struct city *pcity,
97                               const struct player_slot *pslot)
98 {
99   return nation_adjective_for_player(player_slot_get_player(pslot));
100 }
101 
102 /*****************************************************************************
103   The number of citizens for the player in the city.
104 *****************************************************************************/
col_citizens(const struct city * pcity,const struct player_slot * pslot)105 static const char *col_citizens(const struct city *pcity,
106                                 const struct player_slot *pslot)
107 {
108   citizens nationality = citizens_nation_get(pcity, pslot);
109 
110   if (nationality == 0) {
111     return "-";
112   } else {
113     static char buf[8];
114 
115     fc_snprintf(buf, sizeof(buf), "%d", nationality);
116 
117     return buf;
118   }
119 }
120 
121 /****************************************************************************
122   Create a citizens dialog store.
123 
124   FIXME: copy of players_dialog_store_new();
125 ****************************************************************************/
citizens_dialog_store_new(void)126 static GtkTreeStore *citizens_dialog_store_new(void)
127 {
128   GtkTreeStore *store;
129   GType model_types[num_citizens_cols + 3];
130   int i;
131 
132   for (i = 0; i < num_citizens_cols; i++) {
133     switch (citizens_cols[i].type) {
134     case COL_FLAG:
135       model_types[i] = GDK_TYPE_PIXBUF;
136       break;
137     case COL_COLOR:
138       model_types[i] = GDK_TYPE_PIXBUF;
139       break;
140     case COL_BOOLEAN:
141       model_types[i] = G_TYPE_BOOLEAN;
142       break;
143     case COL_TEXT:
144     case COL_RIGHT_TEXT:
145       model_types[i] = G_TYPE_STRING;
146       break;
147     }
148   }
149   /* special (invisible rows) - Text style, weight and player id */
150   model_types[i++] = G_TYPE_INT;        /* CITIZENS_DLG_COL_STYLE. */
151   model_types[i++] = G_TYPE_INT;        /* CITIZENS_DLG_COL_WEIGHT. */
152   model_types[i++] = G_TYPE_INT;        /* CITIZENS_DLG_COL_ID. */
153 
154   store = gtk_tree_store_newv(i, model_types);
155 
156   return store;
157 }
158 
159 /*****************************************************************************
160   Returns column to sort by by default
161 *****************************************************************************/
citizens_dialog_default_sort_column(void)162 static int citizens_dialog_default_sort_column(void) {
163   return 0;
164 }
165 
166 /*****************************************************************************
167   Create citizens dialog
168 *****************************************************************************/
169 static struct citizens_dialog
citizens_dialog_create(const struct city * pcity)170   *citizens_dialog_create(const struct city *pcity)
171 {
172   GtkWidget *frame, *sw;
173   struct citizens_dialog *pdialog = fc_malloc(sizeof(struct citizens_dialog));
174   int i;
175 
176   pdialog->pcity = pcity;
177   pdialog->store = citizens_dialog_store_new();
178   pdialog->sort
179     = gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(pdialog->store));
180   g_object_unref(pdialog->store);
181 
182   gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(pdialog->sort),
183                                        citizens_dialog_default_sort_column(),
184                                        GTK_SORT_DESCENDING);
185 
186   pdialog->list
187     = gtk_tree_view_new_with_model(GTK_TREE_MODEL(pdialog->sort));
188   gtk_widget_set_halign(pdialog->list, GTK_ALIGN_CENTER);
189   g_object_unref(pdialog->sort);
190 
191   for (i = 0; i < num_citizens_cols; i++) {
192     struct citizens_column *pcol;
193     GtkCellRenderer *renderer;
194     GtkTreeViewColumn *col;
195 
196     pcol = &citizens_cols[i];
197     col = NULL;
198 
199     switch (pcol->type) {
200     case COL_FLAG:
201       renderer = gtk_cell_renderer_pixbuf_new();
202       col = gtk_tree_view_column_new_with_attributes(_(pcol->title), renderer,
203               "pixbuf", i, NULL);
204       break;
205     case COL_TEXT:
206       renderer = gtk_cell_renderer_text_new();
207       g_object_set(renderer, "style-set", TRUE, "weight-set", TRUE, NULL);
208 
209       col = gtk_tree_view_column_new_with_attributes(_(pcol->title), renderer,
210               "text", i,
211               "style", CITIZENS_DLG_COL_STYLE,
212               "weight", CITIZENS_DLG_COL_WEIGHT,
213               NULL);
214       gtk_tree_view_column_set_sort_column_id(col, i);
215       break;
216     case COL_RIGHT_TEXT:
217       renderer = gtk_cell_renderer_text_new();
218       g_object_set(renderer, "style-set", TRUE, "weight-set", TRUE, NULL);
219 
220       col = gtk_tree_view_column_new_with_attributes(_(pcol->title), renderer,
221               "text", i,
222               "style", CITIZENS_DLG_COL_STYLE,
223               "weight", CITIZENS_DLG_COL_WEIGHT,
224               NULL);
225       gtk_tree_view_column_set_sort_column_id(col, i);
226       g_object_set(renderer, "xalign", 1.0, NULL);
227       gtk_tree_view_column_set_alignment(col, 1.0);
228       break;
229     case COL_COLOR:
230     case COL_BOOLEAN:
231       /* These are not used. */
232       fc_assert(pcol->type != COL_COLOR && pcol->type != COL_BOOLEAN);
233       continue;
234     }
235 
236     if (col) {
237       gtk_tree_view_append_column(GTK_TREE_VIEW(pdialog->list), col);
238     }
239   }
240 
241   gtk_widget_set_hexpand(GTK_WIDGET(pdialog->list), TRUE);
242   gtk_widget_set_vexpand(GTK_WIDGET(pdialog->list), TRUE);
243 
244   sw = gtk_scrolled_window_new(NULL, NULL);
245   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
246                                  GTK_POLICY_AUTOMATIC,
247                                  GTK_POLICY_AUTOMATIC);
248   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
249                                       GTK_SHADOW_NONE);
250   gtk_container_add(GTK_CONTAINER(sw), pdialog->list);
251 
252   frame = gtk_frame_new(_("Citizens"));
253   gtk_container_add(GTK_CONTAINER(frame), sw);
254 
255   pdialog->shell = frame;
256 
257   dialog_list_prepend(dialog_list, pdialog);
258 
259   citizens_dialog_refresh(pcity);
260 
261   return pdialog;
262 }
263 
264 /*****************************************************************************
265   Initialize citizens dialog
266 *****************************************************************************/
citizens_dialog_init(void)267 void citizens_dialog_init(void)
268 {
269   dialog_list = dialog_list_new();
270 }
271 
272 /*****************************************************************************
273   Free resources allocated for citizens dialog
274 *****************************************************************************/
citizens_dialog_done(void)275 void citizens_dialog_done(void)
276 {
277   dialog_list_destroy(dialog_list);
278 }
279 
280 /*****************************************************************************
281   Get citizen dialog of the given city
282 *****************************************************************************/
citizens_dialog_get(const struct city * pcity)283 static struct citizens_dialog *citizens_dialog_get(const struct city *pcity)
284 {
285   dialog_list_iterate(dialog_list, pdialog) {
286     if (pdialog->pcity == pcity) {
287       return pdialog;
288     }
289   } dialog_list_iterate_end;
290 
291   return NULL;
292 }
293 
294 /*****************************************************************************
295   Refresh citizen dialog of the given city
296 *****************************************************************************/
citizens_dialog_refresh(const struct city * pcity)297 void citizens_dialog_refresh(const struct city *pcity)
298 {
299   struct citizens_dialog *pdialog = citizens_dialog_get(pcity);
300 
301   if (pdialog == NULL) {
302     return;
303   }
304 
305   gtk_tree_store_clear(pdialog->store);
306   citizens_iterate(pcity, pslot, nationality) {
307     GtkTreeIter iter;
308 
309     gtk_tree_store_append(pdialog->store, &iter, NULL);
310     citizens_dialog_row(pdialog->store, &iter, pcity, pslot);
311   } citizens_iterate_end;
312 }
313 
314 /*****************************************************************************
315   Fills the citizens list with the data for 'pslot' at the row given by 'it'.
316 *****************************************************************************/
citizens_dialog_row(GtkTreeStore * store,GtkTreeIter * it,const struct city * pcity,const struct player_slot * pslot)317 static void citizens_dialog_row(GtkTreeStore *store, GtkTreeIter *it,
318                                 const struct city *pcity,
319                                 const struct player_slot *pslot)
320 {
321   GdkPixbuf *pixbuf;
322   int style = PANGO_STYLE_NORMAL, weight = PANGO_WEIGHT_NORMAL;
323   int k;
324 
325   for (k = 0; k < num_citizens_cols; k++) {
326     struct citizens_column *pcol = &citizens_cols[k];
327     switch (pcol->type) {
328     case COL_TEXT:
329     case COL_RIGHT_TEXT:
330       gtk_tree_store_set(store, it, k, pcol->func(pcity, pslot), -1);
331       break;
332     case COL_FLAG:
333       pixbuf = get_flag(nation_of_player(player_slot_get_player(pslot)));
334       if (pixbuf != NULL) {
335         gtk_tree_store_set(store, it, k, pixbuf, -1);
336         g_object_unref(pixbuf);
337       }
338       break;
339     case COL_COLOR:
340     case COL_BOOLEAN:
341       /* These are not used. */
342       fc_assert(pcol->type != COL_COLOR && pcol->type != COL_BOOLEAN);
343       continue;
344       break;
345     }
346   }
347 
348   if (city_owner(pcity)->slot == pslot) {
349     weight = PANGO_WEIGHT_BOLD;
350     style = PANGO_STYLE_NORMAL;
351   }
352 
353   gtk_tree_store_set(store, it,
354                      CITIZENS_DLG_COL_STYLE, style,
355                      CITIZENS_DLG_COL_WEIGHT, weight,
356                      CITIZENS_DLG_COL_ID, player_slot_index(pslot),
357                      -1);
358 }
359 
360 /*****************************************************************************
361   Close citizens dialog of one city
362 *****************************************************************************/
citizens_dialog_close(const struct city * pcity)363 void citizens_dialog_close(const struct city *pcity)
364 {
365   struct citizens_dialog *pdialog = citizens_dialog_get(pcity);
366   if (pdialog == NULL) {
367     return;
368   }
369 
370   gtk_widget_hide(pdialog->shell);
371 
372   dialog_list_remove(dialog_list, pdialog);
373 
374   gtk_widget_destroy(pdialog->shell);
375   free(pdialog);
376 }
377 
378 /*****************************************************************************
379   Make citizen dialog of the city visible.
380 *****************************************************************************/
citizens_dialog_display(const struct city * pcity)381 GtkWidget *citizens_dialog_display(const struct city *pcity)
382 {
383   struct citizens_dialog *pdialog = citizens_dialog_get(pcity);
384 
385   if (!pdialog) {
386     pdialog = citizens_dialog_create(pcity);
387   }
388 
389   gtk_widget_show_all(pdialog->shell);
390   citizens_dialog_refresh(pcity);
391 
392   return pdialog->shell;
393 }
394