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) 2003, 2006 Bas Wijnen <shevek@fmf.nl>
6  * Copyright (C) 2004-2008 Roland Clobus <rclobus@bigfoot.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 
23 #include "config.h"
24 #include <stdlib.h>
25 #include "colors.h"
26 #include "frontend.h"
27 #include "log.h"
28 #include "common_gtk.h"
29 #include "player-icon.h"
30 #include "audio.h"
31 
32 static void player_show_connected_at_iter(gint player_num,
33 					  gboolean connected,
34 					  GtkTreeIter * iter);
35 
36 static GdkRGBA ps_settlement = { 0.73, 0.00, 0.00, 1.0 };
37 static GdkRGBA ps_city = { 1.00, 0.00, 0.00, 1.0 };
38 static GdkRGBA ps_city_wall = { 1.00, 0.00, 0.00, 1.0 };
39 static GdkRGBA ps_largest = { 0.11, 0.71, 0.93, 1.0 };
40 static GdkRGBA ps_soldier = { 0.90, 0.56, 0.09, 1.0 };
41 static GdkRGBA ps_resource = { 0.00, 0.00, 1.00, 1.0 };
42 static GdkRGBA ps_development = { 0.78, 0.78, 0.07, 1.0 };
43 static GdkRGBA ps_building = { 0.04, 0.93, 0.54, 1.0 };
44 
45 typedef struct {
46 	const gchar *singular;
47 	const gchar *plural;
48 	GdkRGBA *textcolor;
49 } Statistic;
50 
51 static Statistic statistics[] = {
52 	{ N_("Settlement"), N_("Settlements"), &ps_settlement },
53 	{ N_("City"), N_("Cities"), &ps_city },
54 	{ N_("City wall"), N_("City walls"), &ps_city_wall },
55 	{ N_("Largest army"), NULL, &ps_largest },
56 	{ N_("Longest road"), NULL, &ps_largest },
57 	{ N_("Chapel"), N_("Chapels"), &ps_building },
58 	{ N_("Pioneer university"), N_("Pioneer universities"),
59 	 &ps_building },
60 	{ N_("Governor's house"), N_("Governor's houses"), &ps_building },
61 	{ N_("Library"), N_("Libraries"), &ps_building },
62 	{ N_("Market"), N_("Markets"), &ps_building },
63 	{ N_("Soldier"), N_("Soldiers"), &ps_soldier },
64 	{ N_("Resource card"), N_("Resource cards"), &ps_resource },
65 	{ N_("Development card"), N_("Development cards"), &ps_development }
66 };
67 
68 enum {
69 	SUMMARY_COLUMN_PLAYER_ICON, /**< Player icon */
70 	SUMMARY_COLUMN_PLAYER_NUM, /**< Internal: player number */
71 	SUMMARY_COLUMN_TEXT, /**< Description of the items */
72 	SUMMARY_COLUMN_TEXT_COLOUR, /**< Colour of the description */
73 	SUMMARY_COLUMN_SCORE, /**< Score of the items (as string) */
74 	SUMMARY_COLUMN_STATISTIC, /**< enum Statistic value+1, or 0 if not in the enum */
75 	SUMMARY_COLUMN_POINTS_ID, /**< Id of points, or -1 */
76 	SUMMARY_COLUMN_LAST
77 };
78 
79 static GtkListStore *summary_store; /**< the player summary data */
80 static GtkWidget *summary_widget; /**< the player summary widget */
81 static gboolean summary_color_enabled = TRUE;
82 
83 /** Structure to find combination of player and statistic */
84 struct Player_statistic {
85 	enum TFindResult result;
86 	GtkTreeIter iter;
87 	gint player_num;
88 	gint statistic;
89 };
90 
91 /** Structure to find combination of player and points */
92 struct Player_point {
93 	enum TFindResult result;
94 	GtkTreeIter iter;
95 	gint player_num;
96 	gint point_id;
97 };
98 
99 static GtkWidget *turn_area;	/** turn indicator in status bar */
100 /** Width for each icon */
101 static const gint turn_area_icon_width = 28;
102 /** Separation between each icon */
103 static const gint turn_area_icon_separation = 2;
104 
player_init(void)105 void player_init(void)
106 {
107 	colors_init();
108 	playericon_init();
109 }
110 
player_color(gint player_num)111 GdkRGBA *player_color(gint player_num)
112 {
113 	return colors_get_player(player_num);
114 }
115 
player_or_spectator_color(gint player_num)116 GdkRGBA *player_or_spectator_color(gint player_num)
117 {
118 	if (player_is_spectator(player_num)) {
119 		/* spectator color is always black */
120 		return &black;
121 	}
122 	return colors_get_player(player_num);
123 }
124 
player_create_icon(gint player_num,gboolean connected)125 GdkPixbuf *player_create_icon(gint player_num, gboolean connected)
126 {
127 	GdkPixbuf *pixbuf;
128 	gint width;
129 	gint height;
130 	cairo_surface_t *surface;
131 
132 	gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
133 	surface =
134 	    playericon_create_icon(player_get_style(player_num),
135 				   player_or_spectator_color(player_num),
136 				   player_is_spectator(player_num),
137 				   connected, width, height);
138 	pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, width, height);
139 	cairo_surface_destroy(surface);
140 	return pixbuf;
141 }
142 
143 /** Locate a line suitable for the statistic */
summary_locate_statistic(GtkTreeModel * model,G_GNUC_UNUSED GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)144 static gboolean summary_locate_statistic(GtkTreeModel * model,
145 					 G_GNUC_UNUSED GtkTreePath * path,
146 					 GtkTreeIter * iter,
147 					 gpointer user_data)
148 {
149 	struct Player_statistic *ps =
150 	    (struct Player_statistic *) user_data;
151 	gint current_player;
152 	gint current_statistic;
153 	gtk_tree_model_get(model, iter,
154 			   SUMMARY_COLUMN_PLAYER_NUM, &current_player,
155 			   SUMMARY_COLUMN_STATISTIC, &current_statistic,
156 			   -1);
157 	if (current_player > ps->player_num) {
158 		ps->result = FIND_MATCH_INSERT_BEFORE;
159 		ps->iter = *iter;
160 		return TRUE;
161 	} else if (current_player == ps->player_num) {
162 		if (current_statistic > ps->statistic) {
163 			ps->result = FIND_MATCH_INSERT_BEFORE;
164 			ps->iter = *iter;
165 			return TRUE;
166 		} else if (current_statistic == ps->statistic) {
167 			ps->result = FIND_MATCH_EXACT;
168 			ps->iter = *iter;
169 			return TRUE;
170 		}
171 	}
172 	return FALSE;
173 }
174 
175 /** Locate a line suitable for the statistic */
summary_locate_point(GtkTreeModel * model,G_GNUC_UNUSED GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)176 static gboolean summary_locate_point(GtkTreeModel * model,
177 				     G_GNUC_UNUSED GtkTreePath * path,
178 				     GtkTreeIter * iter,
179 				     gpointer user_data)
180 {
181 	struct Player_point *pp = (struct Player_point *) user_data;
182 	gint current_player;
183 	gint current_point_id;
184 	gint current_statistic;
185 
186 	gtk_tree_model_get(model, iter,
187 			   SUMMARY_COLUMN_PLAYER_NUM, &current_player,
188 			   SUMMARY_COLUMN_STATISTIC, &current_statistic,
189 			   SUMMARY_COLUMN_POINTS_ID, &current_point_id,
190 			   -1);
191 	if (current_player > pp->player_num) {
192 		pp->result = FIND_MATCH_INSERT_BEFORE;
193 		pp->iter = *iter;
194 		return TRUE;
195 	} else if (current_player == pp->player_num) {
196 		if (current_statistic >= STAT_SOLDIERS) {
197 			pp->result = FIND_MATCH_INSERT_BEFORE;
198 			pp->iter = *iter;
199 			return TRUE;
200 		}
201 		if (current_point_id > pp->point_id) {
202 			pp->result = FIND_MATCH_INSERT_BEFORE;
203 			pp->iter = *iter;
204 			return TRUE;
205 		} else if (current_point_id == pp->point_id) {
206 			pp->result = FIND_MATCH_EXACT;
207 			pp->iter = *iter;
208 			return TRUE;
209 		}
210 	}
211 	return FALSE;
212 }
213 
214 /** Function to redisplay the running point total for the indicated player */
refresh_victory_point_total(int player_num)215 static void refresh_victory_point_total(int player_num)
216 {
217 	gchar points[16];
218 	GtkTreeIter iter;
219 	enum TFindResult found;
220 
221 	g_return_if_fail(player_num >= 0 && player_num < num_players());
222 
223 	found =
224 	    find_integer_in_tree(GTK_TREE_MODEL(summary_store), &iter,
225 				 SUMMARY_COLUMN_PLAYER_NUM, player_num);
226 
227 	if (found == FIND_MATCH_EXACT) {
228 		snprintf(points, sizeof(points), "%d",
229 			 player_get_score(player_num));
230 		gtk_list_store_set(summary_store, &iter,
231 				   SUMMARY_COLUMN_SCORE, points, -1);
232 	}
233 }
234 
235 /** Apply colors to the summary */
summary_apply_colors_cb(GtkTreeModel * model,G_GNUC_UNUSED GtkTreePath * path,GtkTreeIter * iter,G_GNUC_UNUSED gpointer user_data)236 static gboolean summary_apply_colors_cb(GtkTreeModel * model,
237 					G_GNUC_UNUSED GtkTreePath * path,
238 					GtkTreeIter * iter,
239 					G_GNUC_UNUSED gpointer user_data)
240 {
241 	gint current_statistic;
242 	gint point_id;
243 
244 	gtk_tree_model_get(model, iter,
245 			   SUMMARY_COLUMN_STATISTIC, &current_statistic,
246 			   SUMMARY_COLUMN_POINTS_ID, &point_id, -1);
247 	if (current_statistic > 0)
248 		gtk_list_store_set(summary_store, iter,
249 				   SUMMARY_COLUMN_TEXT_COLOUR,
250 				   summary_color_enabled ?
251 				   statistics[current_statistic -
252 					      1].textcolor : &black, -1);
253 	else if (point_id >= 0)
254 		gtk_list_store_set(summary_store, iter,
255 				   SUMMARY_COLUMN_TEXT_COLOUR,
256 				   summary_color_enabled ?
257 				   &ps_largest : &black, -1);
258 	return FALSE;
259 }
260 
set_color_summary(gboolean flag)261 void set_color_summary(gboolean flag)
262 {
263 	if (flag != summary_color_enabled) {
264 		summary_color_enabled = flag;
265 		if (summary_store)
266 			gtk_tree_model_foreach(GTK_TREE_MODEL
267 					       (summary_store),
268 					       summary_apply_colors_cb,
269 					       NULL);
270 	}
271 }
272 
frontend_new_statistics(gint player_num,StatisticType type,G_GNUC_UNUSED gint num)273 void frontend_new_statistics(gint player_num, StatisticType type,
274 			     G_GNUC_UNUSED gint num)
275 {
276 	Player *player = player_get(player_num);
277 	gint value;
278 	gchar points[16];
279 	GtkTreeIter iter;
280 	struct Player_statistic ps;
281 
282 	value = player->statistics[type];
283 	if (stat_get_vp_value(type) > 0)
284 		refresh_victory_point_total(player_num);
285 
286 	ps.result = FIND_NO_MATCH;
287 	ps.player_num = player_num;
288 	ps.statistic = type + 1;
289 	gtk_tree_model_foreach(GTK_TREE_MODEL(summary_store),
290 			       summary_locate_statistic, &ps);
291 
292 	if (value == 0) {
293 		if (ps.result == FIND_MATCH_EXACT)
294 			gtk_list_store_remove(summary_store, &ps.iter);
295 	} else {
296 		gchar *desc;
297 		if (value == 1) {
298 			if (statistics[type].plural != NULL)
299 				desc = g_strdup_printf("%d %s", value,
300 						       gettext(statistics
301 							       [type].
302 							       singular));
303 			else
304 				desc = g_strdup(gettext
305 						(statistics
306 						 [type].singular));
307 		} else
308 			desc = g_strdup_printf("%d %s", value,
309 					       gettext(statistics
310 						       [type].plural));
311 		if (stat_get_vp_value(type) > 0)
312 			sprintf(points, "%d",
313 				value * stat_get_vp_value(type));
314 		else
315 			strcpy(points, "");
316 
317 		switch (ps.result) {
318 		case FIND_NO_MATCH:
319 			gtk_list_store_append(summary_store, &iter);
320 			break;
321 		case FIND_MATCH_INSERT_BEFORE:
322 			gtk_list_store_insert_before(summary_store, &iter,
323 						     &ps.iter);
324 			break;
325 		case FIND_MATCH_EXACT:
326 			iter = ps.iter;
327 			break;
328 		default:
329 			g_error("unknown case in frontend_new_statistics");
330 		};
331 		gtk_list_store_set(summary_store, &iter,
332 				   SUMMARY_COLUMN_PLAYER_NUM, player_num,
333 				   SUMMARY_COLUMN_TEXT, desc,
334 				   SUMMARY_COLUMN_TEXT_COLOUR,
335 				   summary_color_enabled ?
336 				   statistics[type].textcolor : &black,
337 				   SUMMARY_COLUMN_STATISTIC, type + 1,
338 				   SUMMARY_COLUMN_POINTS_ID, -1,
339 				   SUMMARY_COLUMN_SCORE, points, -1);
340 		g_free(desc);
341 	}
342 	frontend_gui_update();
343 }
344 
frontend_new_points(gint player_num,Points * points,gboolean added)345 void frontend_new_points(gint player_num, Points * points, gboolean added)
346 {
347 	GtkTreeIter iter;
348 	struct Player_point pp;
349 	gchar score[16];
350 
351 	refresh_victory_point_total(player_num);
352 
353 	pp.result = FIND_NO_MATCH;
354 	pp.player_num = player_num;
355 	pp.point_id = points->id;
356 	gtk_tree_model_foreach(GTK_TREE_MODEL(summary_store),
357 			       summary_locate_point, &pp);
358 
359 	if (!added) {
360 		if (pp.result != FIND_MATCH_EXACT)
361 			g_error("cannot remove point");
362 		gtk_list_store_remove(summary_store, &pp.iter);
363 		frontend_gui_update();
364 		return;
365 	}
366 
367 	switch (pp.result) {
368 	case FIND_NO_MATCH:
369 		gtk_list_store_append(summary_store, &iter);
370 		break;
371 	case FIND_MATCH_INSERT_BEFORE:
372 		gtk_list_store_insert_before(summary_store, &iter,
373 					     &pp.iter);
374 		break;
375 	case FIND_MATCH_EXACT:
376 		iter = pp.iter;
377 		break;
378 	default:
379 		g_error("unknown case in frontend_new_points");
380 	};
381 	snprintf(score, sizeof(score), "%d", points->points);
382 	gtk_list_store_set(summary_store, &iter,
383 			   SUMMARY_COLUMN_PLAYER_NUM, player_num,
384 			   SUMMARY_COLUMN_TEXT, _(points->name),
385 			   SUMMARY_COLUMN_TEXT_COLOUR,
386 			   summary_color_enabled ? &ps_largest : &black,
387 			   SUMMARY_COLUMN_STATISTIC, 0,
388 			   SUMMARY_COLUMN_POINTS_ID, points->id,
389 			   SUMMARY_COLUMN_SCORE, score, -1);
390 	frontend_gui_update();
391 }
392 
player_create_find_player(gint player_num,GtkTreeIter * iter)393 static void player_create_find_player(gint player_num, GtkTreeIter * iter)
394 {
395 	GtkTreeIter found_iter;
396 	enum TFindResult result;
397 
398 	/* Search for a place to add information about the player/spectator */
399 	result =
400 	    find_integer_in_tree(GTK_TREE_MODEL(summary_store),
401 				 &found_iter, SUMMARY_COLUMN_PLAYER_NUM,
402 				 player_num);
403 
404 	switch (result) {
405 	case FIND_NO_MATCH:
406 		gtk_list_store_append(summary_store, iter);
407 		gtk_list_store_set(summary_store, iter,
408 				   SUMMARY_COLUMN_PLAYER_NUM, player_num,
409 				   SUMMARY_COLUMN_POINTS_ID, -1, -1);
410 		break;
411 	case FIND_MATCH_INSERT_BEFORE:
412 		gtk_list_store_insert_before(summary_store, iter,
413 					     &found_iter);
414 		gtk_list_store_set(summary_store, iter,
415 				   SUMMARY_COLUMN_PLAYER_NUM, player_num,
416 				   SUMMARY_COLUMN_POINTS_ID, -1, -1);
417 		break;
418 	case FIND_MATCH_EXACT:
419 		*iter = found_iter;
420 		break;
421 	default:
422 		g_error("unknown case in player_create_find_player");
423 	};
424 }
425 
frontend_player_name(gint player_num,const gchar * name)426 void frontend_player_name(gint player_num, const gchar * name)
427 {
428 	GtkTreeIter iter;
429 
430 	player_create_find_player(player_num, &iter);
431 	gtk_list_store_set(summary_store, &iter,
432 			   SUMMARY_COLUMN_TEXT, name, -1);
433 
434 	player_show_connected_at_iter(player_num, TRUE, &iter);
435 	if (callback_mode != MODE_INIT)
436 		play_sound(SOUND_ANNOUNCE);
437 
438 	chat_player_name(player_num, name);
439 }
440 
frontend_player_style(gint player_num,G_GNUC_UNUSED const gchar * style)441 void frontend_player_style(gint player_num,
442 			   G_GNUC_UNUSED const gchar * style)
443 {
444 	GtkTreeIter iter;
445 
446 	player_create_find_player(player_num, &iter);
447 	player_show_connected_at_iter(player_num, TRUE, &iter);
448 	chat_player_style(player_num);
449 }
450 
frontend_spectator_name(gint spectator_num,const gchar * name)451 void frontend_spectator_name(gint spectator_num, const gchar * name)
452 {
453 	GtkTreeIter iter;
454 
455 	player_create_find_player(spectator_num, &iter);
456 	gtk_list_store_set(summary_store, &iter,
457 			   SUMMARY_COLUMN_TEXT, name, -1);
458 	if (callback_mode != MODE_INIT)
459 		play_sound(SOUND_ANNOUNCE);
460 
461 	chat_player_name(spectator_num, name);
462 }
463 
frontend_player_quit(gint player_num)464 void frontend_player_quit(gint player_num)
465 {
466 	GtkTreeIter iter;
467 
468 	player_create_find_player(player_num, &iter);
469 	player_show_connected_at_iter(player_num, FALSE, &iter);
470 
471 	chat_player_quit(player_num);
472 }
473 
frontend_spectator_quit(gint spectator_num)474 void frontend_spectator_quit(gint spectator_num)
475 {
476 	GtkTreeIter iter;
477 
478 	player_create_find_player(spectator_num, &iter);
479 	gtk_list_store_remove(summary_store, &iter);
480 
481 	chat_spectator_quit(spectator_num);
482 }
483 
player_show_connected_at_iter(gint player_num,gboolean connected,GtkTreeIter * iter)484 static void player_show_connected_at_iter(gint player_num,
485 					  gboolean connected,
486 					  GtkTreeIter * iter)
487 {
488 	GdkPixbuf *pixbuf = player_create_icon(player_num, connected);
489 
490 	gtk_list_store_set(summary_store, iter,
491 			   SUMMARY_COLUMN_PLAYER_ICON, pixbuf, -1);
492 	g_object_unref(pixbuf);
493 }
494 
495 /* Get the top and bottom row for player summary and make sure player
496  * is visible
497  */
player_show_summary(gint player_num)498 static void player_show_summary(gint player_num)
499 {
500 	GtkTreeIter found_iter;
501 	enum TFindResult result;
502 	gboolean scroll_to_end = FALSE;
503 
504 	result =
505 	    find_integer_in_tree(GTK_TREE_MODEL(summary_store),
506 				 &found_iter, SUMMARY_COLUMN_PLAYER_NUM,
507 				 player_num + 1);
508 
509 	if (result == FIND_NO_MATCH) {
510 		scroll_to_end = TRUE;
511 	} else {
512 		GtkTreePath *path =
513 		    gtk_tree_model_get_path(GTK_TREE_MODEL(summary_store),
514 					    &found_iter);
515 		if (gtk_tree_path_prev(path))
516 			gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW
517 						     (summary_widget),
518 						     path, NULL, FALSE,
519 						     0.0, 0.0);
520 		gtk_tree_path_free(path);
521 	}
522 
523 	result =
524 	    find_integer_in_tree(GTK_TREE_MODEL(summary_store),
525 				 &found_iter, SUMMARY_COLUMN_PLAYER_NUM,
526 				 player_num);
527 	if (result != FIND_NO_MATCH) {
528 		GtkTreePath *path =
529 		    gtk_tree_model_get_path(GTK_TREE_MODEL(summary_store),
530 					    &found_iter);
531 		gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(summary_widget),
532 					     path, NULL, scroll_to_end,
533 					     0.0, 0.0);
534 		gtk_tree_view_set_cursor(GTK_TREE_VIEW(summary_widget),
535 					 path, NULL, FALSE);
536 		gtk_tree_path_free(path);
537 	}
538 }
539 
player_build_summary(void)540 GtkWidget *player_build_summary(void)
541 {
542 	GtkWidget *vbox;
543 	GtkWidget *label;
544 	GtkWidget *scroll_win;
545 	GtkWidget *alignment;
546 	GtkCellRenderer *renderer;
547 	GtkTreeViewColumn *column;
548 
549 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
550 	gtk_widget_show(vbox);
551 
552 	alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
553 	gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 3, 3);
554 	gtk_widget_show(alignment);
555 	gtk_box_pack_start(GTK_BOX(vbox), alignment, FALSE, FALSE, 0);
556 
557 	label = gtk_label_new(NULL);
558 	/* Caption for the overview of the points and card of other players */
559 	gtk_label_set_markup(GTK_LABEL(label), _("<b>Player summary</b>"));
560 	gtk_widget_show(label);
561 	gtk_label_set_xalign(GTK_LABEL(label), 0.0);
562 	gtk_container_add(GTK_CONTAINER(alignment), label);
563 
564 	summary_store = gtk_list_store_new(SUMMARY_COLUMN_LAST, GDK_TYPE_PIXBUF,	/* player icon */
565 					   G_TYPE_INT,	/* player number */
566 					   G_TYPE_STRING,	/* text */
567 					   GDK_TYPE_RGBA,	/* text colour */
568 					   G_TYPE_STRING,	/* score */
569 					   G_TYPE_INT,	/* statistic */
570 					   G_TYPE_INT);	/* points */
571 	summary_widget =
572 	    gtk_tree_view_new_with_model(GTK_TREE_MODEL(summary_store));
573 
574 	column = gtk_tree_view_column_new_with_attributes("",
575 							  gtk_cell_renderer_pixbuf_new
576 							  (), "pixbuf",
577 							  SUMMARY_COLUMN_PLAYER_ICON,
578 							  NULL);
579 	gtk_tree_view_column_set_sizing(column,
580 					GTK_TREE_VIEW_COLUMN_GROW_ONLY);
581 	gtk_tree_view_append_column(GTK_TREE_VIEW(summary_widget), column);
582 
583 	column = gtk_tree_view_column_new_with_attributes("",
584 							  gtk_cell_renderer_text_new
585 							  (), "text",
586 							  SUMMARY_COLUMN_TEXT,
587 							  "foreground-rgba",
588 							  SUMMARY_COLUMN_TEXT_COLOUR,
589 							  NULL);
590 	gtk_tree_view_column_set_sizing(column,
591 					GTK_TREE_VIEW_COLUMN_AUTOSIZE);
592 	gtk_tree_view_column_set_expand(column, TRUE);
593 	gtk_tree_view_append_column(GTK_TREE_VIEW(summary_widget), column);
594 
595 	renderer = gtk_cell_renderer_text_new();
596 	column = gtk_tree_view_column_new_with_attributes("",
597 							  renderer,
598 							  "text",
599 							  SUMMARY_COLUMN_SCORE,
600 							  "foreground-rgba",
601 							  SUMMARY_COLUMN_TEXT_COLOUR,
602 							  NULL);
603 	g_object_set(renderer, "xalign", 1.0f, NULL);
604 	gtk_tree_view_column_set_sizing(column,
605 					GTK_TREE_VIEW_COLUMN_GROW_ONLY);
606 	gtk_tree_view_append_column(GTK_TREE_VIEW(summary_widget), column);
607 
608 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(summary_widget),
609 					  FALSE);
610 	gtk_widget_show(summary_widget);
611 
612 	scroll_win = gtk_scrolled_window_new(NULL, NULL);
613 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW
614 					    (scroll_win), GTK_SHADOW_IN);
615 	gtk_widget_show(scroll_win);
616 	gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0);
617 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win),
618 				       GTK_POLICY_AUTOMATIC,
619 				       GTK_POLICY_AUTOMATIC);
620 
621 	gtk_container_add(GTK_CONTAINER(scroll_win), summary_widget);
622 
623 	return vbox;
624 }
625 
draw_turn_area_cb(GtkWidget * widget,cairo_t * cr,G_GNUC_UNUSED gpointer user_data)626 static gboolean draw_turn_area_cb(GtkWidget * widget, cairo_t * cr,
627 				  G_GNUC_UNUSED gpointer user_data)
628 {
629 	gint offset;
630 	gint idx;
631 	GtkAllocation allocation;
632 
633 	gtk_widget_get_allocation(widget, &allocation);
634 	offset = 0;
635 	for (idx = 0; idx < num_players(); idx++) {
636 		gdk_cairo_set_source_rgba(cr, player_color(idx));
637 		cairo_rectangle(cr, offset, 0, turn_area_icon_width,
638 				allocation.height);
639 		cairo_fill(cr);
640 
641 		gdk_cairo_set_source_rgba(cr, &black);
642 		if (idx == current_player()) {
643 			cairo_set_line_width(cr, 3.0);
644 			cairo_rectangle(cr, offset + 1.5, 1.5,
645 					turn_area_icon_width - 3,
646 					allocation.height - 3);
647 		} else {
648 			cairo_set_line_width(cr, 1.0);
649 			cairo_rectangle(cr, offset + 0.5, 0.5,
650 					turn_area_icon_width - 1,
651 					allocation.height - 1);
652 		}
653 		cairo_stroke(cr);
654 
655 		offset += turn_area_icon_width + turn_area_icon_separation;
656 	}
657 	return TRUE;
658 }
659 
player_build_turn_area(void)660 GtkWidget *player_build_turn_area(void)
661 {
662 	turn_area = gtk_drawing_area_new();
663 	g_signal_connect(G_OBJECT(turn_area), "draw",
664 			 G_CALLBACK(draw_turn_area_cb), NULL);
665 	gtk_widget_set_size_request(turn_area,
666 				    turn_area_icon_width * num_players() +
667 				    turn_area_icon_separation *
668 				    (num_players() - 1), -1);
669 	gtk_widget_show(turn_area);
670 
671 	return turn_area;
672 }
673 
set_num_players(gint num)674 void set_num_players(gint num)
675 {
676 	gtk_widget_set_size_request(turn_area,
677 				    turn_area_icon_width * num +
678 				    turn_area_icon_separation * (num - 1),
679 				    -1);
680 }
681 
player_show_current(gint player_num)682 void player_show_current(gint player_num)
683 {
684 	gtk_widget_queue_draw(turn_area);
685 	player_show_summary(player_num);
686 }
687 
player_clear_summary(void)688 void player_clear_summary(void)
689 {
690 	gtk_list_store_clear(GTK_LIST_STORE(summary_store));
691 }
692