1 /*
2 * Move history for WinBoard
3 *
4 * Author: Alessandro Scotti (Dec 2005)
5 * back-end part split off by HGM
6 *
7 * Copyright 2005 Alessandro Scotti
8 *
9 * Enhancements Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015,
10 * 2016 Free Software Foundation, Inc.
11 *
12 * ------------------------------------------------------------------------
13 *
14 * GNU XBoard is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or (at
17 * your option) any later version.
18 *
19 * GNU XBoard is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see http://www.gnu.org/licenses/.
26 *
27 * ------------------------------------------------------------------------
28 ** See the file ChangeLog for a revision history. */
29
30 #include "config.h"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "common.h"
37 #include "frontend.h"
38 #include "backend.h"
39
40 /* templates for low-level front-end tasks (requiring platform-dependent implementation) */
41 void ClearHistoryMemo P((void)); // essential
42 int AppendToHistoryMemo P(( char * text, int bold, int colorNr )); // essential (coloring / styling optional)
43 void HighlightMove P(( int from, int to, Boolean highlight )); // optional (can be dummy)
44 void ScrollToCurrent P((int caretPos)); // optional (can be dummy)
45
46 /* templates for front-end entry point to allow inquiring about front-end state */
47 Boolean MoveHistoryDialogExists P((void));
48 Boolean MoveHistoryIsUp P((void));
49
50 /* Module globals */
51 typedef char MoveHistoryString[ MOVE_LEN*2 ];
52
53 static int lastFirst = 0;
54 static int lastLast = 0;
55 static int lastCurrent = -1;
56 static int lastGames;
57
58 static char lastLastMove[ MOVE_LEN ];
59
60 static MoveHistoryString * currMovelist;
61 static ChessProgramStats_Move * currPvInfo;
62 static int currFirst = 0;
63 static int currLast = 0;
64 static int currCurrent = -1;
65
66 typedef struct {
67 int memoOffset;
68 int memoLength;
69 } HistoryMove;
70
71 static HistoryMove histMoves[ MAX_MOVES ];
72
73 /* Note: in the following code a "Memo" is a Rich Edit control (it's Delphi lingo) */
74
75 // back-end after replacing Windows data-types by equivalents
76 static Boolean
OnlyCurrentPositionChanged()77 OnlyCurrentPositionChanged ()
78 {
79 Boolean result = FALSE;
80
81 if( lastFirst >= 0 &&
82 lastLast >= lastFirst &&
83 lastCurrent >= lastFirst &&
84 currFirst == lastFirst &&
85 currLast == lastLast &&
86 currCurrent >= 0 &&
87 lastGames == storedGames )
88 {
89 result = TRUE;
90
91 /* Special case: last move changed */
92 if( currCurrent == currLast-1 ) {
93 if( strcmp( currMovelist[currCurrent], lastLastMove ) != 0 ) {
94 result = FALSE;
95 }
96 }
97 }
98
99 return result;
100 }
101
102 // back-end, after replacing Windows data types
103 static Boolean
OneMoveAppended()104 OneMoveAppended ()
105 {
106 Boolean result = FALSE;
107
108 if( lastCurrent >= 0 && lastCurrent >= lastFirst && lastLast >= lastFirst &&
109 currCurrent >= 0 && currCurrent >= currFirst && currLast >= currFirst &&
110 lastFirst == currFirst &&
111 lastLast == (currLast-1) &&
112 lastCurrent == (currCurrent-1) &&
113 currCurrent == (currLast-1) &&
114 lastGames == storedGames )
115 {
116 result = TRUE;
117 }
118
119 return result;
120 }
121
122 // back-end, now that color and font-style are passed as numbers
123 static void
AppendMoveToMemo(int index)124 AppendMoveToMemo (int index)
125 {
126 char buf[64];
127
128 if( index < 0 || index >= MAX_MOVES ) {
129 return;
130 }
131
132 buf[0] = '\0';
133
134 /* Move number */
135 if( (index % 2) == 0 ) {
136 sprintf( buf, "%d.%s ", (index / 2)+1, index & 1 ? ".." : "" );
137 AppendToHistoryMemo( buf, 1, 0 ); // [HGM] 1 means bold, 0 default color
138 }
139
140 /* Move text */
141 safeStrCpy( buf, SavePart( currMovelist[index]) , sizeof( buf)/sizeof( buf[0]) );
142 strcat( buf, " " );
143
144 histMoves[index].memoOffset = AppendToHistoryMemo( buf, 0, 0 );
145 histMoves[index].memoLength = strlen(buf)-1;
146
147 /* PV info (if any) */
148 if( appData.showEvalInMoveHistory && currPvInfo[index].depth > 0 ) {
149 sprintf( buf, "{%s%.2f/%d} ",
150 currPvInfo[index].score >= 0 ? "+" : "",
151 currPvInfo[index].score / 100.0,
152 currPvInfo[index].depth );
153
154 AppendToHistoryMemo( buf, 0, 1); // [HGM] 1 means gray
155 }
156 }
157
158 // back-end
159 void
RefreshMemoContent()160 RefreshMemoContent ()
161 {
162 int i;
163
164 ClearHistoryMemo();
165
166 for( i=currFirst; i<currLast; i++ ) {
167 AppendMoveToMemo( i );
168 }
169 }
170
171 // back-end part taken out of HighlightMove to determine character positions
172 static void
DoHighlight(int index,int onoff)173 DoHighlight (int index, int onoff)
174 {
175 if( index >= 0 && index < MAX_MOVES ) {
176 HighlightMove( histMoves[index].memoOffset,
177 histMoves[index].memoOffset + histMoves[index].memoLength, onoff );
178 }
179 }
180
181 // back-end, now that a wrapper is provided for the front-end code to do the actual scrolling
182 void
MemoContentUpdated()183 MemoContentUpdated ()
184 {
185 int caretPos;
186
187 if(lastCurrent <= currLast) DoHighlight( lastCurrent, FALSE );
188
189 lastFirst = currFirst;
190 lastLast = currLast;
191 lastCurrent = currCurrent;
192 lastGames = storedGames;
193 lastLastMove[0] = '\0';
194
195 if( lastLast > 0 ) {
196 safeStrCpy( lastLastMove, SavePart( currMovelist[lastLast-1] ) , sizeof( lastLastMove)/sizeof( lastLastMove[0]) );
197 }
198
199 /* Deselect any text, move caret to end of memo */
200 if( currCurrent >= 0 ) {
201 caretPos = histMoves[currCurrent].memoOffset + histMoves[currCurrent].memoLength;
202 }
203 else {
204 caretPos = -1;
205 }
206
207 ScrollToCurrent(caretPos);
208 DoHighlight( currCurrent, TRUE ); // [HGM] moved last, because in X some scrolling methods spoil highlighting
209 }
210
211 // back-end. Must be called as double-click call-back on move-history text edit
212 void
FindMoveByCharIndex(int char_index)213 FindMoveByCharIndex (int char_index)
214 {
215 int index;
216
217 for( index=currFirst; index<currLast; index++ ) {
218 if( char_index >= histMoves[index].memoOffset &&
219 char_index < (histMoves[index].memoOffset + histMoves[index].memoLength) )
220 {
221 ToNrEvent( index + 1 ); // moved here from call-back
222 }
223 }
224 }
225
226 // back-end. In WinBoard called by call-back, but could be called directly by SetIfExists?
227 void
UpdateMoveHistory()228 UpdateMoveHistory ()
229 {
230 /* Update the GUI */
231 if( OnlyCurrentPositionChanged() ) {
232 /* Only "cursor" changed, no need to update memo content */
233 }
234 else if( OneMoveAppended() ) {
235 AppendMoveToMemo( currCurrent );
236 }
237 else {
238 RefreshMemoContent();
239 }
240
241 MemoContentUpdated();
242 }
243
244 // back-end
245 void
MoveHistorySet(char movelist[][2* MOVE_LEN],int first,int last,int current,ChessProgramStats_Move * pvInfo)246 MoveHistorySet (char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo)
247 {
248 /* [AS] Danger! For now we rely on the movelist parameter being a static variable! */
249
250 currMovelist = movelist;
251 currFirst = first;
252 currLast = last;
253 currCurrent = current;
254 currPvInfo = pvInfo;
255
256 if(MoveHistoryDialogExists())
257 UpdateMoveHistory(); // [HGM] call this directly, in stead of through call-back
258 }
259