1 /* Pioneers - Implementation of the excellent Settlers of Catan board game.
2  *   Go buy a copy.
3  *
4  * Copyright (C) 1999 Dave Cole
5  * Copyright (C) 2004,2011 Roland Clobus
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #include "config.h"
23 #include "frontend.h"
24 #include "histogram.h"
25 #include "theme.h"
26 
27 static const int DIALOG_HEIGHT = 270;
28 static const int DIALOG_WIDTH = 450;
29 static const int GRID_DIVISIONS = 4;
30 static const int BAR_SEPARATION = 3;
31 static const int CHIT_DIAGRAM_SEPARATION = 3;
32 static const int SPACING_AROUND = 6;
33 
34 static void histogram_update(gint roll);
35 
36 static GtkWidget *histogram_dlg;
37 static GtkWidget *histogram_area;
38 static gint last_roll;
39 
40 static int histogram[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
41 
42 /*
43  *
44  * Non-Gui stuff -- maintain dice histogram state
45  *
46  */
47 
histogram_dice_rolled(gint roll,G_GNUC_UNUSED gint playernum)48 void histogram_dice_rolled(gint roll, G_GNUC_UNUSED gint playernum)
49 {
50 	g_assert(roll >= 2 && roll <= 12);
51 
52 	++histogram[roll];
53 	if (histogram_dlg)
54 		histogram_update(roll);
55 }
56 
histogram_dice_retrieve(gint roll)57 static gint histogram_dice_retrieve(gint roll)
58 {
59 	g_assert(roll >= 2 && roll <= 12);
60 	return histogram[roll];
61 }
62 
63 /*
64  *
65  * GUI Stuff -- draw a pretty histogram picture
66  *
67  */
68 
draw_histogram_cb(GtkWidget * widget,cairo_t * cr,G_GNUC_UNUSED gpointer user_data)69 static gboolean draw_histogram_cb(GtkWidget * widget, cairo_t * cr,
70 				  G_GNUC_UNUSED gpointer user_data)
71 {
72 	gint w;
73 	gint h;
74 	gint total;
75 	gint max;
76 	gdouble le;
77 	gdouble mi;
78 	gdouble ri;
79 	gint grid_width;
80 	gint grid_height;
81 	gint grid_offset_x;
82 	gint grid_offset_y;
83 	gdouble by_36;
84 	gdouble expected_low_y, expected_high_y;
85 	gint i;
86 	gchar buff[30];
87 	gint label_width, label_height;	/* Maximum size of the labels of the y-axis */
88 	gint width, height;	/* size of the individual labels  */
89 	gdouble bar_width;
90 	PangoLayout *layout;
91 	gboolean seven_thrown;
92 	gboolean draw_labels_and_chits;
93 	gint CHIT_RADIUS;
94 	GdkPixbuf *pixbuf;
95 	GtkAllocation allocation;
96 	MapTheme *theme;
97 
98 	cairo_set_line_width(cr, 1.0);
99 
100 	gtk_widget_get_allocation(widget, &allocation);
101 	w = allocation.width;
102 	h = allocation.height;
103 
104 	/* Calculate the highest dice throw */
105 	max = 0;
106 	for (i = 2; i <= 12; i++) {
107 		if (histogram_dice_retrieve(i) > max) {
108 			max = histogram_dice_retrieve(i);
109 		}
110 		/* Make max a multiple of GRID_DIVISIONS */
111 		if (max % GRID_DIVISIONS != 0)
112 			max += GRID_DIVISIONS - (max % GRID_DIVISIONS);
113 	}
114 	if (max == 0)
115 		max = GRID_DIVISIONS;
116 
117 	/* Calculate size of the labels of the y-axis */
118 	sprintf(buff, "%d", max);
119 	layout = gtk_widget_create_pango_layout(widget, buff);
120 	pango_layout_get_pixel_size(layout, &label_width, &label_height);
121 
122 	CHIT_RADIUS = guimap_get_chit_radius(layout, TRUE);
123 
124 	/* Determine if the drawing area is large enough to draw the labels */
125 	draw_labels_and_chits = TRUE;
126 	if (label_width + (CHIT_RADIUS + 1) * 2 * 11 > w)
127 		draw_labels_and_chits = FALSE;
128 	if (label_height * 5 + CHIT_RADIUS * 2 > h)
129 		draw_labels_and_chits = FALSE;
130 
131 	grid_offset_x =
132 	    (draw_labels_and_chits ? label_width : 0) + SPACING_AROUND;
133 	grid_width = w - grid_offset_x - SPACING_AROUND;
134 	grid_offset_y = (draw_labels_and_chits ? label_height : 0);
135 	grid_height =
136 	    h - grid_offset_y -
137 	    (draw_labels_and_chits ? 2 * CHIT_RADIUS : 0) -
138 	    CHIT_DIAGRAM_SEPARATION;
139 
140 	/* horizontal grid */
141 	for (i = 0; i <= GRID_DIVISIONS; ++i) {
142 		gdouble y =
143 		    grid_offset_y + grid_height - 0.5 -
144 		    (gdouble) i * grid_height / GRID_DIVISIONS;
145 		gdk_cairo_set_source_rgba(cr, &lightblue);
146 		cairo_move_to(cr, grid_offset_x, y);
147 		cairo_line_to(cr, w - SPACING_AROUND, y);
148 		cairo_stroke(cr);
149 
150 		if (draw_labels_and_chits) {
151 			sprintf(buff, "%d", i * max / GRID_DIVISIONS);
152 			pango_layout_set_text(layout, buff, -1);
153 			pango_layout_get_pixel_size(layout, &width,
154 						    &height);
155 			gdk_cairo_set_source_rgba(cr, &black);
156 			cairo_move_to(cr,
157 				      label_width - width + SPACING_AROUND,
158 				      y - height / 2.0);
159 			pango_cairo_show_layout(cr, layout);
160 		}
161 	}
162 
163 	bar_width = (grid_width - 12 * BAR_SEPARATION) / 11.0;
164 	grid_offset_x += BAR_SEPARATION;
165 
166 	/* the filling of the bars */
167 	theme = theme_get_current();
168 	if (gdk_pixbuf_get_has_alpha
169 	    (theme_get_terrain_pixbuf(SEA_TERRAIN))) {
170 		GdkPixbuf *p;
171 		int w;
172 		int h;
173 
174 		/* The image has transparency, so it is probably a hexagon.
175 		   Take the middle rectangle.
176 		   It doesn't tile the best, but it is better than tiling the hexagon.
177 		 */
178 		w = gdk_pixbuf_get_width(theme->scaledata
179 					 [SEA_TERRAIN].native_image);
180 		h = gdk_pixbuf_get_height(theme->scaledata
181 					  [SEA_TERRAIN].native_image);
182 		p = gdk_pixbuf_new_subpixbuf(theme->scaledata
183 					     [SEA_TERRAIN].native_image, 0,
184 					     h / 4, w, h / 2);
185 		pixbuf = gdk_pixbuf_copy(p);
186 		g_object_unref(p);
187 	} else {
188 		pixbuf =
189 		    gdk_pixbuf_copy(theme->
190 				    scaledata[SEA_TERRAIN].native_image);
191 	}
192 
193 	/* histogram bars */
194 	for (i = 2; i <= 12; i++) {
195 		gdouble bh =
196 		    (gdouble) grid_height * histogram_dice_retrieve(i) /
197 		    max + 0.5;
198 		gdouble x =
199 		    grid_offset_x + (i - 2) * (bar_width + BAR_SEPARATION);
200 		gdk_cairo_set_source_pixbuf(cr, pixbuf, grid_offset_x,
201 					    grid_offset_y);
202 		cairo_pattern_set_extend(cairo_get_source(cr),
203 					 CAIRO_EXTEND_REPEAT);
204 		cairo_rectangle(cr, x, grid_height + grid_offset_y - bh,
205 				bar_width, bh);
206 		cairo_fill(cr);
207 
208 		if (draw_labels_and_chits) {
209 			sprintf(buff, "%d", histogram_dice_retrieve(i));
210 			pango_layout_set_markup(layout, buff, -1);
211 			pango_layout_get_pixel_size(layout, &width,
212 						    &height);
213 			gdk_cairo_set_source_rgba(cr, &black);
214 			cairo_move_to(cr, x + (bar_width - width) / 2,
215 				      grid_height + grid_offset_y - bh -
216 				      height);
217 			pango_cairo_show_layout(cr, layout);
218 
219 			draw_dice_roll(layout, cr, x + bar_width / 2,
220 				       h - CHIT_RADIUS - 1,
221 				       CHIT_RADIUS,
222 				       i, SEA_TERRAIN, i == last_roll);
223 		}
224 	}
225 	g_object_unref(pixbuf);
226 
227 	/* expected value */
228 	seven_thrown = histogram_dice_retrieve(7) != 0;
229 
230 	total = 0;
231 	for (i = 2; i <= 12; i++) {
232 		total += histogram_dice_retrieve(i);
233 	}
234 
235 	by_36 = total * grid_height / max / (seven_thrown ? 36.0 : 30.0);
236 	expected_low_y = grid_height + grid_offset_y - 1 - by_36;
237 	expected_high_y =
238 	    grid_height + grid_offset_y - 1 -
239 	    (seven_thrown ? 6 : 5) * by_36;
240 
241 	le = grid_offset_x + bar_width / 2;
242 	mi = le + 5 * (bar_width + BAR_SEPARATION);
243 	ri = mi + 5 * (bar_width + BAR_SEPARATION);
244 
245 	gdk_cairo_set_source_rgba(cr, &red);
246 	cairo_move_to(cr, le, expected_low_y);
247 	cairo_line_to(cr,
248 		      mi - (seven_thrown ? 0 : bar_width + BAR_SEPARATION),
249 		      expected_high_y);
250 	cairo_move_to(cr,
251 		      mi + (seven_thrown ? 0 : bar_width + BAR_SEPARATION),
252 		      expected_high_y);
253 	cairo_line_to(cr, ri, expected_low_y);
254 	cairo_stroke(cr);
255 
256 	g_object_unref(layout);
257 
258 	return TRUE;
259 }
260 
histogram_destroyed_cb(GtkWidget * widget,gpointer arg)261 static void histogram_destroyed_cb(GtkWidget * widget, gpointer arg)
262 {
263 	gtk_widget_destroyed(histogram_area, &histogram_area);
264 	gtk_widget_destroyed(widget, arg);
265 }
266 
histogram_create_dlg(GtkWindow * parent_window)267 GtkWidget *histogram_create_dlg(GtkWindow * parent_window)
268 {
269 	GtkWidget *dlg_vbox;
270 
271 	if (histogram_dlg != NULL) {
272 		return histogram_dlg;
273 	}
274 
275 	/* Dialog caption */
276 	histogram_dlg = gtk_dialog_new_with_buttons(_("Dice Histogram"),
277 						    parent_window,
278 						    GTK_DIALOG_DESTROY_WITH_PARENT,
279 						    /* Button text */
280 						    _("_Close"),
281 						    GTK_RESPONSE_CLOSE,
282 						    NULL);
283 	g_signal_connect(G_OBJECT(histogram_dlg), "destroy",
284 			 G_CALLBACK(histogram_destroyed_cb),
285 			 &histogram_dlg);
286 	gtk_window_set_default_size(GTK_WINDOW(histogram_dlg),
287 				    DIALOG_WIDTH, DIALOG_HEIGHT);
288 
289 	dlg_vbox = gtk_dialog_get_content_area(GTK_DIALOG(histogram_dlg));
290 
291 	histogram_area = gtk_drawing_area_new();
292 	g_signal_connect(G_OBJECT(histogram_area), "draw",
293 			 G_CALLBACK(draw_histogram_cb), NULL);
294 	gtk_box_pack_start(GTK_BOX(dlg_vbox), histogram_area, TRUE, TRUE,
295 			   SPACING_AROUND);
296 	gtk_widget_show(histogram_area);
297 
298 	gtk_widget_show(histogram_dlg);
299 	g_signal_connect(histogram_dlg, "response",
300 			 G_CALLBACK(gtk_widget_destroy), NULL);
301 
302 	histogram_update(0);
303 
304 	return histogram_dlg;
305 }
306 
histogram_update(gint roll)307 static void histogram_update(gint roll)
308 {
309 	last_roll = roll;
310 	gtk_widget_queue_draw(histogram_area);
311 }
312 
histogram_theme_changed(void)313 static void histogram_theme_changed(void)
314 {
315 	if (histogram_dlg)
316 		gtk_widget_queue_draw(histogram_area);
317 }
318 
histogram_init(void)319 void histogram_init(void)
320 {
321 	theme_register_callback(G_CALLBACK(histogram_theme_changed));
322 }
323 
histogram_reset(void)324 void histogram_reset(void)
325 {
326 	gint i;
327 	for (i = 2; i <= 12; ++i)
328 		histogram[i] = 0;
329 	if (histogram_dlg)
330 		histogram_update(0);
331 }
332