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