1 /*
2  * Gyatzee: Gnomified Yahtzee game.
3  * (C) 1998 the Free Software Foundation
4  *
5  * File:   clist.c
6  *
7  * Author: Scott Heavner
8  *
9  *   Scoring is done using a GtkTreeView and handled in this file.
10  *
11  *   Variables are exported in gyahtzee.h
12  *
13  *   This file is largely based upon GTT code by Eckehard Berns.
14  *
15  *   This program is free software; you can redistribute it and/or modify
16  *   it under the terms of the GNU General Public License as published by
17  *   the Free Software Foundation; either version 2 of the License, or
18  *   (at your option) any later version.
19  *
20  *   This program is distributed in the hope that it will be useful,
21  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
22  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  *   GNU General Public License for more details.
24  *
25  *   You should have received a copy of the GNU General Public License
26  *   along with this program; if not, write to the Free Software
27  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28  */
29 #include <gtk/gtk.h>
30 #include <gdk/gdk.h>
31 
32 #include <stdio.h>
33 
34 #include "yahtzee.h"
35 #include "gyahtzee.h"
36 
37 static gchar *row_tooltips[MAX_FIELDS];
38 
39 void
update_score_cell(GtkWidget * treeview,gint row,gint col,int val)40 update_score_cell (GtkWidget * treeview, gint row, gint col, int val)
41 {
42   GtkTreeModel *model;
43   GtkTreeIter iter;
44   char *buf;
45 
46   g_assert (treeview != NULL);
47 
48   model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
49   gtk_tree_model_iter_nth_child (model, &iter, NULL, row);
50   if (val < 0)
51     gtk_list_store_set (GTK_LIST_STORE (model), &iter, col, "", -1);
52   else {
53     buf = g_strdup_printf ("%i", val);
54     gtk_list_store_set (GTK_LIST_STORE (model), &iter, col, buf, -1);
55     g_free (buf);
56   }
57 }
58 
59 static void
set_label_bold(GtkLabel * label,gboolean make_bold)60 set_label_bold (GtkLabel * label, gboolean make_bold)
61 {
62   PangoAttrList *attrlist;
63   PangoAttribute *attr;
64 
65   g_assert (label != NULL);
66 
67   attrlist = gtk_label_get_attributes (label);
68   if (!attrlist)
69     attrlist = pango_attr_list_new ();
70 
71   if (make_bold) {
72     attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
73     attr->start_index = 0;
74     attr->end_index = -1;
75     pango_attr_list_change (attrlist, attr);
76   } else {
77     attr = pango_attr_weight_new (PANGO_WEIGHT_NORMAL);
78     attr->start_index = 0;
79     attr->end_index = -1;
80     pango_attr_list_change (attrlist, attr);
81   }
82   gtk_label_set_attributes (label, attrlist);
83 }
84 
85 /* Shows the active player by make the player name bold in the TreeView. */
86 void
ShowoffPlayer(GtkWidget * treeview,int player,int so)87 ShowoffPlayer (GtkWidget * treeview, int player, int so)
88 {
89   GtkTreeViewColumn *col;
90   GtkWidget *label;
91   GList *collist;
92 
93   g_return_if_fail (treeview != NULL);
94 
95   if (player < 0 || player >= MAX_NUMBER_OF_PLAYERS)
96     return;
97 
98   collist = gtk_tree_view_get_columns (GTK_TREE_VIEW (treeview));
99   col = GTK_TREE_VIEW_COLUMN (g_list_nth_data (collist, player + 1));
100   g_list_free (collist);
101 
102   label = gtk_tree_view_column_get_widget (col);
103   if (!label)
104     return;
105 
106   g_assert (GTK_IS_LABEL (label));
107 
108   set_label_bold (GTK_LABEL (label), so);
109 }
110 
gtk_tree_path_to_row(GtkTreePath * path)111 static gint gtk_tree_path_to_row (GtkTreePath *path)
112 {
113   char *path_str = gtk_tree_path_to_string (path);
114   gint  row;
115   if (sscanf (path_str, "%i", &row) != 1) {
116     g_warning ("%s: could not convert '%s' to integer\n",
117 	       G_STRFUNC, path_str);
118     g_free (path_str);
119     return -1;
120   }
121   g_free (path_str);
122   return row;
123 }
124 
125 /* Convert the row of a tree into the score row */
126 
score_row(GtkTreePath * path)127 static gint score_row (GtkTreePath *path)
128 {
129     gint row = gtk_tree_path_to_row (path);
130     if (row == R_UTOTAL || row == R_BONUS || row == R_BLANK1 ||
131         row == R_GTOTAL || row == R_LTOTAL)
132         return -1;
133 
134     /* Adjust for Upper Total / Bonus entries */
135     if (row >= NUM_UPPER)
136         row -= 3;
137 
138     if (row < 0 || row >= NUM_FIELDS)
139         return -1;
140 
141     return row;
142 }
143 
144 static void
row_activated_cb(GtkTreeView * treeview,GtkTreePath * path,GtkTreeViewColumn * column,gpointer user_data)145 row_activated_cb (GtkTreeView * treeview, GtkTreePath * path,
146 		  GtkTreeViewColumn * column, gpointer user_data)
147 {
148   int row = score_row (path);
149 
150   if (players[CurrentPlayer].comp)
151     return;
152 
153   if (row >= 0) {
154     if (row < NUM_FIELDS && !players[CurrentPlayer].finished) {
155       if (play_score (CurrentPlayer, row) == SLOT_USED) {
156         say (_("Already used! " "Where do you want to put that?"));
157       } else {
158         UndoScoreElement *elem = RedoHead ();
159         if (elem && elem->player == CurrentPlayer) {
160           RedoPlayer ();
161         } else {
162           NextPlayer ();
163         }
164       }
165     }
166   }
167   update_undo_sensitivity ();
168 }
169 
170 static gboolean
activate_selected_row_idle_cb(gpointer data)171 activate_selected_row_idle_cb (gpointer data)
172 {
173   GtkTreeView *tree = GTK_TREE_VIEW (data);
174   GtkTreeViewColumn *column;
175   GtkTreePath *path;
176 
177   path = NULL;
178   gtk_tree_view_get_cursor (tree, &path, &column);
179   if (path) {
180     if (!column)
181       column = gtk_tree_view_get_column (tree, 0);
182     gtk_tree_view_row_activated (tree, path, column);
183   }
184 
185   /* Quoted from docs: "The returned GtkTreePath must be freed
186    * with gtk_tree_path_free() when you are done with it." */
187   gtk_tree_path_free (path);
188 
189   return FALSE;
190 }
191 
192 /* Returns: FALSE to let the GtkTreeView focus the selected row */
193 static gboolean
tree_button_press_cb(GtkWidget * widget,GdkEventButton * event,gpointer data)194 tree_button_press_cb (GtkWidget * widget, GdkEventButton * event,
195 		      gpointer data)
196 {
197   GtkTreeView *tree = GTK_TREE_VIEW (data);
198 
199   g_assert (widget != NULL);
200   g_assert (event != NULL);
201 
202   if (event->type != GDK_BUTTON_PRESS)
203     return FALSE;
204 
205   g_idle_add_full (G_PRIORITY_HIGH, activate_selected_row_idle_cb,
206 		   (gpointer) tree, NULL);
207 
208   return FALSE;
209 }
210 
211 /* Returns: TRUE to let GtkTreeView know it can show a tooltip */
212 static gboolean
tree_query_tooltip_cb(GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,gpointer data)213 tree_query_tooltip_cb (GtkWidget * widget, gint x, gint y,
214                        gboolean keyboard_mode, GtkTooltip *tooltip, gpointer data)
215 {
216     GtkTreeModel *model_ptr = NULL;
217     GtkTreePath  *path_ptr  = NULL;
218     GtkTreeIter  *iter_ptr  = NULL;
219     gint rval = FALSE;
220 
221     if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget), &x, &y,
222                 keyboard_mode, &model_ptr, &path_ptr, iter_ptr)) {
223         if (path_ptr) {
224             gint row = score_row (path_ptr);
225             if (row >= 0) {
226                 gtk_tooltip_set_text (tooltip, row_tooltips[row]);
227                 rval = TRUE;
228             }
229         }
230     }
231 
232     return rval;
233 }
234 
235 GtkWidget *
create_score_list(void)236 create_score_list (void)
237 {
238   GtkWidget *tree;
239   GtkListStore *store;
240 
241   store = gtk_list_store_new (MAX_NUMBER_OF_PLAYERS + 3,
242 			      G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
243 			      G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
244 			      G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
245   tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
246   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree), TRUE);
247   gtk_tree_view_set_enable_search (GTK_TREE_VIEW (tree), FALSE);
248   gtk_widget_set_has_tooltip (GTK_WIDGET (tree), TRUE);
249 
250   g_object_unref (store);
251 
252   g_signal_connect (G_OBJECT (tree), "row-activated",
253 		    G_CALLBACK (row_activated_cb), NULL);
254   g_signal_connect (G_OBJECT (tree), "button-press-event",
255 		    G_CALLBACK (tree_button_press_cb), (gpointer) tree);
256   g_signal_connect (G_OBJECT (tree), "query-tooltip",
257             G_CALLBACK (tree_query_tooltip_cb), (gpointer) tree);
258 
259   return tree;
260 }
261 
262 static void
add_columns(GtkTreeView * tree)263 add_columns (GtkTreeView * tree)
264 {
265   GtkTreeViewColumn *column;
266   GtkCellRenderer *renderer;
267   GtkWidget *label;
268   GValue *prop_value;
269   int i;
270 
271   /* Create columns */
272   renderer = gtk_cell_renderer_text_new ();
273   column = gtk_tree_view_column_new_with_attributes ("", renderer,
274 						     "text", 0,
275 						     "weight", LAST_COL,
276 						     NULL);
277   g_object_set (renderer, "weight-set", TRUE, NULL);
278   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
279   for (i = 0; i < MAX_NUMBER_OF_PLAYERS; i++) {
280     renderer = gtk_cell_renderer_text_new ();
281     prop_value = g_new0 (GValue, 1);
282     g_value_init (prop_value, G_TYPE_FLOAT);
283     g_value_set_float (prop_value, 1.0);
284     g_object_set_property (G_OBJECT (renderer), "xalign", prop_value);
285     g_object_set (renderer, "weight-set", TRUE, NULL);
286     g_value_unset (prop_value);
287     g_free (prop_value);
288 
289     column = gtk_tree_view_column_new ();
290     gtk_tree_view_column_pack_start (column, renderer, TRUE);
291     gtk_tree_view_column_set_attributes (column, renderer,
292 					 "text", i + 1,
293 					 "weight", LAST_COL, NULL);
294     gtk_tree_view_column_set_min_width (column, 95);
295     gtk_tree_view_column_set_alignment (column, 1.0);
296     label = gtk_label_new (players[i].name);
297     gtk_tree_view_column_set_widget (column, label);
298     gtk_widget_show (label);
299     gtk_tree_view_column_set_visible (column, FALSE);
300     gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
301   }
302   renderer = gtk_cell_renderer_text_new ();
303   column = gtk_tree_view_column_new_with_attributes ("", renderer, "text",
304 						     MAX_NUMBER_OF_PLAYERS
305 						     + 1, NULL);
306   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
307 }
308 
309 void
score_list_set_column_title(GtkWidget * treeview,int column,const char * str)310 score_list_set_column_title (GtkWidget * treeview, int column,
311 			     const char *str)
312 {
313   GtkTreeViewColumn *col;
314   GtkWidget *label;
315 
316   g_assert (treeview != NULL);
317 
318   col = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), column);
319   label = gtk_tree_view_column_get_widget (col);
320   if (!label)
321     return;
322 
323   gtk_label_set_text (GTK_LABEL (label), str);
324 }
325 
326 static void
initialize_column_titles(GtkTreeView * treeview)327 initialize_column_titles (GtkTreeView * treeview)
328 {
329   GtkTreeViewColumn *col;
330   GList *collist, *node;
331   GtkWidget *label;
332   int i;
333 
334   collist = gtk_tree_view_get_columns (treeview);
335   i = 0;
336   for (node = collist; node != NULL; node = g_list_next (node)) {
337     col = GTK_TREE_VIEW_COLUMN (node->data);
338     label = gtk_tree_view_column_get_widget (col);
339     if (!label)
340       continue;
341 
342     gtk_tree_view_column_set_visible (col, i < NumberOfPlayers);
343     gtk_label_set_text (GTK_LABEL (label), players[i].name);
344     i++;
345   }
346   g_list_free (collist);
347 }
348 
349 void
setup_score_list(GtkWidget * treeview)350 setup_score_list (GtkWidget * treeview)
351 {
352   GtkTreeModel *model;
353   GtkListStore *store;
354   GtkTreeIter iter;
355   GList *columns;
356   int i;
357 
358   g_assert (treeview != NULL);
359 
360   columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (treeview));
361   if (!columns) {
362     add_columns (GTK_TREE_VIEW (treeview));
363   } else {
364     initialize_column_titles (GTK_TREE_VIEW (treeview));
365   }
366 
367   model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
368   store = GTK_LIST_STORE (model);
369   gtk_list_store_clear (store);
370 
371   for (i = 0; i < NUM_UPPER; i++) {
372     gtk_list_store_append (store, &iter);
373     gtk_list_store_set (store, &iter, 0, _(FieldLabels[i]),
374 			LAST_COL, PANGO_WEIGHT_NORMAL, -1);
375   }
376 
377   gtk_list_store_append (store, &iter);
378   gtk_list_store_set (store, &iter, 0, _(FieldLabels[F_BONUS]),
379 		      LAST_COL, PANGO_WEIGHT_BOLD, -1);
380   gtk_list_store_append (store, &iter);
381   gtk_list_store_set (store, &iter, 0, _(FieldLabels[F_UPPERT]),
382 		      LAST_COL, PANGO_WEIGHT_BOLD, -1);
383   gtk_list_store_append (store, &iter);
384   gtk_list_store_set (store, &iter, 0, "", -1);
385 
386   for (i = 0; i < NUM_LOWER; i++) {
387     gtk_list_store_append (store, &iter);
388     gtk_list_store_set (store, &iter,
389 			0, _(FieldLabels[i + NUM_UPPER]),
390 			LAST_COL, PANGO_WEIGHT_NORMAL, -1);
391   }
392 
393   gtk_list_store_append (store, &iter);
394   gtk_list_store_set (store, &iter, 0, _(FieldLabels[F_LOWERT]),
395 		      LAST_COL, PANGO_WEIGHT_BOLD, -1);
396   gtk_list_store_append (store, &iter);
397   gtk_list_store_set (store, &iter, 0, _(FieldLabels[F_GRANDT]),
398 		      LAST_COL, PANGO_WEIGHT_BOLD, -1);
399 }
400 
401 void
update_score_tooltips(void)402 update_score_tooltips (void)
403 {
404     gint ii;
405 
406     for (ii = 0; ii < NUM_FIELDS; ii++) {
407         gint score = player_field_score (CurrentPlayer, ii);
408         if (!row_tooltips[ii]) row_tooltips[ii] = g_new0 (gchar, 100);
409 
410 
411         if (score >= 0)
412             sprintf (row_tooltips[ii], _("Score: %d"), score);
413         else
414             sprintf (row_tooltips[ii], _("Field used"));
415     }
416 }
417