2 * Move history for WinBoard
\r
4 * Author: Alessandro Scotti (Dec 2005)
\r
6 * Copyright 2005 Alessandro Scotti
\r
8 * ------------------------------------------------------------------------
\r
10 * GNU XBoard is free software: you can redistribute it and/or modify
\r
11 * it under the terms of the GNU General Public License as published by
\r
12 * the Free Software Foundation, either version 3 of the License, or (at
\r
13 * your option) any later version.
\r
15 * GNU XBoard is distributed in the hope that it will be useful, but
\r
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
18 * General Public License for more details.
\r
20 * You should have received a copy of the GNU General Public License
\r
21 * along with this program. If not, see http://www.gnu.org/licenses/.
\r
23 * ------------------------------------------------------------------------
\r
24 ** See the file ChangeLog for a revision history. */
\r
28 #include <windows.h> /* required for all Windows applications */
\r
29 #include <richedit.h>
\r
33 #include <commdlg.h>
\r
37 #include "winboard.h"
\r
38 #include "frontend.h"
\r
39 #include "backend.h"
\r
43 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );
\r
44 VOID MoveHistoryPopUp();
\r
45 VOID MoveHistoryPopDown();
\r
46 BOOL MoveHistoryIsUp();
\r
48 /* Imports from backend.c */
\r
49 char * SavePart(char *str);
\r
51 /* Imports from winboard.c */
\r
52 extern HWND moveHistoryDialog;
\r
53 extern BOOLEAN moveHistoryDialogUp;
\r
55 extern HINSTANCE hInst;
\r
56 extern HWND hwndMain;
\r
58 extern WindowPlacement wpMoveHistory;
\r
60 extern BoardSize boardSize;
\r
62 /* Module globals */
\r
63 typedef char MoveHistoryString[ MOVE_LEN*2 ];
\r
65 static int lastFirst = 0;
\r
66 static int lastLast = 0;
\r
67 static int lastCurrent = -1;
\r
69 static char lastLastMove[ MOVE_LEN ];
\r
71 static MoveHistoryString * currMovelist;
\r
72 static ChessProgramStats_Move * currPvInfo;
\r
73 static int currFirst = 0;
\r
74 static int currLast = 0;
\r
75 static int currCurrent = -1;
\r
82 static HistoryMove histMoves[ MAX_MOVES ];
\r
84 #define WM_REFRESH_HISTORY (WM_USER+4657)
\r
86 #define DEFAULT_COLOR 0xFFFFFFFF
\r
91 /* Note: in the following code a "Memo" is a Rich Edit control (it's Delphi lingo) */
\r
93 static VOID HighlightMove( int index, BOOL highlight )
\r
95 if( index >= 0 && index < MAX_MOVES ) {
\r
97 HWND hMemo = GetDlgItem( moveHistoryDialog, IDC_MoveHistory );
\r
99 SendMessage( hMemo,
\r
101 histMoves[index].memoOffset,
\r
102 histMoves[index].memoOffset + histMoves[index].memoLength );
\r
106 ZeroMemory( &cf, sizeof(cf) );
\r
108 cf.cbSize = sizeof(cf);
\r
109 cf.dwMask = CFM_BOLD | CFM_COLOR;
\r
112 cf.dwEffects |= CFE_BOLD;
\r
113 cf.crTextColor = RGB( 0x00, 0x00, 0xFF );
\r
116 cf.dwEffects |= CFE_AUTOCOLOR;
\r
119 SendMessage( hMemo, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf );
\r
123 static BOOL OnlyCurrentPositionChanged()
\r
125 BOOL result = FALSE;
\r
127 if( lastFirst >= 0 &&
\r
128 lastLast >= lastFirst &&
\r
129 lastCurrent >= lastFirst &&
\r
130 currFirst == lastFirst &&
\r
131 currLast == lastLast &&
\r
132 currCurrent >= 0 &&
\r
137 /* Special case: last move changed */
\r
138 if( currCurrent == currLast-1 ) {
\r
139 if( strcmp( currMovelist[currCurrent], lastLastMove ) != 0 ) {
\r
148 static BOOL OneMoveAppended()
\r
150 BOOL result = FALSE;
\r
152 if( lastCurrent >= 0 && lastCurrent >= lastFirst && lastLast >= lastFirst &&
\r
153 currCurrent >= 0 && currCurrent >= currFirst && currLast >= currFirst &&
\r
154 lastFirst == currFirst &&
\r
155 lastLast == (currLast-1) &&
\r
156 lastCurrent == (currCurrent-1) &&
\r
157 currCurrent == (currLast-1) &&
\r
166 static VOID ClearMemo()
\r
168 SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, WM_SETTEXT, 0, (LPARAM) "" );
\r
171 static int AppendToMemo( char * text, DWORD flags, DWORD color )
\r
175 HWND hMemo = GetDlgItem( moveHistoryDialog, IDC_MoveHistory );
\r
177 /* Select end of text */
\r
178 int cbTextLen = (int) SendMessage( hMemo, WM_GETTEXTLENGTH, 0, 0 );
\r
180 SendMessage( hMemo, EM_SETSEL, cbTextLen, cbTextLen );
\r
183 ZeroMemory( &cf, sizeof(cf) );
\r
185 cf.cbSize = sizeof(cf);
\r
186 cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_COLOR | CFM_UNDERLINE;
\r
187 cf.dwEffects = flags;
\r
189 if( color != DEFAULT_COLOR ) {
\r
190 cf.crTextColor = color;
\r
193 cf.dwEffects |= CFE_AUTOCOLOR;
\r
196 SendMessage( hMemo, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf );
\r
199 SendMessage( hMemo, EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );
\r
201 /* Return offset of appended text */
\r
205 static VOID AppendMoveToMemo( int index )
\r
209 DWORD color = DEFAULT_COLOR;
\r
211 if( index < 0 || index >= MAX_MOVES ) {
\r
218 if( (index % 2) == 0 ) {
\r
219 sprintf( buf, "%d.%s ", (index / 2)+1, index & 1 ? ".." : "" );
\r
220 AppendToMemo( buf, CFE_BOLD, DEFAULT_COLOR );
\r
224 strcpy( buf, SavePart( currMovelist[index] ) );
\r
225 strcat( buf, " " );
\r
227 histMoves[index].memoOffset = AppendToMemo( buf, flags, color );
\r
228 histMoves[index].memoLength = strlen(buf)-1;
\r
230 /* PV info (if any) */
\r
231 if( appData.showEvalInMoveHistory && currPvInfo[index].depth > 0 ) {
\r
232 sprintf( buf, "{%s%.2f/%d} ",
\r
233 currPvInfo[index].score >= 0 ? "+" : "",
\r
234 currPvInfo[index].score / 100.0,
\r
235 currPvInfo[index].depth );
\r
237 AppendToMemo( buf, flags,
\r
238 color == DEFAULT_COLOR ? GetSysColor(COLOR_GRAYTEXT) : color );
\r
242 static void RefreshMemoContent()
\r
248 for( i=currFirst; i<currLast; i++ ) {
\r
249 AppendMoveToMemo( i );
\r
253 static void MemoContentUpdated()
\r
257 HighlightMove( lastCurrent, FALSE );
\r
258 HighlightMove( currCurrent, TRUE );
\r
260 lastFirst = currFirst;
\r
261 lastLast = currLast;
\r
262 lastCurrent = currCurrent;
\r
263 lastLastMove[0] = '\0';
\r
265 if( lastLast > 0 ) {
\r
266 strcpy( lastLastMove, SavePart( currMovelist[lastLast-1] ) );
\r
269 /* Deselect any text, move caret to end of memo */
\r
270 if( currCurrent >= 0 ) {
\r
271 caretPos = histMoves[currCurrent].memoOffset + histMoves[currCurrent].memoLength;
\r
274 caretPos = (int) SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, WM_GETTEXTLENGTH, 0, 0 );
\r
277 SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SETSEL, caretPos, caretPos );
\r
279 SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SCROLLCARET, 0, 0 );
\r
282 int FindMoveByCharIndex( int char_index )
\r
286 for( index=currFirst; index<currLast; index++ ) {
\r
287 if( char_index >= histMoves[index].memoOffset &&
\r
288 char_index < (histMoves[index].memoOffset + histMoves[index].memoLength) )
\r
297 LRESULT CALLBACK HistoryDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
\r
299 static SnapData sd;
\r
302 case WM_INITDIALOG:
\r
303 if( moveHistoryDialog == NULL ) {
\r
304 moveHistoryDialog = hDlg;
\r
306 /* Enable word wrapping and notifications */
\r
307 SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SETTARGETDEVICE, 0, 0 );
\r
309 SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
312 SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, WM_SETFONT, (WPARAM)font[boardSize][MOVEHISTORY_FONT]->hf, MAKELPARAM(TRUE, 0 ));
\r
314 /* Restore window placement */
\r
315 RestoreWindowPlacement( hDlg, &wpMoveHistory );
\r
319 RefreshMemoContent();
\r
321 MemoContentUpdated();
\r
326 switch (LOWORD(wParam)) {
\r
328 EndDialog(hDlg, TRUE);
\r
332 EndDialog(hDlg, FALSE);
\r
342 if( wParam == IDC_MoveHistory ) {
\r
343 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
345 if( lpMF->msg == WM_LBUTTONDBLCLK && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
349 pt.x = LOWORD( lpMF->lParam );
\r
350 pt.y = HIWORD( lpMF->lParam );
\r
352 index = SendDlgItemMessage( hDlg, IDC_MoveHistory, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
354 index = FindMoveByCharIndex( index );
\r
357 ToNrEvent( index + 1 );
\r
360 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
361 lpMF->msg = WM_USER;
\r
368 case WM_REFRESH_HISTORY:
\r
369 /* Update the GUI */
\r
370 if( OnlyCurrentPositionChanged() ) {
\r
371 /* Only "cursor" changed, no need to update memo content */
\r
373 else if( OneMoveAppended() ) {
\r
374 AppendMoveToMemo( currCurrent );
\r
377 RefreshMemoContent();
\r
380 MemoContentUpdated();
\r
385 SetWindowPos( GetDlgItem( moveHistoryDialog, IDC_MoveHistory ),
\r
387 H_MARGIN, V_MARGIN,
\r
388 LOWORD(lParam) - 2*H_MARGIN,
\r
389 HIWORD(lParam) - 2*V_MARGIN,
\r
393 case WM_GETMINMAXINFO:
\r
395 MINMAXINFO * mmi = (MINMAXINFO *) lParam;
\r
397 mmi->ptMinTrackSize.x = 100;
\r
398 mmi->ptMinTrackSize.y = 100;
\r
403 MoveHistoryPopDown();
\r
406 case WM_ENTERSIZEMOVE:
\r
407 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
410 return OnSizing( &sd, hDlg, wParam, lParam );
\r
413 return OnMoving( &sd, hDlg, wParam, lParam );
\r
415 case WM_EXITSIZEMOVE:
\r
416 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
422 VOID MoveHistoryPopUp()
\r
426 CheckMenuItem(GetMenu(hwndMain), IDM_ShowMoveHistory, MF_CHECKED);
\r
428 if( moveHistoryDialog ) {
\r
429 SendMessage( moveHistoryDialog, WM_INITDIALOG, 0, 0 );
\r
431 if( ! moveHistoryDialogUp ) {
\r
432 ShowWindow(moveHistoryDialog, SW_SHOW);
\r
436 lpProc = MakeProcInstance( (FARPROC) HistoryDialogProc, hInst );
\r
438 /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
\r
439 CreateDialog( hInst, MAKEINTRESOURCE(DLG_MoveHistory), hwndMain, (DLGPROC)lpProc );
\r
441 FreeProcInstance(lpProc);
\r
444 moveHistoryDialogUp = TRUE;
\r
447 VOID MoveHistoryPopDown()
\r
449 CheckMenuItem(GetMenu(hwndMain), IDM_ShowMoveHistory, MF_UNCHECKED);
\r
451 if( moveHistoryDialog ) {
\r
452 ShowWindow(moveHistoryDialog, SW_HIDE);
\r
455 moveHistoryDialogUp = FALSE;
\r
458 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo )
\r
460 /* [AS] Danger! For now we rely on the movelist parameter being a static variable! */
\r
462 currMovelist = movelist;
\r
465 currCurrent = current;
\r
466 currPvInfo = pvInfo;
\r
468 if( moveHistoryDialog ) {
\r
469 SendMessage( moveHistoryDialog, WM_REFRESH_HISTORY, 0, 0 );
\r
473 BOOL MoveHistoryIsUp()
\r
475 return moveHistoryDialogUp;
\r