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