From 60876535e02fb87e638a0aa94aa80490093099fc Mon Sep 17 00:00:00 2001 From: H.G. Muller Date: Thu, 18 Feb 2010 17:42:48 +0100 Subject: [PATCH] Support playing through PGN variation comments Right-clicking a variation line in the comment dialog will shelve the current variation, and parse the comment to replace it. This uses ParsePV() in an alternative way, for which the latter had to be enhanced: it was made resistent to move numbers and comments inside the PV. It now has an argument to indicate if such comments should be stored with the moves. The Comment Popup is closed on revert, to make sure no comments to moves that are destroyed, containing variations on a non-valid line, can keep hanging around. --- backend.c | 78 +++++++++++++++++++++++++++++++++++++++++++++------ backend.h | 1 + winboard/winboard.c | 33 +++++++++++++++++++++- 3 files changed, 102 insertions(+), 10 deletions(-) diff --git a/backend.c b/backend.c index 932e5bb..424463d 100644 --- a/backend.c +++ b/backend.c @@ -4816,17 +4816,17 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar) void -ParsePV(char *pv) +ParsePV(char *pv, Boolean storeComments) { // Parse a string of PV moves, and append to current game, behind forwardMostMove int fromX, fromY, toX, toY; char promoChar; ChessMove moveType; Boolean valid; - int nr = 0; + int nr = 0, dummy; endPV = forwardMostMove; do { while(*pv == ' ') pv++; - if(*pv == '(') pv++; // first (ponder) move can be in parentheses + if(nr == 0 && *pv == '(') pv++; // first (ponder) move can be in parentheses valid = ParseOneMove(pv, endPV, &moveType, &fromX, &fromY, &toX, &toY, &promoChar); if(appData.debugMode){ fprintf(debugFP,"parsePV: %d %c%c%c%c '%s'\n", valid, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, pv); @@ -4848,9 +4848,30 @@ fprintf(debugFP,"parsePV: %d %c%c%c%c '%s'\n", valid, fromX+AAA, fromY+ONE, toX+ strcpy(moveList[endPV-2], "_0_0"); // suppress premove highlight on takeback move } } + if(moveType == Comment) { + // [HGM] vari: try to skip comment + int level = 0; char c, *start = pv, wait = NULLCHAR; + do { + if(!wait) { + if(*pv == '(') level++; else + if(*pv == ')' && level) level--; + } + if(*pv == wait) wait = NULLCHAR; else + if(*pv == '{') wait = '}'; else + if(*pv == '[') wait = ']'; + pv++; + } while(*pv && (wait || level)); + if(storeComments) { + c = *pv; *pv = NULLCHAR; + AppendComment(endPV, start, FALSE); + *pv = c; + } + valid++; // allow comments in PV + continue; } + if(sscanf(pv, "%d...", &dummy) == 1 || sscanf(pv, "%d.", &dummy) == 1) + while(*pv && *pv++ != ' '); // skip any move numbers while(*pv && *pv++ != ' '); // skip what we parsed; assume space separators - if(moveType == Comment) { valid++; continue; } // allow comments in PV nr++; if(endPV+1 > framePtr) break; // no space, truncate if(!valid) break; @@ -4861,7 +4882,13 @@ fprintf(debugFP,"parsePV: %d %c%c%c%c '%s'\n", valid, fromX+AAA, fromY+ONE, toX+ moveList[endPV-1][1] = fromY + ONE; moveList[endPV-1][2] = toX + AAA; moveList[endPV-1][3] = toY + ONE; - parseList[endPV-1][0] = NULLCHAR; + if(storeComments) + CoordsToAlgebraic(boards[endPV - 1], + PosFlags(endPV - 1), + fromY, fromX, toY, toX, promoChar, + parseList[endPV - 1]); + else + parseList[endPV-1][0] = NULLCHAR; } while(valid); currentMove = endPV; if(currentMove == forwardMostMove) ClearPremoveHighlights(); else @@ -4885,7 +4912,7 @@ LoadMultiPV(int x, int y, char *buf, int index, int *start, int *end) index = startPV; while(buf[index] && buf[index] != '\n') index++; buf[index] = 0; - ParsePV(buf+startPV); + ParsePV(buf+startPV, FALSE); *start = startPV; *end = index-1; return TRUE; } @@ -4895,7 +4922,7 @@ LoadPV(int x, int y) { // called on right mouse click to load PV int which = gameMode == TwoMachinesPlay && (WhiteOnMove(forwardMostMove) == (second.twoMachinesColor[0] == 'w')); lastX = x; lastY = y; - ParsePV(lastPV[which]); // load the PV of the thinking engine in the boards array. + ParsePV(lastPV[which], FALSE); // load the PV of the thinking engine in the boards array. return TRUE; } @@ -15030,7 +15057,7 @@ PushTail(int firstMove, int lastMove) } storedGames++; - forwardMostMove = currentMove; // truncte game so we can start variation + forwardMostMove = firstMove; // truncate game so we can start variation if(storedGames == 1) GreyRevert(FALSE); } @@ -15042,6 +15069,7 @@ PopTail(Boolean annotate) if(appData.icsActive) return FALSE; // only in local mode if(!storedGames) return FALSE; // sanity + CommentPopDown(); // make sure no stale variation comments to the destroyed line can remain open storedGames--; ToNrEvent(savedFirst[storedGames]); // sets currentMove @@ -15060,7 +15088,7 @@ PopTail(Boolean annotate) } strcat(buf, ")"); } - for(i=1; i 0 && --level == 0 && p-text > index && end == NULL) end = p-1; + } + if(*p == wait) wait = NULLCHAR; // closing ]} found + p++; + } + if(!start || !end) return; // no variation found, or syntax error in PGN: ignore click + if(appData.debugMode) fprintf(debugFP, "at move %d load variation '%s'\n", currentMove, start); + end[1] = NULLCHAR; // clip off comment beyond variation + ToNrEvent(currentMove-1); + PushTail(currentMove, forwardMostMove); // shelve main variation. This truncates game + // kludge: use ParsePV() to append variation to game + move = currentMove; + ParsePV(start, TRUE); + forwardMostMove = endPV; endPV = -1; currentMove = move; // cleanup what ParsePV did + ClearPremoveHighlights(); + CommentPopDown(); + ToNrEvent(currentMove+1); +} diff --git a/backend.h b/backend.h index aac6ab1..2b2a2a7 100644 --- a/backend.h +++ b/backend.h @@ -194,6 +194,7 @@ void EditCommentEvent P((void)); void ReplaceComment P((int index, char *text)); int ReplaceTags P((char *tags, GameInfo *gi));/* returns nonzero on error */ void AppendComment P((int index, char *text, Boolean addBraces)); +void LoadVariation P((int index, char *text)); void ReloadCmailMsgEvent P((int unregister)); void MailMoveEvent P((void)); void EditTagsEvent P((void)); diff --git a/winboard/winboard.c b/winboard/winboard.c index e0a6969..e417559 100644 --- a/winboard/winboard.c +++ b/winboard/winboard.c @@ -5855,6 +5855,7 @@ CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) sizeY = newSizeY; } } + SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS ); return FALSE; case WM_COMMAND: /* message: received a command */ @@ -5899,6 +5900,36 @@ CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) } break; + case WM_NOTIFY: // [HGM] vari: cloned from whistory.c + if( wParam == OPT_CommentText ) { + MSGFILTER * lpMF = (MSGFILTER *) lParam; + + if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) { + POINTL pt; + LRESULT index; + + pt.x = LOWORD( lpMF->lParam ); + pt.y = HIWORD( lpMF->lParam ); + + index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt ); + + hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above + len = GetWindowTextLength(hwndText); + str = (char *) malloc(len + 1); + GetWindowText(hwndText, str, len + 1); + ReplaceComment(commentIndex, str); + if(commentIndex != currentMove) ToNrEvent(commentIndex); + LoadVariation( index, str ); // [HGM] also does the actual moving to it, now + free(str); + + /* Zap the message for good: apparently, returning non-zero is not enough */ + lpMF->msg = WM_USER; + + return TRUE; + } + } + break; + case WM_SIZE: newSizeX = LOWORD(lParam); newSizeY = HIWORD(lParam); @@ -8090,7 +8121,7 @@ VOID CommentPopUp(char *title, char *str) { HWND hwnd = GetActiveWindow(); - EitherCommentPopUp(0, title, str, FALSE); + EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0 SAY(str); SetActiveWindow(hwnd); } -- 1.7.0.4