1 /*
2 * gtkmovelist.c
3 * by Jon Kinsey, 2005
4 *
5 * Analysis move list
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of version 3 or later of the GNU General Public License as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 * $Id: gtkmovelist.c,v 1.38 2018/05/13 17:05:24 plm Exp $
21 */
22
23 #include "config.h"
24 #include "gtklocdefs.h"
25 #include "gtkgame.h"
26 #include <string.h>
27
28 #include "format.h"
29 #include "gtkmovelistctrl.h"
30 #include "drawboard.h"
31
32 #define DETAIL_COLUMN_COUNT 11
33 #define MIN_COLUMN_COUNT 5
34
35 enum {
36 ML_COL_RANK = 0,
37 ML_COL_TYPE,
38 ML_COL_WIN,
39 ML_COL_GWIN,
40 ML_COL_BGWIN,
41 ML_COL_LOSS,
42 ML_COL_GLOSS,
43 ML_COL_BGLOSS,
44 ML_COL_EQUITY,
45 ML_COL_DIFF,
46 ML_COL_MOVE,
47 ML_COL_FGCOL,
48 ML_COL_DATA
49 };
50
51 extern void
MoveListCreate(hintdata * phd)52 MoveListCreate(hintdata * phd)
53 {
54 static const char *aszTitleDetails[] = {
55 N_("Rank"),
56 N_("Type"),
57 N_("Win"),
58 N_("W g"),
59 N_("W bg"),
60 N_("Lose"),
61 N_("L g"),
62 N_("L bg"),
63 NULL,
64 N_("Diff."),
65 N_("noun|Move")
66 };
67 unsigned int i;
68 int showWLTree = showMoveListDetail && !phd->fDetails;
69
70 /* Create list widget */
71 GtkListStore *store;
72 GtkTreeIter iter;
73 GtkTreeSelection *sel;
74 GtkWidget *view = gtk_tree_view_new();
75 int offset = (phd->fDetails) ? 0 : MIN_COLUMN_COUNT - DETAIL_COLUMN_COUNT;
76
77 if (showWLTree) {
78 GtkStyle *psDefault = gtk_widget_get_style(view);
79
80 GtkCellRenderer *renderer = custom_cell_renderer_movelist_new();
81 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, aszTitleDetails[ML_COL_RANK], renderer,
82 "movelist", 0, "rank", 1, NULL);
83 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
84 g_object_set(renderer, "cell-background-gdk", &psDefault->bg[GTK_STATE_NORMAL],
85 "cell-background-set", TRUE, NULL);
86
87 g_object_set_data(G_OBJECT(view), "hintdata", phd);
88 } else {
89 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
90 g_object_set(renderer, "ypad", 0, NULL);
91
92 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, aszTitleDetails[ML_COL_RANK], renderer,
93 "text", ML_COL_RANK, "foreground", ML_COL_FGCOL + offset, NULL);
94 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, aszTitleDetails[ML_COL_TYPE], renderer,
95 "text", ML_COL_TYPE, "foreground", ML_COL_FGCOL + offset, NULL);
96
97 if (phd->fDetails) {
98 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, aszTitleDetails[ML_COL_WIN], renderer,
99 "text", ML_COL_WIN, "foreground", ML_COL_FGCOL + offset, NULL);
100 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, aszTitleDetails[ML_COL_GWIN], renderer,
101 "text", ML_COL_GWIN, "foreground", ML_COL_FGCOL + offset, NULL);
102 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, aszTitleDetails[ML_COL_BGWIN],
103 renderer, "text", ML_COL_BGWIN, "foreground",
104 ML_COL_FGCOL + offset, NULL);
105 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, aszTitleDetails[ML_COL_LOSS], renderer,
106 "text", ML_COL_LOSS, "foreground", ML_COL_FGCOL + offset, NULL);
107 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, aszTitleDetails[ML_COL_GLOSS],
108 renderer, "text", ML_COL_GLOSS, "foreground",
109 ML_COL_FGCOL + offset, NULL);
110 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, aszTitleDetails[ML_COL_BGLOSS],
111 renderer, "text", ML_COL_BGLOSS, "foreground",
112 ML_COL_FGCOL + offset, NULL);
113 }
114
115 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, aszTitleDetails[ML_COL_EQUITY], renderer,
116 "text", ML_COL_EQUITY + offset, "foreground", ML_COL_FGCOL + offset,
117 NULL);
118 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, aszTitleDetails[ML_COL_DIFF], renderer,
119 "text", ML_COL_DIFF + offset, "foreground", ML_COL_FGCOL + offset,
120 NULL);
121 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, Q_(aszTitleDetails[ML_COL_MOVE]), renderer,
122 "text", ML_COL_MOVE + offset, "foreground", ML_COL_FGCOL + offset,
123 NULL);
124 }
125
126 phd->pwMoves = view;
127
128 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
129 gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
130
131 g_signal_connect(view, "row-activated", G_CALLBACK(HintDoubleClick), phd);
132 g_signal_connect(sel, "changed", G_CALLBACK(HintSelect), phd);
133
134
135 /* Add empty rows */
136 if (phd->fDetails)
137 store =
138 gtk_list_store_new(DETAIL_COLUMN_COUNT + 2, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
139 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
140 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
141 else {
142 if (showWLTree)
143 store = gtk_list_store_new(2, G_TYPE_POINTER, G_TYPE_INT);
144 else
145 store =
146 gtk_list_store_new(MIN_COLUMN_COUNT + 2, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
147 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
148 }
149
150 for (i = 0; i < phd->pml->cMoves; i++)
151 gtk_list_store_append(store, &iter);
152
153 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
154 MoveListUpdate(phd);
155 }
156
157 float rBest;
158
159 GtkStyle *psHighlight = NULL;
160
161 extern void
MoveListRefreshSize(void)162 MoveListRefreshSize(void)
163 {
164 custom_cell_renderer_invalidate_size();
165 if (pwMoveAnalysis) {
166 hintdata *phd = (hintdata *) g_object_get_data(G_OBJECT(pwMoveAnalysis), "user_data");
167 MoveListUpdate(phd);
168 }
169 }
170
171 /*
172 * Call UpdateMostList to update the movelist in the GTK hint window.
173 * For example, after new evaluations, rollouts or toggle of MWC/Equity.
174 *
175 */
176 extern void
MoveListUpdate(const hintdata * phd)177 MoveListUpdate(const hintdata * phd)
178 {
179 unsigned int i, j, colNum;
180 char sz[32];
181 cubeinfo ci;
182 movelist *pml = phd->pml;
183 int col = phd->fDetails ? 8 : 2;
184 int showWLTree = showMoveListDetail && !phd->fDetails;
185
186 int offset = (phd->fDetails) ? 0 : MIN_COLUMN_COUNT - DETAIL_COLUMN_COUNT;
187 GtkTreeIter iter;
188 GtkListStore *store;
189 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(phd->pwMoves)));
190 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
191
192 if (!psHighlight) { /* Get highlight style first time in */
193 GtkStyle *psTemp;
194 GtkStyle *psMoves = gtk_widget_get_style(phd->pwMoves);
195 GetStyleFromRCFile(&psHighlight, "move-done", psMoves);
196 /* Use correct background colour when selected */
197 memcpy(&psHighlight->bg[GTK_STATE_SELECTED], &psMoves->bg[GTK_STATE_SELECTED], sizeof(GdkColor));
198
199 /* Also get colour to use for w/l stats in detail view */
200 GetStyleFromRCFile(&psTemp, "move-winlossfg", psMoves);
201 memcpy(&wlCol, &psTemp->fg[GTK_STATE_NORMAL], sizeof(GdkColor));
202 g_object_unref(psTemp);
203 }
204
205 /* This function should only be called when the game state matches
206 * the move list. */
207 g_assert(ms.fMove == 0 || ms.fMove == 1);
208
209 GetMatchStateCubeInfo(&ci, &ms);
210 rBest = pml->amMoves[0].rScore;
211
212 if (!showWLTree)
213 gtk_tree_view_column_set_title(gtk_tree_view_get_column(GTK_TREE_VIEW(phd->pwMoves), col),
214 (fOutputMWC && ms.nMatchTo) ? _("MWC") : _("Equity"));
215
216 for (i = 0; i < pml->cMoves; i++) {
217 float *ar = pml->amMoves[i].arEvalMove;
218 int rankKnown;
219 const char *highlight_sz;
220
221 if (showWLTree)
222 gtk_list_store_set(store, &iter, 0, pml->amMoves + i, -1);
223 else
224 gtk_list_store_set(store, &iter, ML_COL_DATA + offset, pml->amMoves + i, -1);
225
226 rankKnown = 1;
227 if (i && i == pml->cMoves - 1 && phd->piHighlight && i == *phd->piHighlight)
228 /* The move made is the last on the list. Some moves might
229 * have been deleted to fit this one in */
230 {
231 /* Lets count how many moves are possible to see if this is the last move */
232 movelist ml;
233 int dice[2];
234 memcpy(dice, ms.anDice, sizeof(dice));
235 if (!dice[0]) { /* If the dice have got lost, try to find them */
236 moverecord *pmr = (moverecord *) plLastMove->plNext->p;
237 if (pmr) {
238 dice[0] = pmr->anDice[0];
239 dice[1] = pmr->anDice[1];
240 }
241 }
242 GenerateMoves(&ml, msBoard(), dice[0], dice[1], FALSE);
243 if (i < ml.cMoves - 1)
244 rankKnown = 0;
245 }
246
247 highlight_sz = (phd->piHighlight && *phd->piHighlight == i) ? "*" : "";
248
249 if (rankKnown)
250 sprintf(sz, "%s%s%u", pml->amMoves[i].cmark ? "+" : "", highlight_sz, i + 1);
251 else
252 sprintf(sz, "%s%s??", pml->amMoves[i].cmark ? "+" : "", highlight_sz);
253
254 if (showWLTree) {
255 gtk_list_store_set(store, &iter, 1, rankKnown ? (int) i + 1 : -1, -1);
256 goto skipoldcode;
257 } else
258 gtk_list_store_set(store, &iter, ML_COL_RANK, sz, -1);
259 FormatEval(sz, &pml->amMoves[i].esMove);
260 gtk_list_store_set(store, &iter, ML_COL_TYPE, sz, -1);
261
262 /* gwc */
263 if (phd->fDetails) {
264 colNum = ML_COL_WIN;
265 for (j = 0; j < 5; j++) {
266 if (j == 3) {
267 gtk_list_store_set(store, &iter, colNum, OutputPercent(1.0f - ar[OUTPUT_WIN]), -1);
268 colNum++;
269 }
270 gtk_list_store_set(store, &iter, colNum, OutputPercent(ar[j]), -1);
271 colNum++;
272 }
273 }
274
275 /* cubeless equity */
276 gtk_list_store_set(store, &iter, ML_COL_EQUITY + offset, OutputEquity(pml->amMoves[i].rScore, &ci, TRUE), -1);
277 if (i != 0) {
278 gtk_list_store_set(store, &iter, ML_COL_DIFF + offset,
279 OutputEquityDiff(pml->amMoves[i].rScore, rBest, &ci), -1);
280 }
281
282 gtk_list_store_set(store, &iter, ML_COL_MOVE + offset, FormatMove(sz, msBoard(), pml->amMoves[i].anMove), -1);
283
284 /* highlight row */
285 if (phd->piHighlight && *phd->piHighlight == i) {
286 char buf[20];
287 sprintf(buf, "#%02x%02x%02x", psHighlight->fg[GTK_STATE_SELECTED].red / 256,
288 psHighlight->fg[GTK_STATE_SELECTED].green / 256, psHighlight->fg[GTK_STATE_SELECTED].blue / 256);
289 gtk_list_store_set(store, &iter, ML_COL_FGCOL + offset, buf, -1);
290 } else
291 gtk_list_store_set(store, &iter, ML_COL_FGCOL + offset, NULL, -1);
292 skipoldcode: /* Messy as 3 copies of code at moment... */
293 gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
294 }
295
296 }
297
298 extern GList *
MoveListGetSelectionList(const hintdata * phd)299 MoveListGetSelectionList(const hintdata * phd)
300 {
301 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(phd->pwMoves));
302 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(phd->pwMoves));
303 return gtk_tree_selection_get_selected_rows(sel, &model);
304 }
305
306 /* gtk_tree_path_free() is not the right function type
307 to be called directly from g_list_foreach() below
308 */
309 static inline void
my_gtk_tree_path_free(gpointer data,gpointer UNUSED (user_data))310 my_gtk_tree_path_free(gpointer data, gpointer UNUSED(user_data))
311 {
312 gtk_tree_path_free((GtkTreePath *) data);
313 }
314
315 extern void
MoveListFreeSelectionList(GList * pl)316 MoveListFreeSelectionList(GList * pl)
317 {
318 g_list_foreach(pl, my_gtk_tree_path_free, NULL);
319 g_list_free(pl);
320 }
321
322 extern move *
MoveListGetMove(const hintdata * phd,GList * pl)323 MoveListGetMove(const hintdata * phd, GList * pl)
324 {
325 move *m;
326 int showWLTree = showMoveListDetail && !phd->fDetails;
327 int col, offset = (phd->fDetails) ? 0 : MIN_COLUMN_COUNT - DETAIL_COLUMN_COUNT;
328 GtkTreeIter iter;
329 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(phd->pwMoves));
330
331 gboolean check = gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) (pl->data));
332 if (check == 0)
333 printf("Error in move list!\n");
334
335 if (showWLTree)
336 col = 0;
337 else
338 col = ML_COL_DATA + offset;
339 gtk_tree_model_get(model, &iter, col, &m, -1);
340 return m;
341 }
342
343 extern void
MoveListShowToggledClicked(GtkWidget * UNUSED (pw),hintdata * phd)344 MoveListShowToggledClicked(GtkWidget * UNUSED(pw), hintdata * phd)
345 {
346 int f = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(phd->pwShow));
347 if (f)
348 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(phd->pwMoves)), GTK_SELECTION_SINGLE);
349 else
350 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(phd->pwMoves)), GTK_SELECTION_MULTIPLE);
351
352 ShowMove(phd, f);
353 }
354
355 extern gint
MoveListClearSelection(GtkWidget * UNUSED (pw),GdkEventSelection * UNUSED (pes),hintdata * phd)356 MoveListClearSelection(GtkWidget * UNUSED(pw), GdkEventSelection * UNUSED(pes), hintdata * phd)
357 {
358 gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(phd->pwMoves)));
359 return TRUE;
360 }
361