From: Arun Persaud Date: Sun, 7 Feb 2010 00:15:15 +0000 (-0800) Subject: Merge branch 'master' into gtk X-Git-Tag: gtk-20100206~14 X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=4042b38821940cd48e9f3d10b5208da7b6288dfd;hp=4626ea070c35a9c59ae231280f96e215dfc9205f;p=xboard.git Merge branch 'master' into gtk Conflicts: Makefile.am backend.c configure.ac xboard.c xgamelist.c --- diff --git a/Makefile.am b/Makefile.am index 131a9fc..6e5e713 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,13 +40,17 @@ EXTRA_DIST = pixmaps bitmaps svg sounds \ DISTCLEANFILES = stamp-h -AM_CPPFLAGS=-DINFODIR='"$(infodir)"' @GTK_CFLAGS@ + +AM_CPPFLAGS=-DINFODIR='"$(infodir)"' @GTK_CFLAGS@ -DSYSCONFDIR='"$(sysconfdir)"' + info_TEXINFOS = xboard.texi xboard_TEXINFOS = copyright.texi man6_MANS = xboard.man -sysconf_DATA=xboard.conf + +dist_sysconf_DATA = xboard.conf + xboard.man: xboard.texi copyright.texi gpl.texinfo version.texi $(srcdir)/texi2man $(srcdir)/xboard.texi > xboard.man || (rm -f xboard.man ; false) diff --git a/args.h b/args.h index ff9b55b..e85deb4 100644 --- a/args.h +++ b/args.h @@ -241,6 +241,9 @@ ArgDescriptor argDescriptors[] = { { "internetChessserverHelper", ArgFilename, (void *) &appData.icsHelper, FALSE, INVALID }, // for XB { "icshelper", ArgFilename, (void *) &appData.icsHelper, FALSE, (ArgIniType) "" }, + { "seekGraph", ArgBoolean, (void *) &appData.seekGraph, TRUE, (ArgIniType) FALSE }, + { "sg", ArgTrue, (void *) &appData.seekGraph, FALSE, INVALID }, + { "autoRefresh", ArgBoolean, (void *) &appData.autoRefresh, TRUE, (ArgIniType) FALSE }, { "gateway", ArgString, (void *) &appData.gateway, FALSE, (ArgIniType) "" }, { "loadGameFile", ArgFilename, (void *) &appData.loadGameFile, FALSE, (ArgIniType) "" }, { "lgf", ArgFilename, (void *) &appData.loadGameFile, FALSE, INVALID }, @@ -539,6 +542,7 @@ ArgDescriptor argDescriptors[] = { { "defaultPathEGTB", ArgFilename, (void *) &appData.defaultPathEGTB, TRUE, (ArgIniType) "c:\\egtb" }, /* [HGM] board-size, adjudication and misc. options */ + { "oneClickMove", ArgBoolean, (void *) &appData.oneClick, TRUE, (ArgIniType) FALSE }, { "boardWidth", ArgInt, (void *) &appData.NrFiles, TRUE, (ArgIniType) -1 }, { "boardHeight", ArgInt, (void *) &appData.NrRanks, TRUE, (ArgIniType) -1 }, { "holdingsSize", ArgInt, (void *) &appData.holdingsSize, TRUE, (ArgIniType) -1 }, @@ -699,6 +703,14 @@ ExitArgError(char *msg, char *badArg) exit(2); } +int +ValidateInt(char *s) +{ + char *p = s; + if(*p == '-' || *p == '+') p++; + while(*p) if(!isdigit(*p++)) ExitArgError("Bad integer value", s); + return atoi(s); +} char StringGet(void *getClosure) @@ -919,19 +931,19 @@ ParseArgs(GetFunc get, void *cl) switch (ad->argType) { case ArgInt: - *(int *) ad->argLoc = atoi(argValue); + *(int *) ad->argLoc = ValidateInt(argValue); break; case ArgX: - *(int *) ad->argLoc = atoi(argValue) + wpMain.x; // [HGM] placement: translate stored relative to absolute + *(int *) ad->argLoc = ValidateInt(argValue) + wpMain.x; // [HGM] placement: translate stored relative to absolute break; case ArgY: - *(int *) ad->argLoc = atoi(argValue) + wpMain.y; // (this is really kludgey, it should be done where used...) + *(int *) ad->argLoc = ValidateInt(argValue) + wpMain.y; // (this is really kludgey, it should be done where used...) break; case ArgZ: - *(int *) ad->argLoc = atoi(argValue); + *(int *) ad->argLoc = ValidateInt(argValue); EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY); break; diff --git a/backend.c b/backend.c index 6095cc6..4d23241 100644 --- a/backend.c +++ b/backend.c @@ -167,6 +167,7 @@ int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY, /*char*/int promoChar)); void BackwardInner P((int target)); void ForwardInner P((int target)); +int Adjudicate P((ChessProgramState *cps)); void GameEnds P((ChessMove result, char *resultDetails, int whosays)); void EditPositionDone P((Boolean fakeRights)); void PrintOpponents P((FILE *fp)); @@ -187,6 +188,7 @@ void DisplayMove P((int moveNumber)); void ParseGameHistory P((char *game)); void ParseBoard12 P((char *string)); +void KeepAlive P((void)); void StartClocks P((void)); void SwitchClocks P((void)); void StopClocks P((void)); @@ -244,6 +246,7 @@ char endingGame = 0; /* [HGM] crash: flag to prevent recursion of GameEnds() int whiteNPS, blackNPS; /* [HGM] nps: for easily making clocks aware of NPS */ VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */ int lastIndex = 0; /* [HGM] autoinc: last game/position used in match mode */ +Boolean connectionAlive;/* [HGM] alive: ICS connection status from probing */ int opponentKibitzes; int lastSavedGame; /* [HGM] save: ID of game */ char chatPartner[MAX_CHAT][MSG_SIZ]; /* [HGM] chat: list of chatting partners */ @@ -1117,11 +1120,14 @@ InitBackEnd3 P((void)) DisplayFatalError(buf, err, 1); return; } - SetICSMode(); - telnetISR = - AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR); - fromUserISR = - AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR); + + SetICSMode(); + telnetISR = + AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR); + fromUserISR = + AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR); + if(appData.keepAlive) // [HGM] alive: schedule sending of dummy 'date' command + ScheduleDelayedEvent(KeepAlive, appData.keepAlive*60*1000); } else if (appData.noChessProgram) { @@ -1509,6 +1515,8 @@ read_from_player(isr, closure, message, count, error) void KeepAlive() { // [HGM] alive: periodically send dummy (date) command to ICS to prevent time-out + if(!connectionAlive) DisplayFatalError("No response from ICS", 0, 1); + connectionAlive = FALSE; // only sticks if no response to 'date' command. SendToICS("date\n"); if(appData.keepAlive) ScheduleDelayedEvent(KeepAlive, appData.keepAlive*60*1000); } @@ -2125,6 +2133,203 @@ static int player2Rating = -1; ColorClass curColor = ColorNormal; int suppressKibitz = 0; +// [HGM] seekgraph +Boolean soughtPending = FALSE; +Boolean seekGraphUp; +#define MAX_SEEK_ADS 200 +#define SQUARE 0x80 +char *seekAdList[MAX_SEEK_ADS]; +int ratingList[MAX_SEEK_ADS], xList[MAX_SEEK_ADS], yList[MAX_SEEK_ADS], seekNrList[MAX_SEEK_ADS], zList[MAX_SEEK_ADS]; +float tcList[MAX_SEEK_ADS]; +char colorList[MAX_SEEK_ADS]; +int nrOfSeekAds = 0; +int minRating = 1010, maxRating = 2800; +int hMargin = 10, vMargin = 20, h, w; +extern int squareSize, lineGap; + +void +PlotSeekAd(int i) +{ + int x, y, color = 0, r = ratingList[i]; float tc = tcList[i]; + xList[i] = yList[i] = -100; // outside graph, so cannot be clicked + if(r < minRating+100 && r >=0 ) r = minRating+100; + if(r > maxRating) r = maxRating; + if(tc < 1.) tc = 1.; + if(tc > 95.) tc = 95.; + x = (w-hMargin)* log(tc)/log(100.) + hMargin; + y = ((double)r - minRating)/(maxRating - minRating) + * (h-vMargin-squareSize/8-1) + vMargin; + if(ratingList[i] < 0) y = vMargin + squareSize/4; + if(strstr(seekAdList[i], " u ")) color = 1; + if(!strstr(seekAdList[i], "lightning") && // for now all wilds same color + !strstr(seekAdList[i], "bullet") && + !strstr(seekAdList[i], "blitz") && + !strstr(seekAdList[i], "standard") ) color = 2; + if(strstr(seekAdList[i], "(C) ")) color |= SQUARE; // plot computer seeks as squares + DrawSeekDot(xList[i]=x+3*(color&~SQUARE), yList[i]=h-1-y, colorList[i]=color); +} + +void +AddAd(char *handle, char *rating, int base, int inc, char rated, char *type, int nr, Boolean plot) +{ + char buf[MSG_SIZ], *ext = ""; + VariantClass v = StringToVariant(type); + if(strstr(type, "wild")) { + ext = type + 4; // append wild number + if(v == VariantFischeRandom) type = "chess960"; else + if(v == VariantLoadable) type = "setup"; else + type = VariantName(v); + } + sprintf(buf, "%s (%s) %d %d %c %s%s", handle, rating, base, inc, rated, type, ext); + if(nrOfSeekAds < MAX_SEEK_ADS-1) { + if(seekAdList[nrOfSeekAds]) free(seekAdList[nrOfSeekAds]); + ratingList[nrOfSeekAds] = -1; // for if seeker has no rating + sscanf(rating, "%d", &ratingList[nrOfSeekAds]); + tcList[nrOfSeekAds] = base + (2./3.)*inc; + seekNrList[nrOfSeekAds] = nr; + zList[nrOfSeekAds] = 0; + seekAdList[nrOfSeekAds++] = StrSave(buf); + if(plot) PlotSeekAd(nrOfSeekAds-1); + } +} + +void +EraseSeekDot(int i) +{ + int x = xList[i], y = yList[i], d=squareSize/4, k; + DrawSeekBackground(x-squareSize/8, y-squareSize/8, x+squareSize/8+1, y+squareSize/8+1); + if(x < hMargin+d) DrawSeekAxis(hMargin, y-squareSize/8, hMargin, y+squareSize/8+1); + // now replot every dot that overlapped + for(k=0; k x-d && yy <= y+d && yy > y-d) + DrawSeekDot(xx, yy, colorList[k]); + } +} + +void +RemoveSeekAd(int nr) +{ + int i; + for(i=0; i=minRating && i40 ? i%20 : i%10) == 0) { + char buf[MSG_SIZ]; + sprintf(buf, "%d", i); + DrawSeekText(buf, xx-2-3*(i>9), h-1-vMargin/2); + } + } + for(i=0; i0) zList[i] *= 0.8; // age priority + } + if(dist < 120) { + char buf[MSG_SIZ]; + second = (second > 1); + if(displayed != closest || second != lastSecond) { + DisplayMessage(second ? "!" : "", seekAdList[closest]); + lastSecond = second; displayed = closest; + } + sprintf(buf, "play %d\n", seekNrList[closest]); + if(click == Press) { + if(moving == 2) zList[closest] = 100; // right-click; push to back on press + lastDown = closest; + return TRUE; + } // on press 'hit', only show info + if(moving == 2) return TRUE; // ignore right up-clicks on dot + SendToICS(ics_prefix); + SendToICS(buf); // should this be "sought all"? + } else if(click == Release) { // release 'miss' is ignored + zList[lastDown] = 100; // make future selection of the rejected ad more difficult + if(moving == 2) { // right up-click + nrOfSeekAds = 0; // refresh graph + soughtPending = TRUE; + SendToICS(ics_prefix); + SendToICS("sought\n"); // should this be "sought all"? + } + return TRUE; + } else if(moving) { if(displayed >= 0) DisplayMessage("", ""); displayed = -1; return TRUE; } + // press miss or release hit 'pop down' seek graph + seekGraphUp = FALSE; + DrawPosition(TRUE, NULL); + } + return TRUE; +} + void read_from_ics(isr, closure, data, count, error) InputSourceRef isr; @@ -2162,6 +2367,8 @@ read_from_ics(isr, closure, data, count, error) char talker[MSG_SIZ]; // [HGM] chat int channel; + connectionAlive = TRUE; // [HGM] alive: I think, therefore I am... + if (appData.debugMode) { if (!error) { fprintf(debugFP, "= 0) // channel broadcast; look if there is a chatbox for this channel for(p=0; p= 3) // [HGM] chat: continuation of line for chat box + chattingPartner = savingComment - 3; // kludge to remember the box } else { started = STARTED_CHATTER; } @@ -2933,6 +3187,11 @@ read_from_ics(isr, closure, data, count, error) if (looking_at(buf, &i, "% ") || ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE) && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book + if(ics_type == ICS_ICC && soughtPending) { // [HGM] seekgraph: on ICC sought-list has no termination line + soughtPending = FALSE; + seekGraphUp = TRUE; + DrawSeekGraph(); + } if(suppressKibitz) next_out = i; savingComment = FALSE; suppressKibitz = 0; @@ -4103,7 +4362,9 @@ ParseBoard12(string) ((gameMode == IcsPlayingBlack) && (!WhiteOnMove(currentMove)))) ClearPremoveHighlights(); - DrawPosition(FALSE, boards[currentMove]); + j = seekGraphUp; seekGraphUp = FALSE; // [HGM] seekgraph: when we draw a board, it overwrites the seek graph + DrawPosition(j, boards[currentMove]); + DisplayMove(moveNum - 1); if (appData.ringBellAfterMoves && /*!ics_user_moved*/ // [HGM] use absolute method to recognize own move !((gameMode == IcsPlayingWhite) && (!WhiteOnMove(moveNum)) || @@ -5357,6 +5618,55 @@ OKToStartUserMove(x, y) return TRUE; } +Boolean +OnlyMove(int *x, int *y) { + DisambiguateClosure cl; + if (appData.zippyPlay) return FALSE; + switch(gameMode) { + case MachinePlaysBlack: + case IcsPlayingWhite: + case BeginningOfGame: + if(!WhiteOnMove(currentMove)) return FALSE; + break; + case MachinePlaysWhite: + case IcsPlayingBlack: + if(WhiteOnMove(currentMove)) return FALSE; + break; + default: + return FALSE; + } + cl.pieceIn = EmptySquare; + cl.rfIn = *y; + cl.ffIn = *x; + cl.rtIn = -1; + cl.ftIn = -1; + cl.promoCharIn = NULLCHAR; + Disambiguate(boards[currentMove], PosFlags(currentMove), &cl); + if(cl.kind == NormalMove) { + fromX = cl.ff; + fromY = cl.rf; + *x = cl.ft; + *y = cl.rt; + return TRUE; + } + if(cl.kind != ImpossibleMove) return FALSE; + cl.pieceIn = EmptySquare; + cl.rfIn = -1; + cl.ffIn = -1; + cl.rtIn = *y; + cl.ftIn = *x; + cl.promoCharIn = NULLCHAR; + Disambiguate(boards[currentMove], PosFlags(currentMove), &cl); + if(cl.kind == NormalMove) { + fromX = cl.ff; + fromY = cl.rf; + *x = cl.ft; + *y = cl.rt; + return TRUE; + } + return FALSE; +} + FILE *lastLoadGameFP = NULL, *lastLoadPositionFP = NULL; int lastLoadGameNumber = 0, lastLoadPositionNumber = 0; int lastLoadGameUseList = FALSE; @@ -5521,7 +5831,6 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn) return WhiteDrop; /* Not needed to specify white or black yet */ } - userOfferedDraw = FALSE; /* [HGM] always test for legality, to get promotion info */ moveType = LegalityTest(boards[currentMove], PosFlags(currentMove), @@ -5654,7 +5963,10 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/ - if (gameMode == BeginningOfGame) + + if(Adjudicate(NULL)) return 1; // [HGM] adjudicate: take care of automtic game end + +if (gameMode == BeginningOfGame) { if (appData.noChessProgram) { @@ -5677,38 +5989,37 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) StartClocks(); } ModeHighlight(); + } /* Relay move to ICS or chess engine */ - if (appData.icsActive) - { - if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || - gameMode == IcsExamining) - { - SendMoveToICS(moveType, fromX, fromY, toX, toY); - ics_user_moved = 1; - } + + if (appData.icsActive) { + if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || + gameMode == IcsExamining) { + if(userOfferedDraw && (signed char)boards[forwardMostMove][EP_STATUS] <= EP_DRAWS) { + SendToICS(ics_prefix); // [HGM] drawclaim: send caim and move on one line for FICS + SendToICS("draw "); + SendMoveToICS(moveType, fromX, fromY, toX, toY); + } + // also send plain move, in case ICS does not understand atomic claims + SendMoveToICS(moveType, fromX, fromY, toX, toY); + ics_user_moved = 1; + } + } else { + if (first.sendTime && (gameMode == BeginningOfGame || + gameMode == MachinePlaysWhite || + gameMode == MachinePlaysBlack)) { + SendTimeRemaining(&first, gameMode != MachinePlaysBlack); + } + if (gameMode != EditGame && gameMode != PlayFromGameFile) { + // [HGM] book: if program might be playing, let it use book + bookHit = SendMoveToBookUser(forwardMostMove-1, &first, FALSE); + first.maybeThinking = TRUE; + } else SendMoveToProgram(forwardMostMove-1, &first); + if (currentMove == cmailOldMove + 1) { + cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE; } - else - { - if (first.sendTime && (gameMode == BeginningOfGame || - gameMode == MachinePlaysWhite || - gameMode == MachinePlaysBlack)) - { - SendTimeRemaining(&first, gameMode != MachinePlaysBlack); - } - if (gameMode != EditGame && gameMode != PlayFromGameFile) - { - // [HGM] book: if program might be playing, let it use book - bookHit = SendMoveToBookUser(forwardMostMove-1, &first, FALSE); - first.maybeThinking = TRUE; - } - else - SendMoveToProgram(forwardMostMove-1, &first); - if (currentMove == cmailOldMove + 1) - { - cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE; - } } ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ @@ -5744,10 +6055,12 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) default: break; } - + userOfferedDraw = FALSE; // [HGM] drawclaim: after move made, and tested for claimable draw + if(bookHit) { // [HGM] book: simulate book reply - static char bookMove[MSG_SIZ]; // a bit generous? + static char bookMove[MSG_SIZ]; // a bit generous? + programStats.nodes = programStats.depth = programStats.time = programStats.score = programStats.got_only_move = 0; @@ -5827,6 +6140,12 @@ void LeftClick(ClickType clickType, int xPix, int yPix) static int second = 0, promotionChoice = 0; char promoChoice = NULLCHAR; + if(appData.seekGraph && appData.icsActive && loggedOn && + (gameMode == BeginningOfGame || gameMode == IcsIdle)) { + SeekGraphClick(clickType, xPix, yPix, 0); + return; + } + if (clickType == Press) ErrorPopDown(); MarkTargetSquares(1); @@ -5868,6 +6187,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix) return; if (fromX == -1) { + if(!appData.oneClick || !OnlyMove(&x, &y)) { if (clickType == Press) { /* First square */ if (OKToStartUserMove(x, y)) { @@ -5882,6 +6202,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix) } } return; + } } /* fromX != -1 */ @@ -5977,13 +6298,13 @@ void LeftClick(ClickType clickType, int xPix, int yPix) if(x == BOARD_LEFT-2 && piece >= BlackPawn) { n = PieceToNumber(piece - (int)BlackPawn); - if(n > gameInfo.holdingsSize) { n = 0; piece = BlackPawn; } + if(n >= gameInfo.holdingsSize) { n = 0; piece = BlackPawn; } boards[currentMove][BOARD_HEIGHT-1 - n][0] = piece; boards[currentMove][BOARD_HEIGHT-1 - n][1]++; } else if(x == BOARD_RGHT+1 && piece < BlackPawn) { n = PieceToNumber(piece); - if(n > gameInfo.holdingsSize) { n = 0; piece = WhitePawn; } + if(n >= gameInfo.holdingsSize) { n = 0; piece = WhitePawn; } boards[currentMove][n][BOARD_WIDTH-1] = piece; boards[currentMove][n][BOARD_WIDTH-2]++; } @@ -6027,6 +6348,69 @@ void LeftClick(ClickType clickType, int xPix, int yPix) } } +int RightClick(ClickType action, int x, int y, int *fromX, int *fromY) +{ // front-end-free part taken out of PieceMenuPopup + int whichMenu; int xSqr, ySqr; + + if(seekGraphUp) { // [HGM] seekgraph + if(action == Press) SeekGraphClick(Press, x, y, 2); // 2 indicates right-click: no pop-down on miss + if(action == Release) SeekGraphClick(Release, x, y, 2); // and no challenge on hit + return -2; + } + + xSqr = EventToSquare(x, BOARD_WIDTH); + ySqr = EventToSquare(y, BOARD_HEIGHT); + if (action == Release) UnLoadPV(); // [HGM] pv + if (action != Press) return -2; // return code to be ignored + switch (gameMode) { + case IcsExamining: + if(xSqr < BOARD_LEFT || xSqr >= BOARD_RGHT) return -1; + case EditPosition: + if (xSqr == BOARD_LEFT-1 || xSqr == BOARD_RGHT) return -1; + if (xSqr < 0 || ySqr < 0) return -1; + whichMenu = 0; // edit-position menu + break; + case IcsObserving: + if(!appData.icsEngineAnalyze) return -1; + case IcsPlayingWhite: + case IcsPlayingBlack: + if(!appData.zippyPlay) goto noZip; + case AnalyzeMode: + case AnalyzeFile: + case MachinePlaysWhite: + case MachinePlaysBlack: + case TwoMachinesPlay: // [HGM] pv: use for showing PV + if (!appData.dropMenu) { + LoadPV(x, y); + return 2; // flag front-end to grab mouse events + } + if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode || + gameMode == AnalyzeFile || gameMode == IcsObserving) return -1; + case EditGame: + noZip: + if (xSqr < 0 || ySqr < 0) return -1; + if (!appData.dropMenu || appData.testLegality && + gameInfo.variant != VariantBughouse && + gameInfo.variant != VariantCrazyhouse) return -1; + whichMenu = 1; // drop menu + break; + default: + return -1; + } + + if (((*fromX = xSqr) < 0) || + ((*fromY = ySqr) < 0)) { + *fromX = *fromY = -1; + return -1; + } + if (flipView) + *fromX = BOARD_WIDTH - 1 - *fromX; + else + *fromY = BOARD_HEIGHT - 1 - *fromY; + + return whichMenu; +} + void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats ) { // char * hint = lastHint; @@ -6053,6 +6437,361 @@ void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cp SetProgramStats( &stats ); } +int +Adjudicate(ChessProgramState *cps) +{ // [HGM] some adjudications useful with buggy engines + // [HGM] adjudicate: made into separate routine, which now can be called after every move + // In any case it determnes if the game is a claimable draw (filling in EP_STATUS). + // Actually ending the game is now based on the additional internal condition canAdjudicate. + // Only when the game is ended, and the opponent is a computer, this opponent gets the move relayed. + int k, count = 0; static int bare = 1; + ChessProgramState *engineOpponent = (gameMode == TwoMachinesPlay ? cps->other : (cps ? NULL : &first)); + Boolean canAdjudicate = !appData.icsActive; + + // most tests only when we understand the game, i.e. legality-checking on, and (for the time being) no piece drops + if(gameInfo.holdingsSize == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { + if( appData.testLegality ) + { /* [HGM] Some more adjudications for obstinate engines */ + int NrWN=0, NrBN=0, NrWB=0, NrBB=0, NrWR=0, NrBR=0, + NrWQ=0, NrBQ=0, NrW=0, NrK=0, bishopsColor = 0, + NrPieces=0, NrPawns=0, PawnAdvance=0, i, j; + static int moveCount = 6; + ChessMove result; + char *reason = NULL; + + + /* Count what is on board. */ + for(i=0; i 1 ? WhiteWins : NrPieces - NrW > 1 ? BlackWins : GameIsDrawn, + "Xboard adjudication: Bare king", GE_XBOARD ); + return 1; + } + } + } else bare = 1; + + + // don't wait for engine to announce game end if we can judge ourselves + switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove)) ) { + case MT_CHECK: + if(gameInfo.variant == Variant3Check) { // [HGM] 3check: when in check, test if 3rd time + int i, checkCnt = 0; // (should really be done by making nr of checks part of game state) + for(i=forwardMostMove-2; i>=backwardMostMove; i-=2) { + if(MateTest(boards[i], PosFlags(i)) == MT_CHECK) + checkCnt++; + if(checkCnt >= 2) { + reason = "Xboard adjudication: 3rd check"; + boards[forwardMostMove][EP_STATUS] = EP_CHECKMATE; + break; + } + } + } + case MT_NONE: + default: + break; + case MT_STALEMATE: + case MT_STAINMATE: + reason = "Xboard adjudication: Stalemate"; + if((signed char)boards[forwardMostMove][EP_STATUS] != EP_CHECKMATE) { // [HGM] don't touch win through baring or K-capt + boards[forwardMostMove][EP_STATUS] = EP_STALEMATE; // default result for stalemate is draw + if(gameInfo.variant == VariantLosers || gameInfo.variant == VariantGiveaway) // [HGM] losers: + boards[forwardMostMove][EP_STATUS] = EP_WINS; // in these variants stalemated is always a win + else if(gameInfo.variant == VariantSuicide) // in suicide it depends + boards[forwardMostMove][EP_STATUS] = NrW == NrPieces-NrW ? EP_STALEMATE : + ((NrW < NrPieces-NrW) != WhiteOnMove(forwardMostMove) ? + EP_CHECKMATE : EP_WINS); + else if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi) + boards[forwardMostMove][EP_STATUS] = EP_CHECKMATE; // and in these variants being stalemated loses + } + break; + case MT_CHECKMATE: + reason = "Xboard adjudication: Checkmate"; + boards[forwardMostMove][EP_STATUS] = (gameInfo.variant == VariantLosers ? EP_WINS : EP_CHECKMATE); + break; + } + + switch(i = (signed char)boards[forwardMostMove][EP_STATUS]) { + case EP_STALEMATE: + result = GameIsDrawn; break; + case EP_CHECKMATE: + result = WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins; break; + case EP_WINS: + result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins; break; + default: + result = (ChessMove) 0; + } + if(canAdjudicate && appData.checkMates && result) { // [HGM] mates: adjudicate finished games if requested + if(engineOpponent) + SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */ + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( result, reason, GE_XBOARD ); + return 1; + } + + /* Next absolutely insufficient mating material. */ + if( NrPieces == 2 || gameInfo.variant != VariantXiangqi && + gameInfo.variant != VariantShatranj && // [HGM] baring will remain possible + (NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 || + NrPieces == NrBB+NrWB+2 && bishopsColor != 3)) // [HGM] all Bishops (Ferz!) same color + { /* KBK, KNK, KK of KBKB with like Bishops */ + + /* always flag draws, for judging claims */ + boards[forwardMostMove][EP_STATUS] = EP_INSUF_DRAW; + + if(canAdjudicate && appData.materialDraws) { + /* but only adjudicate them if adjudication enabled */ + if(engineOpponent) { + SendToProgram("force\n", engineOpponent); // suppress reply + SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see last move */ + } + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( GameIsDrawn, "Xboard adjudication: Insufficient mating material", GE_XBOARD ); + return 1; + } + } + + /* Then some trivial draws (only adjudicate, cannot be claimed) */ + if(NrPieces == 4 && + ( NrWR == 1 && NrBR == 1 /* KRKR */ + || NrWQ==1 && NrBQ==1 /* KQKQ */ + || NrWN==2 || NrBN==2 /* KNNK */ + || NrWN+NrWB == 1 && NrBN+NrBB == 1 /* KBKN, KBKB, KNKN */ + ) ) { + if(canAdjudicate && --moveCount < 0 && appData.trivialDraws) + { /* if the first 3 moves do not show a tactical win, declare draw */ + if(engineOpponent) { + SendToProgram("force\n", engineOpponent); // suppress reply + SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */ + } + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( GameIsDrawn, "Xboard adjudication: Trivial draw", GE_XBOARD ); + return 1; + } + } else moveCount = 6; + } + } + + if (appData.debugMode) { int i; + fprintf(debugFP, "repeat test fmm=%d bmm=%d ep=%d, reps=%d\n", + forwardMostMove, backwardMostMove, boards[backwardMostMove][EP_STATUS], + appData.drawRepeats); + for( i=forwardMostMove; i>=backwardMostMove; i-- ) + fprintf(debugFP, "%d ep=%d\n", i, (signed char)boards[i][EP_STATUS]); + + } + + // Repetition draws and 50-move rule can be applied independently of legality testing + + /* Check for rep-draws */ + count = 0; + for(k = forwardMostMove-2; + k>=backwardMostMove && k>=forwardMostMove-100 && + (signed char)boards[k][EP_STATUS] < EP_UNKNOWN && + (signed char)boards[k+2][EP_STATUS] <= EP_NONE && (signed char)boards[k+1][EP_STATUS] <= EP_NONE; + k-=2) + { int rights=0; + if(CompareBoards(boards[k], boards[forwardMostMove])) { + /* compare castling rights */ + if( boards[forwardMostMove][CASTLING][2] != boards[k][CASTLING][2] && + (boards[k][CASTLING][0] != NoRights || boards[k][CASTLING][1] != NoRights) ) + rights++; /* King lost rights, while rook still had them */ + if( boards[forwardMostMove][CASTLING][2] != NoRights ) { /* king has rights */ + if( boards[forwardMostMove][CASTLING][0] != boards[k][CASTLING][0] || + boards[forwardMostMove][CASTLING][1] != boards[k][CASTLING][1] ) + rights++; /* but at least one rook lost them */ + } + if( boards[forwardMostMove][CASTLING][5] != boards[k][CASTLING][5] && + (boards[k][CASTLING][3] != NoRights || boards[k][CASTLING][4] != NoRights) ) + rights++; + if( boards[forwardMostMove][CASTLING][5] != NoRights ) { + if( boards[forwardMostMove][CASTLING][3] != boards[k][CASTLING][3] || + boards[forwardMostMove][CASTLING][4] != boards[k][CASTLING][4] ) + rights++; + } + if( canAdjudicate && rights == 0 && ++count > appData.drawRepeats-2 + && appData.drawRepeats > 1) { + /* adjudicate after user-specified nr of repeats */ + if(engineOpponent) { + SendToProgram("force\n", engineOpponent); // suppress reply + SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */ + } + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + if(gameInfo.variant == VariantXiangqi && appData.testLegality) { + // [HGM] xiangqi: check for forbidden perpetuals + int m, ourPerpetual = 1, hisPerpetual = 1; + for(m=forwardMostMove; m>k; m-=2) { + if(MateTest(boards[m], PosFlags(m)) != MT_CHECK) + ourPerpetual = 0; // the current mover did not always check + if(MateTest(boards[m-1], PosFlags(m-1)) != MT_CHECK) + hisPerpetual = 0; // the opponent did not always check + } + if(appData.debugMode) fprintf(debugFP, "XQ perpetual test, our=%d, his=%d\n", + ourPerpetual, hisPerpetual); + if(ourPerpetual && !hisPerpetual) { // we are actively checking him: forfeit + GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, + "Xboard adjudication: perpetual checking", GE_XBOARD ); + return 1; + } + if(hisPerpetual && !ourPerpetual) // he is checking us, but did not repeat yet + break; // (or we would have caught him before). Abort repetition-checking loop. + // Now check for perpetual chases + if(!ourPerpetual && !hisPerpetual) { // no perpetual check, test for chase + hisPerpetual = PerpetualChase(k, forwardMostMove); + ourPerpetual = PerpetualChase(k+1, forwardMostMove); + if(ourPerpetual && !hisPerpetual) { // we are actively chasing him: forfeit + GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, + "Xboard adjudication: perpetual chasing", GE_XBOARD ); + return 1; + } + if(hisPerpetual && !ourPerpetual) // he is chasing us, but did not repeat yet + break; // Abort repetition-checking loop. + } + // if neither of us is checking or chasing all the time, or both are, it is draw + } + GameEnds( GameIsDrawn, "Xboard adjudication: repetition draw", GE_XBOARD ); + return 1; + } + if( rights == 0 && count > 1 ) /* occurred 2 or more times before */ + boards[forwardMostMove][EP_STATUS] = EP_REP_DRAW; + } + } + + /* Now we test for 50-move draws. Determine ply count */ + count = forwardMostMove; + /* look for last irreversble move */ + while( (signed char)boards[count][EP_STATUS] <= EP_NONE && count > backwardMostMove ) + count--; + /* if we hit starting position, add initial plies */ + if( count == backwardMostMove ) + count -= initialRulePlies; + count = forwardMostMove - count; + if( count >= 100) + boards[forwardMostMove][EP_STATUS] = EP_RULE_DRAW; + /* this is used to judge if draw claims are legal */ + if(canAdjudicate && appData.ruleMoves > 0 && count >= 2*appData.ruleMoves) { + if(engineOpponent) { + SendToProgram("force\n", engineOpponent); // suppress reply + SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */ + } + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( GameIsDrawn, "Xboard adjudication: 50-move rule", GE_XBOARD ); + return 1; + } + + /* if draw offer is pending, treat it as a draw claim + * when draw condition present, to allow engines a way to + * claim draws before making their move to avoid a race + * condition occurring after their move + */ + if((gameMode == TwoMachinesPlay ? second.offeredDraw : userOfferedDraw) || first.offeredDraw ) { + char *p = NULL; + if((signed char)boards[forwardMostMove][EP_STATUS] == EP_RULE_DRAW) + p = "Draw claim: 50-move rule"; + if((signed char)boards[forwardMostMove][EP_STATUS] == EP_REP_DRAW) + p = "Draw claim: 3-fold repetition"; + if((signed char)boards[forwardMostMove][EP_STATUS] == EP_INSUF_DRAW) + p = "Draw claim: insufficient mating material"; + if( p != NULL && canAdjudicate) { + if(engineOpponent) { + SendToProgram("force\n", engineOpponent); // suppress reply + SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */ + } + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( GameIsDrawn, p, GE_XBOARD ); + return 1; + } + } + + if( canAdjudicate && appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) { + if(engineOpponent) { + SendToProgram("force\n", engineOpponent); // suppress reply + SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */ + } + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD ); + return 1; + } + return 0; +} + char *SendMoveToBookUser(int moveNr, ChessProgramState *cps, int initial) { // [HGM] book: this routine intercepts moves to simulate book replies char *bookHit = NULL; @@ -6081,7 +6820,7 @@ char *SendMoveToBookUser(int moveNr, ChessProgramState *cps, int initial) cps->bookSuspend = FALSE; // after a 'go' we are never suspended } else { // 'go' might be sent based on 'firstMove' after this routine returns if(cps->bookSuspend && !firstMove) // 'go' needed, and it will not be done after we return - SendToProgram("go\n", cps); + SendToProgram("go\n", cps); cps->bookSuspend = FALSE; // anyhow, we will not be suspended after a miss } return bookHit; // notify caller of hit, so it can take action to send move to opponent @@ -6233,6 +6972,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if (gameMode == TwoMachinesPlay) { GameEnds(machineWhite ? BlackWins : WhiteWins, buf1, GE_XBOARD); +<<<<<<< HEAD } return; } @@ -6487,8 +7227,13 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. reason = "Xboard adjudication: Checkmate"; boards[forwardMostMove][EP_STATUS] = (gameInfo.variant == VariantLosers ? EP_WINS : EP_CHECKMATE); break; +======= +>>>>>>> master } + return; + } +<<<<<<< HEAD switch(i = (signed char)boards[forwardMostMove][EP_STATUS]) { case EP_STALEMATE: result = GameIsDrawn; break; @@ -6512,20 +7257,64 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. (NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 || NrPieces == NrBB+NrWB+2 && bishopsColor != 3)) // [HGM] all Bishops (Ferz!) same color { /* KBK, KNK, KK of KBKB with like Bishops */ +======= + /* [HGM] Apparently legal, but so far only tested with EP_UNKOWN */ + /* So we have to redo legality test with true e.p. status here, */ + /* to make sure an illegal e.p. capture does not slip through, */ + /* to cause a forfeit on a justified illegal-move complaint */ + /* of the opponent. */ + if( gameMode==TwoMachinesPlay && appData.testLegality + && fromY != DROP_RANK /* [HGM] temporary; should still add legality test for drops */ + ) { + ChessMove moveType; + moveType = LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove), + fromY, fromX, toY, toX, promoChar); + if (appData.debugMode) { + int i; + for(i=0; i< nrCastlingRights; i++) fprintf(debugFP, "(%d,%d) ", + boards[forwardMostMove][CASTLING][i], castlingRank[i]); + fprintf(debugFP, "castling rights\n"); + } + if(moveType == IllegalMove) { + sprintf(buf1, "Xboard: Forfeit due to illegal move: %s (%c%c%c%c)%c", + machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0); + GameEnds(machineWhite ? BlackWins : WhiteWins, + buf1, GE_XBOARD); + return; + } else if(gameInfo.variant != VariantFischeRandom && gameInfo.variant != VariantCapaRandom) + /* [HGM] Kludge to handle engines that send FRC-style castling + when they shouldn't (like TSCP-Gothic) */ + switch(moveType) { + case WhiteASideCastleFR: + case BlackASideCastleFR: + toX+=2; + currentMoveString[2]++; + break; + case WhiteHSideCastleFR: + case BlackHSideCastleFR: + toX--; + currentMoveString[2]--; + break; + default: ; // nothing to do, but suppresses warning of pedantic compilers + } + } + hintRequested = FALSE; + lastHint[0] = NULLCHAR; + bookRequested = FALSE; + /* Program may be pondering now */ + cps->maybeThinking = TRUE; + if (cps->sendTime == 2) cps->sendTime = 1; + if (cps->offeredDraw) cps->offeredDraw--; +>>>>>>> master - /* always flag draws, for judging claims */ - boards[forwardMostMove][EP_STATUS] = EP_INSUF_DRAW; + /* currentMoveString is set as a side-effect of ParseOneMove */ + strcpy(machineMove, currentMoveString); + strcat(machineMove, "\n"); + strcpy(moveList[forwardMostMove], machineMove); - if(appData.materialDraws) { - /* but only adjudicate them if adjudication enabled */ - SendToProgram("force\n", cps->other); // suppress reply - SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see last move */ - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ - GameEnds( GameIsDrawn, "Xboard adjudication: Insufficient mating material", GE_XBOARD ); - return; - } - } + MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/ +<<<<<<< HEAD /* Then some trivial draws (only adjudicate, cannot be claimed) */ if(NrPieces == 4 && ( NrWR == 1 && NrBR == 1 /* KRKR */ @@ -6644,42 +7433,73 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( GameIsDrawn, "Xboard adjudication: 50-move rule", GE_XBOARD ); return; +======= + /* [AS] Adjudicate game if needed (note: remember that forwardMostMove now points past the last move) */ + if( gameMode == TwoMachinesPlay && adjudicateLossThreshold != 0 && forwardMostMove >= adjudicateLossPlies ) { + int count = 0; + + while( count < adjudicateLossPlies ) { + int score = pvInfoList[ forwardMostMove - count - 1 ].score; + + if( count & 1 ) { + score = -score; /* Flip score for winning side */ +>>>>>>> master } - /* if draw offer is pending, treat it as a draw claim - * when draw condition present, to allow engines a way to - * claim draws before making their move to avoid a race - * condition occurring after their move - */ - if( cps->other->offeredDraw || cps->offeredDraw ) { - char *p = NULL; - if((signed char)boards[forwardMostMove][EP_STATUS] == EP_RULE_DRAW) - p = "Draw claim: 50-move rule"; - if((signed char)boards[forwardMostMove][EP_STATUS] == EP_REP_DRAW) - p = "Draw claim: 3-fold repetition"; - if((signed char)boards[forwardMostMove][EP_STATUS] == EP_INSUF_DRAW) - p = "Draw claim: insufficient mating material"; - if( p != NULL ) { - SendToProgram("force\n", cps->other); // suppress reply - SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */ - GameEnds( GameIsDrawn, p, GE_XBOARD ); - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ - return; - } + if( score > adjudicateLossThreshold ) { + break; } + count++; + } - if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) { - SendToProgram("force\n", cps->other); // suppress reply - SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */ - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + if( count >= adjudicateLossPlies ) { + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ - GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD ); + GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, + "Xboard adjudication", + GE_XBOARD ); - return; - } + return; + } } + if(Adjudicate(cps)) return; // [HGM] adjudicate: for all automatic game ends + +#if ZIPPY + if ((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) && + first.initDone) { + if(cps->offeredDraw && (signed char)boards[forwardMostMove][EP_STATUS] <= EP_DRAWS) { + SendToICS(ics_prefix); // [HGM] drawclaim: send caim and move on one line for FICS + SendToICS("draw "); + SendMoveToICS(moveType, fromX, fromY, toX, toY); + } + SendMoveToICS(moveType, fromX, fromY, toX, toY); + ics_user_moved = 1; + if(appData.autoKibitz && !appData.icsEngineAnalyze ) { /* [HGM] kibitz: send most-recent PV info to ICS */ + char buf[3*MSG_SIZ]; + + sprintf(buf, "kibitz !!! %+.2f/%d (%.2f sec, %u nodes, %.0f knps) PV=%s\n", + programStats.score / 100., + programStats.depth, + programStats.time / 100., + (unsigned int)programStats.nodes, + (unsigned int)programStats.nodes / (10*abs(programStats.time) + 1.), + programStats.movelist); + SendToICS(buf); +if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.nodes, programStats.nodes); + } + } +#endif + + /* [AS] Save move info and clear stats for next move */ + pvInfoList[ forwardMostMove-1 ].score = programStats.score; + pvInfoList[ forwardMostMove-1 ].depth = programStats.depth; + pvInfoList[ forwardMostMove-1 ].time = programStats.time; // [HGM] PGNtime: take time from engine stats + ClearProgramStats(); + thinkOutput[0] = NULLCHAR; + hiddenThinkOutputState = 0; + bookHit = NULL; if (gameMode == TwoMachinesPlay) { /* [HGM] relaying draw offers moved to after reception of move */ @@ -11722,13 +12542,13 @@ EditPositionMenuEvent(selection, x, y) int n; if(x == BOARD_LEFT-2 && selection >= BlackPawn) { n = PieceToNumber(selection - BlackPawn); - if(n > gameInfo.holdingsSize) { n = 0; selection = BlackPawn; } + if(n >= gameInfo.holdingsSize) { n = 0; selection = BlackPawn; } boards[0][BOARD_HEIGHT-1-n][0] = selection; boards[0][BOARD_HEIGHT-1-n][1]++; } else if(x == BOARD_RGHT+1 && selection < BlackPawn) { n = PieceToNumber(selection); - if(n > gameInfo.holdingsSize) { n = 0; selection = WhitePawn; } + if(n >= gameInfo.holdingsSize) { n = 0; selection = WhitePawn; } boards[0][n][BOARD_WIDTH-1] = selection; boards[0][n][BOARD_WIDTH-2]++; } @@ -11895,6 +12715,7 @@ DrawEvent() SendToICS(ics_prefix); SendToICS("draw\n"); + userOfferedDraw = TRUE; // [HGM] drawclaim: also set flag in ICS play } else if (cmailMsgLoaded) { if (currentMove == cmailOldMove && commentList[cmailOldMove] != NULL && diff --git a/backend.h b/backend.h index 18f5c58..e2b967b 100644 --- a/backend.h +++ b/backend.h @@ -70,11 +70,17 @@ #else /* place holder * or dummy types for other compiler + * [HGM] seems that -mno-cygwin comple needs %I64? */ #define u64 unsigned long long #define s64 signed long long - #define u64Display "%llu" - #define s64Display "%lld" + #ifdef USE_I64 + #define u64Display "%I64u" + #define s64Display "%I64d" + #else + #define u64Display "%llu" + #define s64Display "%lld" + #endif #define u64Const(c) (c ## ULL) #define s64Const(c) (c ## LL) #endif @@ -121,6 +127,8 @@ int PieceForSquare P((int x, int y)); int OKToStartUserMove P((int x, int y)); void Reset P((int redraw, int init)); void ResetGameEvent P((void)); +Boolean HasPattern P(( const char * text, const char * pattern )); +Boolean SearchPattern P(( const char * text, const char * pattern )); int LoadGame P((FILE *f, int n, char *title, int useList)); int LoadGameFromFile P((char *filename, int n, char *title, int useList)); int CmailLoadGame P((FILE *f, int n, char *title, int useList)); @@ -130,6 +138,8 @@ int SaveGameToFile P((char *filename, int append)); int LoadPosition P((FILE *f, int n, char *title)); int ReloadPosition P((int offset)); int SavePosition P((FILE *f, int dummy, char *dummy2)); +int DrawSeekGraph P(()); +int SeekGraphClick P((ClickType click, int x, int y, int moving)); void EditPositionEvent P((void)); void FlipViewEvent P((void)); void MachineWhiteEvent P((void)); @@ -262,6 +272,8 @@ int GameListBuild P((FILE *)); void GameListInitGameInfo P((GameInfo *)); char *GameListLine P((int, GameInfo *)); char * GameListLineFull P(( int, GameInfo *)); +void GLT_TagsToList P(( char * tags )); +void GLT_ParseList P((void)); extern char* StripHighlight P((char *)); /* returns static data */ extern char* StripHighlightAndTitle P((char *)); /* returns static data */ diff --git a/common.h b/common.h index b6b727f..a5577e2 100644 --- a/common.h +++ b/common.h @@ -413,6 +413,8 @@ typedef struct { char *icsHelper; Boolean icsInputBox; Boolean useTelnet; + Boolean seekGraph; + Boolean autoRefresh; char *telnetProgram; char *gateway; char *loadGameFile; @@ -446,6 +448,7 @@ typedef struct { char *cmailGameName; /* xboard only */ Boolean alwaysPromoteToQueen; Boolean oldSaveStyle; + Boolean oneClick; Boolean quietPlay; Boolean showThinking; Boolean ponderNextMove; diff --git a/configure.ac b/configure.ac index e35bd66..a6d0664 100644 --- a/configure.ac +++ b/configure.ac @@ -29,6 +29,7 @@ dnl| the standard version of xboard. dnl| define second argument as VERSION.PATCHLEVEL. e.g. 4.4.0j AC_INIT([xboard],[gtk-20100118],[bug-xboard@gnu.org]) + AM_INIT_AUTOMAKE AC_CONFIG_HEADERS([config.h]) @@ -432,6 +433,9 @@ echo "" echo " Configurations summary:" echo "" echo " prefix: $prefix" +echo " datarootdir: $datarootdir" +echo " infodir: $infodir" +echo " sysconfdir: $sysconfdir" echo "" echo " Xaw3d: $with_xaw3d" echo "" diff --git a/frontend.h b/frontend.h index 81d7abc..7912f9d 100644 --- a/frontend.h +++ b/frontend.h @@ -91,6 +91,10 @@ void CommentPopDown P((void)); void EditCommentPopUp P((int index, String title, String text)); void ErrorPopDown P((void)); int EventToSquare P((int x, int limit)); +void DrawSeekAxis P(( int x, int y, int xTo, int yTo )); +void DrawSeekBackground P(( int left, int top, int right, int bottom )); +void DrawSeekText P((char *buf, int x, int y)); +void DrawSeekDot P((int x, int y, int color)); void RingBell P((void)); void PlayIcsWinSound P((void)); @@ -128,6 +132,7 @@ void PromotionPopUp P((void)); void DragPieceBegin P((int x, int y)); void DragPieceEnd P((int x, int y)); void LeftClick P((ClickType c, int x, int y)); +int RightClick P((ClickType c, int x, int y, int *col, int *row)); int StartChildProcess P((char *cmdLine, char *dir, ProcRef *pr)); void DestroyChildProcess P((ProcRef pr, int/*boolean*/ signal)); @@ -156,6 +161,14 @@ void CmailSigHandlerCallBack P((InputSourceRef isr, VOIDSTAR closure, extern ProcRef cmailPR; +/* in xgamelist.c or winboard.c */ +void GLT_ClearList(); +void GLT_DeSelectList(); +void GLT_AddToList( char *name ); +Boolean GLT_GetFromList( int index, char *name ); + +extern char lpUserGLT[]; + /* these are in wgamelist.c */ void GameListPopUp P((FILE *fp, char *filename)); void GameListPopDown P((void)); diff --git a/gamelist.c b/gamelist.c index da123a4..aae489a 100644 --- a/gamelist.c +++ b/gamelist.c @@ -56,6 +56,64 @@ static ListGame *GameListCreate P((void)); static void GameListFree P((List *)); static int GameListNewGame P((ListGame **)); +/* [AS] Wildcard pattern matching */ +Boolean +HasPattern( const char * text, const char * pattern ) +{ + while( *pattern != '\0' ) { + if( *pattern == '*' ) { + while( *pattern == '*' ) { + pattern++; + } + + if( *pattern == '\0' ) { + return TRUE; + } + + while( *text != '\0' ) { + if( HasPattern( text, pattern ) ) { + return TRUE; + } + text++; + } + } + else if( (*pattern == *text) || ((*pattern == '?') && (*text != '\0')) ) { + pattern++; + text++; + continue; + } + + return FALSE; + } + + return TRUE; +} + +Boolean +SearchPattern( const char * text, const char * pattern ) +{ + Boolean result = TRUE; + + if( pattern != NULL && *pattern != '\0' ) { + if( *pattern == '*' ) { + result = HasPattern( text, pattern ); + } + else { + result = FALSE; + + while( *text != '\0' ) { + if( HasPattern( text, pattern ) ) { + result = TRUE; + break; + } + text++; + } + } + } + + return result; +} + /* Delete a ListGame; implies removint it from a list. */ static void GameListDeleteGame(listGame) @@ -438,3 +496,115 @@ char * GameListLineFull( int number, GameInfo * gameInfo ) return ret; } + +// --------------------------------------- Game-List options dialog -------------------------------------- + +// back-end +typedef struct { + char id; + char * name; +} GLT_Item; + +// back-end: translation table tag id-char <-> full tag name +static GLT_Item GLT_ItemInfo[] = { + { GLT_EVENT, "Event" }, + { GLT_SITE, "Site" }, + { GLT_DATE, "Date" }, + { GLT_ROUND, "Round" }, + { GLT_PLAYERS, "Players" }, + { GLT_RESULT, "Result" }, + { GLT_WHITE_ELO, "White Rating" }, + { GLT_BLACK_ELO, "Black Rating" }, + { GLT_TIME_CONTROL,"Time Control" }, + { GLT_VARIANT, "Variant" }, + { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK }, + { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom + { 0, 0 } +}; + +char lpUserGLT[64]; + +// back-end: convert the tag id-char to a full tag name +char * GLT_FindItem( char id ) +{ + char * result = 0; + + GLT_Item * list = GLT_ItemInfo; + + while( list->id != 0 ) { + if( list->id == id ) { + result = list->name; + break; + } + + list++; + } + + return result; +} + +// back-end: build the list of tag names +void +GLT_TagsToList( char * tags ) +{ + char * pc = tags; + + GLT_ClearList(); + + while( *pc ) { + GLT_AddToList( GLT_FindItem(*pc) ); + pc++; + } + + GLT_AddToList( " --- Hidden tags --- " ); + + pc = GLT_ALL_TAGS; + + while( *pc ) { + if( strchr( tags, *pc ) == 0 ) { + GLT_AddToList( GLT_FindItem(*pc) ); + } + pc++; + } + + GLT_DeSelectList(); +} + +// back-end: retrieve item from dialog and translate to id-char +char +GLT_ListItemToTag( int index ) +{ + char result = '\0'; + char name[128]; + + GLT_Item * list = GLT_ItemInfo; + + if( GLT_GetFromList(index, name) ) { + while( list->id != 0 ) { + if( strcmp( list->name, name ) == 0 ) { + result = list->id; + break; + } + + list++; + } + } + + return result; +} + +// back-end: add items id-chars one-by-one to temp tags string +void +GLT_ParseList() +{ + char * pc = lpUserGLT; + int idx = 0; + char id; + + do { + id = GLT_ListItemToTag( idx ); + *pc++ = id; + idx++; + } while( id != '\0' ); +} + diff --git a/history.c b/history.c new file mode 100644 index 0000000..4598799 --- /dev/null +++ b/history.c @@ -0,0 +1,246 @@ +/* + * Move history for WinBoard + * + * Author: Alessandro Scotti (Dec 2005) + * back-end part split off by HGM + * + * Copyright 2005 Alessandro Scotti + * + * ------------------------------------------------------------------------ + * + * 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 +#include +#include +#include + +#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 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 && + TRUE ) + { + 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) && + TRUE ) + { + 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 */ + strcpy( buf, SavePart( currMovelist[index] ) ); + 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= 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; + + DoHighlight( lastCurrent, FALSE ); + DoHighlight( currCurrent, TRUE ); + + lastFirst = currFirst; + lastLast = currLast; + lastCurrent = currCurrent; + lastLastMove[0] = '\0'; + + if( lastLast > 0 ) { + strcpy( lastLastMove, SavePart( currMovelist[lastLast-1] ) ); + } + + /* Deselect any text, move caret to end of memo */ + if( currCurrent >= 0 ) { + caretPos = histMoves[currCurrent].memoOffset + histMoves[currCurrent].memoLength; + } + else { + caretPos = -1; + } + + ScrollToCurrent(caretPos); +} + +// 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= 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 +} + diff --git a/winboard/config.h b/winboard/config.h index f0e6b33..d39a80e 100644 --- a/winboard/config.h +++ b/winboard/config.h @@ -46,16 +46,16 @@ #define PACKAGE_NAME "WinBoard" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "WinBoard master-20091122" +#define PACKAGE_STRING "WinBoard master-20100118" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "winboard" /* Define to the version of this package. */ -#define PACKAGE_VERSION "master-20091122" +#define PACKAGE_VERSION "master-20100118" /* Define the Windows-specific FILE version info. this *MUST* be four comma separated 16-bit integers */ -#define PACKAGE_FILEVERSION 4,4,2,0 +#define PACKAGE_FILEVERSION 4,4,2,1 #define PTY_ITERATION diff --git a/winboard/makefile.gcc b/winboard/makefile.gcc index 6bb84c8..db65a67 100644 --- a/winboard/makefile.gcc +++ b/winboard/makefile.gcc @@ -6,7 +6,7 @@ PROJ=winboard OBJS=backend.o book.o gamelist.o lists.o moves.o pgntags.o uci.o zippy.o\ parser.o wbres.o wclipbrd.o wedittags.o wengineoutput.o wevalgraph.o\ - wgamelist.o whistory.o winboard.o wlayout.o woptions.o wsnap.o\ + wgamelist.o whistory.o history.o winboard.o wlayout.o woptions.o wsnap.o\ wsockerr.o help.o wsettings.o wchat.o engineoutput.o evalgraph.o @@ -28,7 +28,7 @@ USE_MINGW=1 # set up for cygwin or not ifeq ($(USE_MINGW),1) -CFCYG = -mno-cygwin +CFCYG = -mno-cygwin -DUSE_I64 LFCYG = -mno-cygwin -lmsvcrt endif @@ -117,7 +117,7 @@ wedittags.o: wedittags.c config.h ../common.h winboard.h resource.h ../frontend. ../backend.h ../lists.h $(call compile, $<) -wgamelist.o: wgamelist.c config.h. ../common.h winboard.h resource.h ../frontend.h \ +wgamelist.o: wgamelist.c config.h ../common.h winboard.h resource.h ../frontend.h \ ../backend.h ../lists.h $(call compile, $<) @@ -137,6 +137,10 @@ whistory.o: whistory.c config.h ../common.h ../frontend.h ../backend.h \ ../lists.h winboard.h resource.h wsnap.h $(call compile, $<) +history.o: ../history.c config.h ../common.h ../frontend.h ../backend.h \ + ../lists.h + $(call compile, $<) + wevalgraph.o: wevalgraph.c ../evalgraph.h config.h ../common.h ../frontend.h \ ../backend.h ../lists.h winboard.h resource.h wsnap.h $(call compile, $<) diff --git a/winboard/makefile.ms b/winboard/makefile.ms index ca127b5..f577381 100644 --- a/winboard/makefile.ms +++ b/winboard/makefile.ms @@ -14,7 +14,7 @@ PROJ = winboard OBJS=backend.obj book.obj gamelist.obj lists.obj moves.obj pgntags.obj uci.obj\ zippy.obj parser.obj wclipbrd.obj wedittags.obj wengineoutput.obj wevalgraph.obj\ - wgamelist.obj whistory.obj winboard.obj wlayout.obj woptions.obj wsnap.obj\ + wgamelist.obj whistory.obj history.obj winboard.obj wlayout.obj woptions.obj wsnap.obj\ wsockerr.obj help.obj wsettings.obj wchat.obj engineoutput.obj evalgraph.obj @@ -125,7 +125,7 @@ wedittags.obj: wedittags.c config.h ../common.h winboard.h resource.h ../fronten ../backend.h ../lists.h $(CC) $(CFLAGS) wedittags.c -wgamelist.obj: wgamelist.c config.h. ../common.h winboard.h resource.h ../frontend.h \ +wgamelist.obj: wgamelist.c config.h ../common.h winboard.h resource.h ../frontend.h \ ../backend.h ../lists.h $(CC) $(CFLAGS) wgamelist.c @@ -145,6 +145,10 @@ whistory.obj: whistory.c config.h ../common.h ../frontend.h ../backend.h \ ../lists.h winboard.h resource.h wsnap.h $(CC) $(CFLAGS) whistory.c +history.obj: ../history.c config.h ../common.h ../frontend.h ../backend.h \ + ../lists.h + $(CC) $(CFLAGS) whistory.c + wevalgraph.obj: wevalgraph.c config.h ../common.h ../frontend.h ../backend.h \ ../lists.h winboard.h resource.h wsnap.h $(CC) $(CFLAGS) wevalgraph.c diff --git a/winboard/wchat.c b/winboard/wchat.c index 363bebd..48d555a 100644 --- a/winboard/wchat.c +++ b/winboard/wchat.c @@ -136,6 +136,7 @@ LRESULT CALLBACK ChatProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam static SnapData sd; char buf[MSG_SIZ], mess[MSG_SIZ]; int partner = -1, i; + static BOOL filterHasFocus[MAX_CHAT]; for(i=0; i is pressed while editing the filter, it's better to apply + the filter rather than selecting the current game. + */ + if( LOWORD(wParam) == IDC_ChatPartner ) { + switch( HIWORD(wParam) ) { + case EN_SETFOCUS: + filterHasFocus[partner] = TRUE; + break; + case EN_KILLFOCUS: + filterHasFocus[partner] = FALSE; + break; + } + } + + if( filterHasFocus[partner] && (LOWORD(wParam) == IDC_Send) ) { + SetFocus(GetDlgItem(hDlg, OPT_ChatInput)); + wParam = IDC_Change; + } + /* [AS] End command replacement */ + switch (LOWORD(wParam)) { case IDCANCEL: diff --git a/winboard/wgamelist.c b/winboard/wgamelist.c index 31d36ff..c9ae1b5 100644 --- a/winboard/wgamelist.c +++ b/winboard/wgamelist.c @@ -54,62 +54,6 @@ struct GameListStats int unfinished; }; -/* [AS] Wildcard pattern matching */ -static BOOL HasPattern( const char * text, const char * pattern ) -{ - while( *pattern != '\0' ) { - if( *pattern == '*' ) { - while( *pattern == '*' ) { - pattern++; - } - - if( *pattern == '\0' ) { - return TRUE; - } - - while( *text != '\0' ) { - if( HasPattern( text, pattern ) ) { - return TRUE; - } - text++; - } - } - else if( (*pattern == *text) || ((*pattern == '?') && (*text != '\0')) ) { - pattern++; - text++; - continue; - } - - return FALSE; - } - - return TRUE; -} - -static BOOL SearchPattern( const char * text, const char * pattern ) -{ - BOOL result = TRUE; - - if( pattern != NULL && *pattern != '\0' ) { - if( *pattern == '*' ) { - result = HasPattern( text, pattern ); - } - else { - result = FALSE; - - while( *text != '\0' ) { - if( HasPattern( text, pattern ) ) { - result = TRUE; - break; - } - text++; - } - } - } - - return result; -} - /* [AS] Setup the game list according to the specified filter */ static int GameListToListBox( HWND hDlg, BOOL boReset, char * pszFilter, struct GameListStats * stats ) { diff --git a/winboard/whistory.c b/winboard/whistory.c index 0a42f18..854557a 100644 --- a/winboard/whistory.c +++ b/winboard/whistory.c @@ -2,6 +2,7 @@ * Move history for WinBoard * * Author: Alessandro Scotti (Dec 2005) + * front-end code split off by HGM * * Copyright 2005 Alessandro Scotti * @@ -25,11 +26,11 @@ #include "config.h" -#include /* required for all Windows applications */ -#include #include #include #include +#include /* required for all Windows applications */ +#include #include #include @@ -37,51 +38,30 @@ #include "frontend.h" #include "backend.h" #include "winboard.h" - #include "wsnap.h" -/* Module globals */ -typedef char MoveHistoryString[ MOVE_LEN*2 ]; -static BOOLEAN moveHistoryDialogUp = FALSE; - -static int lastFirst = 0; -static int lastLast = 0; -static int lastCurrent = -1; - -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 ]; - -#define WM_REFRESH_HISTORY (WM_USER+4657) +// templates for calls into back-end +void RefreshMemoContent P((void)); +void MemoContentUpdated P((void)); +void FindMoveByCharIndex P(( int char_index )); #define DEFAULT_COLOR 0xFFFFFFFF #define H_MARGIN 2 #define V_MARGIN 2 -/* Note: in the following code a "Memo" is a Rich Edit control (it's Delphi lingo) */ +static BOOLEAN moveHistoryDialogUp = FALSE; -static VOID HighlightMove( int index, BOOL highlight ) +// ------------- low-level front-end actions called by MoveHistory back-end ----------------- + +// low-level front-end, after calculating from & to is left to caller +// it task is to highlight the indicated characters. (In WinBoard it makes them bold and blue.) +void HighlightMove( int from, int to, Boolean highlight ) { - if( index >= 0 && index < MAX_MOVES ) { CHARFORMAT cf; HWND hMemo = GetDlgItem( moveHistoryDialog, IDC_MoveHistory ); - SendMessage( hMemo, - EM_SETSEL, - histMoves[index].memoOffset, - histMoves[index].memoOffset + histMoves[index].memoLength ); + SendMessage( hMemo, EM_SETSEL, from, to); /* Set style */ @@ -99,60 +79,24 @@ static VOID HighlightMove( int index, BOOL highlight ) } SendMessage( hMemo, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf ); - } -} - -static BOOL OnlyCurrentPositionChanged() -{ - BOOL result = FALSE; - - if( lastFirst >= 0 && - lastLast >= lastFirst && - lastCurrent >= lastFirst && - currFirst == lastFirst && - currLast == lastLast && - currCurrent >= 0 && - TRUE ) - { - result = TRUE; - - /* Special case: last move changed */ - if( currCurrent == currLast-1 ) { - if( strcmp( currMovelist[currCurrent], lastLastMove ) != 0 ) { - result = FALSE; - } - } - } - - return result; -} - -static BOOL OneMoveAppended() -{ - BOOL 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) && - TRUE ) - { - result = TRUE; - } - - return result; } -static VOID ClearMemo() +// low-level front-end, but replace Windows data types to make it callable from back-end +// its task is to clear the contents of the move-history text edit +void ClearHistoryMemo() { SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, WM_SETTEXT, 0, (LPARAM) "" ); } -static int AppendToMemo( char * text, DWORD flags, DWORD color ) +// low-level front-end, made callable from back-end by passing flags and color numbers +// its task is to append the given text to the text edit +// the bold argument says 0 = normal, 1 = bold typeface +// the colorNr argument says 0 = font-default, 1 = gray +int AppendToHistoryMemo( char * text, int bold, int colorNr ) { CHARFORMAT cf; + DWORD flags = bold ? CFE_BOLD :0; + DWORD color = colorNr ? GetSysColor(COLOR_GRAYTEXT) : DEFAULT_COLOR; HWND hMemo = GetDlgItem( moveHistoryDialog, IDC_MoveHistory ); @@ -184,98 +128,21 @@ static int AppendToMemo( char * text, DWORD flags, DWORD color ) return cbTextLen; } -static VOID AppendMoveToMemo( int index ) -{ - char buf[64]; - DWORD flags = 0; - DWORD color = DEFAULT_COLOR; - - 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 ? ".." : "" ); - AppendToMemo( buf, CFE_BOLD, DEFAULT_COLOR ); - } - - /* Move text */ - strcpy( buf, SavePart( currMovelist[index] ) ); - strcat( buf, " " ); - - histMoves[index].memoOffset = AppendToMemo( buf, flags, color ); - 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 ); - - AppendToMemo( buf, flags, - color == DEFAULT_COLOR ? GetSysColor(COLOR_GRAYTEXT) : color ); - } -} - -static void RefreshMemoContent() +// low-level front-end; wrapper for the code to scroll the mentioned character in view (-1 = end) +void ScrollToCurrent(int caretPos) { - int i; - - ClearMemo(); - - for( i=currFirst; i 0 ) { - strcpy( lastLastMove, SavePart( currMovelist[lastLast-1] ) ); - } - - /* Deselect any text, move caret to end of memo */ - if( currCurrent >= 0 ) { - caretPos = histMoves[currCurrent].memoOffset + histMoves[currCurrent].memoLength; - } - else { + if(caretPos < 0) caretPos = (int) SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, WM_GETTEXTLENGTH, 0, 0 ); - } - SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SETSEL, caretPos, caretPos ); SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SCROLLCARET, 0, 0 ); } -int FindMoveByCharIndex( int char_index ) -{ - int index; - - for( index=currFirst; index= histMoves[index].memoOffset && - char_index < (histMoves[index].memoOffset + histMoves[index].memoLength) ) - { - return index; - } - } - return -1; -} +// ------------------------------ call backs -------------------------- +// front-end. Universal call-back for any event. Recognized vents are dialog creation, OK and cancel button-press +// (dead code, as these buttons do not exist?), mouse clicks on the text edit, and moving / sizing LRESULT CALLBACK HistoryDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { static SnapData sd; @@ -333,11 +200,7 @@ LRESULT CALLBACK HistoryDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPAR index = SendDlgItemMessage( hDlg, IDC_MoveHistory, EM_CHARFROMPOS, 0, (LPARAM) &pt ); - index = FindMoveByCharIndex( index ); - - if( index >= 0 ) { - ToNrEvent( index + 1 ); - } + FindMoveByCharIndex( index ); // [HGM] also does the actual moving to it, now /* Zap the message for good: apparently, returning non-zero is not enough */ lpMF->msg = WM_USER; @@ -347,22 +210,6 @@ LRESULT CALLBACK HistoryDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPAR } break; - case WM_REFRESH_HISTORY: - /* Update the GUI */ - if( OnlyCurrentPositionChanged() ) { - /* Only "cursor" changed, no need to update memo content */ - } - else if( OneMoveAppended() ) { - AppendMoveToMemo( currCurrent ); - } - else { - RefreshMemoContent(); - } - - MemoContentUpdated(); - - break; - case WM_SIZE: SetWindowPos( GetDlgItem( moveHistoryDialog, IDC_MoveHistory ), HWND_TOP, @@ -401,6 +248,9 @@ LRESULT CALLBACK HistoryDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPAR return FALSE; } +// ------------ standard entry points into MoveHistory code ----------- + +// front-end VOID MoveHistoryPopUp() { FARPROC lpProc; @@ -424,8 +274,13 @@ VOID MoveHistoryPopUp() } moveHistoryDialogUp = TRUE; + +// Note that in WIndows creating the dialog causes its call-back to perform +// RefreshMemoContent() and MemoContentUpdated() immediately after it is realized. +// To port this to X we might have to do that from here. } +// front-end VOID MoveHistoryPopDown() { CheckMenuItem(GetMenu(hwndMain), IDM_ShowMoveHistory, MF_UNCHECKED); @@ -437,22 +292,14 @@ VOID MoveHistoryPopDown() moveHistoryDialogUp = FALSE; } -VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo ) +// front-end +Boolean MoveHistoryIsUp() { - /* [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( moveHistoryDialog ) { - SendMessage( moveHistoryDialog, WM_REFRESH_HISTORY, 0, 0 ); - } + return moveHistoryDialogUp; } -Boolean MoveHistoryIsUp() +// front-end +Boolean MoveHistoryDialogExists() { - return moveHistoryDialogUp; + return moveHistoryDialog != NULL; } diff --git a/winboard/winboard.c b/winboard/winboard.c index aa13836..fae7a9d 100644 --- a/winboard/winboard.c +++ b/winboard/winboard.c @@ -159,7 +159,7 @@ BoardSize boardSize; Boolean chessProgram; //static int boardX, boardY; int minX, minY; // [HGM] placement: volatile limits on upper-left corner -static int squareSize, lineGap, minorSize; +int squareSize, lineGap, minorSize; static int winW, winH; static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo static int logoHeight = 0; @@ -2005,6 +2005,8 @@ InitDrawingSizes(BoardSize boardSize, int flags) blackRect.right = blackRect.left + boardWidth/2 - 1; blackRect.top = whiteRect.top; blackRect.bottom = whiteRect.bottom; + + logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout! } messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN; @@ -3082,6 +3084,62 @@ DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo) DeleteDC(tmphdc); } +static HDC hdcSeek; + +// [HGM] seekgraph +void DrawSeekAxis( int x, int y, int xTo, int yTo ) +{ + POINT stPt; + HPEN hp = SelectObject( hdcSeek, gridPen ); + MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt ); + LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo ); + SelectObject( hdcSeek, hp ); +} + +// front-end wrapper for drawing functions to do rectangles +void DrawSeekBackground( int left, int top, int right, int bottom ) +{ + HPEN hp; + RECT rc; + + if (hdcSeek == NULL) { + hdcSeek = GetDC(hwndMain); + if (!appData.monoMode) { + SelectPalette(hdcSeek, hPal, FALSE); + RealizePalette(hdcSeek); + } + } + hp = SelectObject( hdcSeek, gridPen ); + rc.top = boardRect.top+top; rc.left = boardRect.left+left; + rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right; + FillRect( hdcSeek, &rc, lightSquareBrush ); + SelectObject( hdcSeek, hp ); +} + +// front-end wrapper for putting text in graph +void DrawSeekText(char *buf, int x, int y) +{ + SIZE stSize; + SetBkMode( hdcSeek, TRANSPARENT ); + GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize ); + TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) ); +} + +void DrawSeekDot(int x, int y, int color) +{ + int square = color & 0x80; + color &= 0x7F; + HBRUSH oldBrush = SelectObject(hdcSeek, + color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush); + if(square) + Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9, + boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9); + else + Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8, + boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8); + SelectObject(hdcSeek, oldBrush); +} + VOID HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) { @@ -3108,6 +3166,8 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) */ Boolean fullrepaint = repaint; + if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up + if( DrawPositionNeedsFullRepaint() ) { fullrepaint = TRUE; } @@ -3652,7 +3712,7 @@ void DragPieceEnd(int x, int y) VOID MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { - int x, y; + int x, y, menuNr; POINT pt; static int recursive = 0; HMENU hmenu; @@ -3719,7 +3779,8 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) break; case WM_MOUSEMOVE: - MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top); + if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break; + MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top); if ((appData.animateDragging || appData.highlightDragging) && (wParam & MK_LBUTTON) && dragInfo.from.x >= 0) @@ -3757,12 +3818,12 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } break; - case WM_MBUTTONUP: - case WM_RBUTTONUP: - ReleaseCapture(); - UnLoadPV(); - break; - + case WM_MBUTTONUP: + case WM_RBUTTONUP: + ReleaseCapture(); + RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY); + break; + case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: ErrorPopDown(); @@ -3785,14 +3846,9 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } DrawPosition(TRUE, NULL); - switch (gameMode) { - case IcsExamining: - if(x < BOARD_LEFT || x >= BOARD_RGHT) break; - case EditPosition: - if (x == BOARD_LEFT-1 || x == BOARD_RGHT) break; - if (x < 0 || y < 0) break; - fromX = x; - fromY = y; + menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY); + switch (menuNr) { + case 0: if (message == WM_MBUTTONDOWN) { buttonCount = 3; /* even if system didn't think so */ if (wParam & MK_SHIFT) @@ -3809,35 +3865,13 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1); } break; - case IcsObserving: - if(!appData.icsEngineAnalyze) break; - case IcsPlayingWhite: - case IcsPlayingBlack: - if(!appData.zippyPlay) goto noZip; - case MachinePlaysWhite: - case MachinePlaysBlack: - case TwoMachinesPlay: - case AnalyzeMode: - case AnalyzeFile: - if (!appData.dropMenu) { - SetCapture(hwndMain); - LoadPV(pt.x - boardRect.left, pt.y - boardRect.top); - break; - } - if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode || - gameMode == AnalyzeFile || gameMode == IcsObserving) break; - case EditGame: - noZip: - if (x < 0 || y < 0) break; - if (!appData.dropMenu || appData.testLegality && - gameInfo.variant != VariantBughouse && - gameInfo.variant != VariantCrazyhouse) break; - fromX = x; - fromY = y; + case 2: + SetCapture(hwndMain); + break; + case 1: hmenu = LoadMenu(hInst, "DropPieceMenu"); SetupDropMenu(hmenu); MenuPopup(hwnd, pt, hmenu, -1); - break; default: break; } @@ -7868,104 +7902,42 @@ int NewGameFRC() return result; } -/* [AS] Game list options */ -typedef struct { - char id; - char * name; -} GLT_Item; - -static GLT_Item GLT_ItemInfo[] = { - { GLT_EVENT, "Event" }, - { GLT_SITE, "Site" }, - { GLT_DATE, "Date" }, - { GLT_ROUND, "Round" }, - { GLT_PLAYERS, "Players" }, - { GLT_RESULT, "Result" }, - { GLT_WHITE_ELO, "White Rating" }, - { GLT_BLACK_ELO, "Black Rating" }, - { GLT_TIME_CONTROL,"Time Control" }, - { GLT_VARIANT, "Variant" }, - { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK }, - { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom - { 0, 0 } -}; +/* [AS] Game list options. Refactored by HGM */ -const char * GLT_FindItem( char id ) -{ - const char * result = 0; - - GLT_Item * list = GLT_ItemInfo; - - while( list->id != 0 ) { - if( list->id == id ) { - result = list->name; - break; - } +HWND gameListOptionsDialog; - list++; - } - - return result; +// low-level front-end: clear text edit / list widget +void +GLT_ClearList() +{ + SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 ); } -void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index ) +// low-level front-end: clear text edit / list widget +void +GLT_DeSelectList() { - const char * name = GLT_FindItem( id ); - - if( name != 0 ) { - if( index >= 0 ) { - SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name ); - } - else { - SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name ); - } - } + SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 ); } -void GLT_TagsToList( HWND hDlg, char * tags ) +// low-level front-end: append line to text edit / list widget +void +GLT_AddToList( char *name ) { - char * pc = tags; - - SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 ); - - while( *pc ) { - GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 ); - pc++; - } - - SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" ); - - pc = GLT_ALL_TAGS; - - while( *pc ) { - if( strchr( tags, *pc ) == 0 ) { - GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 ); - } - pc++; + if( name != 0 ) { + SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name ); } - - SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 ); } -char GLT_ListItemToTag( HWND hDlg, int index ) +// low-level front-end: get line from text edit / list widget +Boolean +GLT_GetFromList( int index, char *name ) { - char result = '\0'; - char name[128]; - - GLT_Item * list = GLT_ItemInfo; - - if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) { - while( list->id != 0 ) { - if( strcmp( list->name, name ) == 0 ) { - result = list->id; - break; - } - - list++; - } + if( name != 0 ) { + if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) + return TRUE; } - - return result; + return FALSE; } void GLT_MoveSelection( HWND hDlg, int delta ) @@ -7986,20 +7958,15 @@ void GLT_MoveSelection( HWND hDlg, int delta ) LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { - static char glt[64]; - static char * lpUserGLT; - switch( message ) { case WM_INITDIALOG: - lpUserGLT = (char *) lParam; + gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end - strcpy( glt, lpUserGLT ); - CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER)); /* Initialize list */ - GLT_TagsToList( hDlg, glt ); + GLT_TagsToList( lpUserGLT ); SetFocus( GetDlgItem(hDlg, IDC_GameListTags) ); @@ -8008,19 +7975,7 @@ LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LP case WM_COMMAND: switch( LOWORD(wParam) ) { case IDOK: - { - char * pc = lpUserGLT; - int idx = 0; -// int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 ); - char id; - - do { - id = GLT_ListItemToTag( hDlg, idx ); - - *pc++ = id; - idx++; - } while( id != '\0' ); - } + GLT_ParseList(); EndDialog( hDlg, 0 ); return TRUE; case IDCANCEL: @@ -8028,13 +7983,11 @@ LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LP return TRUE; case IDC_GLT_Default: - strcpy( glt, GLT_DEFAULT_TAGS ); - GLT_TagsToList( hDlg, glt ); + GLT_TagsToList( GLT_DEFAULT_TAGS ); return TRUE; case IDC_GLT_Restore: - strcpy( glt, lpUserGLT ); - GLT_TagsToList( hDlg, glt ); + GLT_TagsToList( appData.gameListTags ); return TRUE; case IDC_GLT_Up: @@ -8054,23 +8007,21 @@ LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LP int GameListOptions() { - char glt[64]; int result; FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst ); - strcpy( glt, appData.gameListTags ); + strcpy( lpUserGLT, appData.gameListTags ); - result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt ); + result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT ); if( result == 0 ) { /* [AS] Memory leak here! */ - appData.gameListTags = strdup( glt ); + appData.gameListTags = strdup( lpUserGLT ); } return result; } - VOID DisplayIcsInteractionTitle(char *str) { diff --git a/winboard/winboard.rc b/winboard/winboard.rc index 428e1c4..a2e9a96 100644 --- a/winboard/winboard.rc +++ b/winboard/winboard.rc @@ -857,7 +857,7 @@ BEGIN CONTROL "Periodic Updates (for Analysis Mode)", IDC_EpPeriodicUpdates,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,4,40,131,10 - GROUPBOX "Engine-engine matches",IDC_STATIC,4,56,200,98 + GROUPBOX "Adjudications in non-ICS games",IDC_STATIC,4,56,200,98 LTEXT "Adjudicate draw after:",IDC_STATIC,10,72,70,8 EDITTEXT IDC_EpDrawMoveCount,116,68,40,14,ES_AUTOHSCROLL LTEXT "moves",IDC_STATIC,158,72,22,8 diff --git a/xboard.c b/xboard.c index 343e0e6..4048069 100644 --- a/xboard.c +++ b/xboard.c @@ -1,4 +1,4 @@ -/* +Xg/* * xboard.c -- X front end for XBoard * * Copyright 1991 by Digital Equipment Corporation, Maynard, @@ -322,6 +322,8 @@ void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms)) void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms)); void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms)); void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms)); +void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms)); +void GameListOptionsPopDown P(()); void ShufflePopDown P(()); void EnginePopDown P(()); void UciPopDown P(()); @@ -662,6 +664,7 @@ MenuItem optionsMenu[] = { {N_("Engine #1 Settings ..."), FirstSettingsProc}, {N_("Engine #2 Settings ..."), SecondSettingsProc}, {N_("Time Control ..."), TimeControlProc}, + {N_("Game List ..."), GameListOptionsPopUp}, {"----", NothingProc}, // {N_("Always Queen"), AlwaysQueenProc}, // {N_("Animate Dragging"), AnimateDraggingProc}, @@ -785,10 +788,19 @@ XtActionsRec boardActions[] = { // { "BlackClock", BlackClock }, { "Iconify", Iconify }, { "LoadSelectedProc", LoadSelectedProc }, +<<<<<<< HEAD // { "LoadPositionProc", LoadPositionProc }, // { "LoadNextPositionProc", LoadNextPositionProc }, // { "LoadPrevPositionProc", LoadPrevPositionProc }, // { "ReloadPositionProc", ReloadPositionProc }, +======= + { "SetFilterProc", SetFilterProc }, + { "ReloadGameProc", ReloadGameProc }, + { "LoadPositionProc", LoadPositionProc }, + { "LoadNextPositionProc", LoadNextPositionProc }, + { "LoadPrevPositionProc", LoadPrevPositionProc }, + { "ReloadPositionProc", ReloadPositionProc }, +>>>>>>> master { "CopyPositionProc", CopyPositionProc }, { "PastePositionProc", PastePositionProc }, { "CopyGameProc", CopyGameProc }, @@ -881,6 +893,7 @@ XtActionsRec boardActions[] = { // { "FileNamePopDown", (XtActionProc) FileNamePopDown }, { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown }, { "GameListPopDown", (XtActionProc) GameListPopDown }, + { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown }, { "PromotionPopDown", (XtActionProc) PromotionPopDown }, // { "HistoryPopDown", (XtActionProc) HistoryPopDown }, { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown }, @@ -1074,9 +1087,25 @@ colorVariable[] = { NULL }; +// [HGM] font: keep a font for each square size, even non-stndard ones +#define NUM_SIZES 18 +#define MAX_SIZE 130 +Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE]; +char *fontTable[NUM_FONTS][MAX_SIZE]; + void ParseFont(char *name, int number) { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name + int size; + if(sscanf(name, "size%d:", &size)) { + // [HGM] font: font is meant for specific boardSize (likely from settings file); + // defer processing it until we know if it matches our board size + if(size >= 0 && sizeargLoc) { + char *name, buf[MSG_SIZ]; + int i, n = (int)ad->argLoc; + switch(n) { case 0: // CLOCK_FONT name = appData.clockFont; break; @@ -1151,9 +1182,14 @@ SaveFontArg(FILE *f, ArgDescriptor *ad) default: return; } -// Do not save fonts for now, as the saved font would be board-size specific -// and not suitable for a re-start at another board size -// fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, name); + for(i=0; iargName, i, fontTable[n][i]); } void @@ -1871,6 +1907,20 @@ main(argc, argv) if (appData.animate || appData.animateDragging) CreateAnimVars(); + /* [AS] Restore layout */ + if( wpMoveHistory.visible ) { + HistoryPopUp(); + } + + if( wpEvalGraph.visible ) + { + EvalGraphPopUp(); + }; + + if( wpEngineOutput.visible ) { + EngineOutputPopUp(); + } + InitBackEnd2(); if (errorExitStatus == -1) { @@ -2433,54 +2483,18 @@ void PieceMenuPopup(w, event, params, num_params) String *params; Cardinal *num_params; { - String whichMenu; - - if (event->type != ButtonRelease) UnLoadPV(); // [HGM] pv - if (event->type != ButtonPress) return; - if (errorUp) ErrorPopDown(); - switch (gameMode) { - case EditPosition: - case IcsExamining: - whichMenu = params[0]; - break; - case IcsObserving: - if(!appData.icsEngineAnalyze) return; - case IcsPlayingWhite: - case IcsPlayingBlack: - if(!appData.zippyPlay) goto noZip; - case AnalyzeMode: - case AnalyzeFile: - case MachinePlaysWhite: - case MachinePlaysBlack: - case TwoMachinesPlay: // [HGM] pv: use for showing PV - if (!appData.dropMenu) { - LoadPV(event->xbutton.x, event->xbutton.y); - return; - } - if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode || - gameMode == AnalyzeFile || gameMode == IcsObserving) return; - case EditGame: - noZip: - if (!appData.dropMenu || appData.testLegality && - gameInfo.variant != VariantBughouse && - gameInfo.variant != VariantCrazyhouse) return; - SetupDropMenu(); - whichMenu = "menuD"; - break; - default: - return; - } - - if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_WIDTH)) < 0) || - ((pmFromY = EventToSquare(event->xbutton.y, BOARD_HEIGHT)) < 0)) { - pmFromX = pmFromY = -1; - return; + String whichMenu; int menuNr; + if (event->type == ButtonRelease) + menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY); + else if (event->type == ButtonPress) + menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY); + switch(menuNr) { + case 0: whichMenu = params[0]; break; + case 1: SetupDropMenu(); whichMenu = "menuD"; break; + case 2: + case -1: if (errorUp) ErrorPopDown(); + default: return; } - if (flipView) - pmFromX = BOARD_WIDTH - 1 - pmFromX; - else - pmFromY = BOARD_HEIGHT - 1 - pmFromY; - XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu)); } @@ -2595,10 +2609,6 @@ SetHighlights(fromX, fromY, toX, toY) { drawHighlight(hi1X, hi1Y, LINE_TYPE_NORMAL); } - if (fromX >= 0 && fromY >= 0) - { - drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT); - } } if (hi2X != toX || hi2Y != toY) { @@ -2606,6 +2616,15 @@ SetHighlights(fromX, fromY, toX, toY) { drawHighlight(hi2X, hi2Y, LINE_TYPE_NORMAL); } + } + if (hi1X != fromX || hi1Y != fromY) + { + if (fromX >= 0 && fromY >= 0) + { + drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT); + } + if (hi2X != toX || hi2Y != toY) + { if (toX >= 0 && toY >= 0) { drawHighlight(toX, toY, LINE_TYPE_HIGHLIGHT); @@ -2784,6 +2803,19 @@ void DrawSquare(row, column, piece, do_flash) cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + //TODO +// switch (event->type) { +// case Expose: +// if (event->xexpose.count > 0) return; /* no clipping is done */ +// XDrawPosition(widget, True, NULL); +// break; +// case MotionNotify: +// if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break; +// default: +// return; +// } +//} +/* end why */ cairo_set_font_size (cr, 12.0); cairo_text_extents (cr, string, &extents); @@ -2979,6 +3011,36 @@ static int check_castle_draw(newb, oldb, rrow, rcol) return 0; } +// [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph +void DrawSeekAxis( int x, int y, int xTo, int yTo ) +{ + XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo); +} + +void DrawSeekBackground( int left, int top, int right, int bottom ) +{ + XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top); +} + +void DrawSeekText(char *buf, int x, int y) +{ + XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf)); +} + +void DrawSeekDot(int x, int y, int colorNr) +{ + int square = colorNr & 0x80; + GC color; + colorNr &= 0x7F; + color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC; + if(square) + XFillRectangle(xDisplay, xBoardWindow, color, + x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9); + else + XFillArc(xDisplay, xBoardWindow, color, + x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360); +} + static int damage[BOARD_RANKS][BOARD_FILES]; /* @@ -2994,6 +3056,8 @@ void DrawPosition( repaint, board) static Board lastBoard; int rrow, rcol; + if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up + if (board == NULL) { if (!lastBoardValid) return; board = lastBoard; diff --git a/xboard.h b/xboard.h index 7ada32f..77d41f3 100644 --- a/xboard.h +++ b/xboard.h @@ -113,7 +113,7 @@ typedef struct { #define FCP_NAMES "" #define SCP_NAMES "" #define ICS_TEXT_MENU_DEFAULT "" -#define SETTINGS_FILE "/etc/xboard/xboard.conf" +#define SETTINGS_FILE SYSCONFDIR"/xboard.conf" #define COLOR_BKGD "white" typedef int (*FileProc) P((FILE *f, int n, char *title)); diff --git a/xgamelist.c b/xgamelist.c index 51827b6..70d1dc8 100644 --- a/xgamelist.c +++ b/xgamelist.c @@ -94,15 +94,29 @@ extern char *getenv(); extern GtkWidget *GUI_GameList; extern GtkListStore *LIST_GameList; +void SetFocus P((Widget w, XtPointer data, XEvent *event, Boolean *b)); + +extern Widget formWidget, shellWidget, boardWidget, menuBarWidget, gameListShell; -extern Widget formWidget, boardWidget, menuBarWidget, gameListShell; extern int squareSize; extern Pixmap xMarkPixmap; extern char *layoutName; +static Widget filterText; +static char filterString[MSG_SIZ]; +static int listLength; + char gameListTranslations[] = - "(2): LoadSelectedProc() \n \ - Return: LoadSelectedProc() \n"; + "(2): LoadSelectedProc(0) \n \ + Home: LoadSelectedProc(-2) \n \ + End: LoadSelectedProc(2) \n \ + Up: LoadSelectedProc(-1) \n \ + Down: LoadSelectedProc(1) \n \ + Left: LoadSelectedProc(-1) \n \ + Right: LoadSelectedProc(1) \n \ + Return: LoadSelectedProc(0) \n"; +char filterTranslations[] = + "Return: SetFilterProc() \n"; typedef struct { Widget shell; @@ -113,6 +127,7 @@ typedef struct { char *filename; char **strings; } GameListClosure; +static GameListClosure *glc = NULL; static Arg layoutArgs[] = { { XtNborderWidth, 0 }, @@ -128,6 +143,42 @@ GameListCreate(name, callback, client_data) return; } +static int +GameListPrepare() +{ // [HGM] filter: put in separate routine, to make callable from call-back + int nstrings; + ListGame *lg; + char **st, *line; + + nstrings = ((ListGame *) gameList.tailPred)->number; + glc->strings = (char **) malloc((nstrings + 1) * sizeof(char *)); + st = glc->strings; + lg = (ListGame *) gameList.head; + listLength = 0; + while (nstrings--) { + line = GameListLine(lg->number, &lg->gameInfo); + if(filterString[0] == NULLCHAR || SearchPattern( line, filterString ) ) { + *st++ = line; // [HGM] filter: make adding line conditional + listLength++; + } + lg = (ListGame *) lg->node.succ; + } + *st = NULL; + return listLength; +} + +static void +GameListReplace() +{ // [HGM] filter: put in separate routine, to make callable from call-back + Arg args[16]; + int j; + Widget listwidg; + + listwidg = XtNameToWidget(glc->shell, "*form.viewport.list"); + XawListChange(listwidg, glc->strings, 0, 0, True); + XawListHighlight(listwidg, 0); +} + void GameListCallback(w, client_data, call_data) Widget w; @@ -159,7 +210,7 @@ GameListCallback(w, client_data, call_data) } } else if (strcmp(name, _("next")) == 0) { index = rs->list_index + 1; - if (index >= ((ListGame *) gameList.tailPred)->number) { + if (index >= listLength) { DisplayError(_("Can't go forward any further"), 0); return; } @@ -171,7 +222,17 @@ GameListCallback(w, client_data, call_data) return; } XawListHighlight(listwidg, index); + } else if (strcmp(name, _("apply")) == 0) { + String name; + j = 0; + XtSetArg(args[j], XtNstring, &name); j++; + XtGetValues(filterText, args, j); + strcpy(filterString, name); + XawListHighlight(listwidg, 0); + if(GameListPrepare()) GameListReplace(); // crashes on empty list... + return; } + index = atoi(glc->strings[index])-1; // [HGM] filter: read true index from sequence nr of line if (cmailMsgLoaded) { CmailLoadGame(glc->fp, index + 1, glc->filename, True); } else { @@ -179,8 +240,6 @@ GameListCallback(w, client_data, call_data) } } -static GameListClosure *glc = NULL; - void GameListPopUp(fp, filename) FILE *fp; @@ -207,7 +266,6 @@ GameListPopUp(fp, filename) lg = (ListGame *) lg->node.succ; } - /* show widget */ gtk_widget_show (GUI_GameList); @@ -265,13 +323,22 @@ LoadSelectedProc(w, event, prms, nprms) { Widget listwidg; XawListReturnStruct *rs; - int index; + int index, direction = atoi(prms[0]); if (glc == NULL) return; listwidg = XtNameToWidget(glc->shell, "*form.viewport.list"); rs = XawListShowCurrent(listwidg); index = rs->list_index; if (index < 0) return; + if(direction != 0) { + index += direction; + if(direction == -2) index = 0; + if(direction == 2) index = listLength-1; + if(index < 0 || index >= listLength) return; + XawListHighlight(listwidg, index); + return; + } + index = atoi(glc->strings[index])-1; // [HGM] filter: read true index from sequence nr of line if (cmailMsgLoaded) { CmailLoadGame(glc->fp, index + 1, glc->filename, True); } else { @@ -280,6 +347,29 @@ LoadSelectedProc(w, event, prms, nprms) } void +SetFilterProc(w, event, prms, nprms) + Widget w; + XEvent *event; + String *prms; + Cardinal *nprms; +{ + Arg args[16]; + String name; + Widget list; + int j = 0; + XtSetArg(args[j], XtNstring, &name); j++; + XtGetValues(filterText, args, j); + strcpy(filterString, name); + if(GameListPrepare()) GameListReplace(); // crashes on empty list... + list = XtNameToWidget(glc->shell, "*form.viewport.list"); + XawListHighlight(list, 0); + j = 0; + XtSetArg(args[j], XtNdisplayCaret, False); j++; + XtSetValues(filterText, args, j); + XtSetKeyboardFocus(glc->shell, list); +} + +void GameListPopDown() { /* hides the history window */ @@ -293,9 +383,12 @@ GameListHighlight(index) int index; { Widget listwidg; + int i=0; char **st; if (glc == NULL || !glc->up) return; listwidg = XtNameToWidget(glc->shell, "*form.viewport.list"); - XawListHighlight(listwidg, index - 1); + st = glc->strings; + while(*st && atoi(*st)list_index; + if (index < 0) { + DisplayError(_("No tag selected"), 0); + return; + } + p = strings[index]; + if (strcmp(name, _("down")) == 0) { + if(index >= strlen(GLT_ALL_TAGS)) return; + strings[index] = strings[index+1]; + strings[++index] = p; + } else + if (strcmp(name, _("up")) == 0) { + if(index == 0) return; + strings[index] = strings[index-1]; + strings[--index] = p; + } else + if (strcmp(name, _("factory")) == 0) { + strcpy(lpUserGLT, GLT_DEFAULT_TAGS); + GLT_TagsToList(lpUserGLT); + index = 0; + } + XawListHighlight(listwidg, index); +} + +Widget +GameListOptionsCreate() +{ + Arg args[16]; + Widget shell, form, viewport, layout; + Widget b_load, b_loadprev, b_loadnext, b_close, b_cancel; + Dimension fw_width; + XtPointer client_data = NULL; + int j; + + j = 0; + XtSetArg(args[j], XtNwidth, &fw_width); j++; + XtGetValues(formWidget, args, j); + + j = 0; + XtSetArg(args[j], XtNresizable, True); j++; + XtSetArg(args[j], XtNallowShellResize, True); j++; + shell = gameListOptShell = + XtCreatePopupShell("Game-list options", transientShellWidgetClass, + shellWidget, args, j); + layout = + XtCreateManagedWidget(layoutName, formWidgetClass, shell, + layoutArgs, XtNumber(layoutArgs)); + j = 0; + XtSetArg(args[j], XtNborderWidth, 0); j++; + form = + XtCreateManagedWidget("form", formWidgetClass, layout, args, j); + + j = 0; + XtSetArg(args[j], XtNdefaultColumns, 1); j++; + XtSetArg(args[j], XtNforceColumns, True); j++; + XtSetArg(args[j], XtNverticalList, True); j++; + listwidg = viewport = + XtCreateManagedWidget("list", listWidgetClass, form, args, j); + XawListHighlight(listwidg, 0); +// XtAugmentTranslations(listwidg, +// XtParseTranslationTable(gameListOptTranslations)); + + j = 0; + XtSetArg(args[j], XtNfromVert, viewport); j++; + XtSetArg(args[j], XtNtop, XtChainBottom); j++; + XtSetArg(args[j], XtNbottom, XtChainBottom); j++; + XtSetArg(args[j], XtNleft, XtChainLeft); j++; + XtSetArg(args[j], XtNright, XtChainLeft); j++; + b_load = + XtCreateManagedWidget(_("factory"), commandWidgetClass, form, args, j); + XtAddCallback(b_load, XtNcallback, GameListOptionsCallback, client_data); + + j = 0; + XtSetArg(args[j], XtNfromVert, viewport); j++; + XtSetArg(args[j], XtNfromHoriz, b_load); j++; + XtSetArg(args[j], XtNtop, XtChainBottom); j++; + XtSetArg(args[j], XtNbottom, XtChainBottom); j++; + XtSetArg(args[j], XtNleft, XtChainLeft); j++; + XtSetArg(args[j], XtNright, XtChainLeft); j++; + b_loadprev = + XtCreateManagedWidget(_("up"), commandWidgetClass, form, args, j); + XtAddCallback(b_loadprev, XtNcallback, GameListOptionsCallback, client_data); + + j = 0; + XtSetArg(args[j], XtNfromVert, viewport); j++; + XtSetArg(args[j], XtNfromHoriz, b_loadprev); j++; + XtSetArg(args[j], XtNtop, XtChainBottom); j++; + XtSetArg(args[j], XtNbottom, XtChainBottom); j++; + XtSetArg(args[j], XtNleft, XtChainLeft); j++; + XtSetArg(args[j], XtNright, XtChainLeft); j++; + b_loadnext = + XtCreateManagedWidget(_("down"), commandWidgetClass, form, args, j); + XtAddCallback(b_loadnext, XtNcallback, GameListOptionsCallback, client_data); + + j = 0; + XtSetArg(args[j], XtNfromVert, viewport); j++; + XtSetArg(args[j], XtNfromHoriz, b_loadnext); j++; + XtSetArg(args[j], XtNtop, XtChainBottom); j++; + XtSetArg(args[j], XtNbottom, XtChainBottom); j++; + XtSetArg(args[j], XtNleft, XtChainLeft); j++; + XtSetArg(args[j], XtNright, XtChainLeft); j++; + b_cancel = + XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j); + XtAddCallback(b_cancel, XtNcallback, GameListOptionsCallback, client_data); + + j = 0; + XtSetArg(args[j], XtNfromVert, viewport); j++; + XtSetArg(args[j], XtNfromHoriz, b_cancel); j++; + XtSetArg(args[j], XtNtop, XtChainBottom); j++; + XtSetArg(args[j], XtNbottom, XtChainBottom); j++; + XtSetArg(args[j], XtNleft, XtChainLeft); j++; + XtSetArg(args[j], XtNright, XtChainLeft); j++; + b_close = + XtCreateManagedWidget(_("OK"), commandWidgetClass, form, args, j); + XtAddCallback(b_close, XtNcallback, GameListOptionsCallback, client_data); + + strcpy(lpUserGLT, appData.gameListTags); + GLT_TagsToList(lpUserGLT); + + XtRealizeWidget(shell); + CatchDeleteWindow(shell, "GameListOptionsPopDown"); + + return shell; +} + +void +GameListOptionsPopUp(Widget w, XEvent *event, String *prms, Cardinal *nprms) +{ + Arg args[16]; + int j, nstrings; + Widget listwidg; + + if (gameListOptShell == NULL) { + gameListOptShell = GameListOptionsCreate(); + } + + XtPopup(gameListOptShell, XtGrabNone); +} + + diff --git a/xgamelist.h b/xgamelist.h index 21340aa..0c3fdf8 100644 --- a/xgamelist.h +++ b/xgamelist.h @@ -27,6 +27,8 @@ void ShowGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms)); void LoadSelectedProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms)); +void SetFilterProc P((Widget w, XEvent *event, + String *prms, Cardinal *nprms)); extern Widget gameListShell; #endif /* _XGAMEL_H */ diff --git a/xoptions.c b/xoptions.c index f6cac78..cf42d8e 100644 --- a/xoptions.c +++ b/xoptions.c @@ -88,6 +88,7 @@ extern char *layoutName; extern Window xBoardWindow; extern Arg layoutArgs[2], formArgs[2]; Pixel timerForegroundPixel, timerBackgroundPixel; +extern int searchTime; // [HGM] the following code for makng menu popups was cloned from the FileNamePopUp routines @@ -266,54 +267,77 @@ void TimeControlCallback(w, client_data, call_data) XtGetValues(w, args, 1); if (strcmp(name, _("classical")) == 0) { - if(!tcInc) return; + if(tcInc == 0) return; j=0; XtSetArg(args[j], XtNlabel, _("minutes for each")); j++; XtSetValues(tcMess1, args, j); j=0; XtSetArg(args[j], XtNlabel, _("moves")); j++; XtSetValues(tcMess2, args, j); - j=0; - XtSetArg(args[j], XtNstring, &name); j++; - XtGetValues(tcData, args, j); - tcIncrement = 0; sscanf(name, "%d", &tcIncrement); + if(tcInc == 1) { + j=0; + XtSetArg(args[j], XtNstring, &name); j++; + XtGetValues(tcData, args, j); + tcIncrement = 0; sscanf(name, "%d", &tcIncrement); + } sprintf(buf, "%d", tcMoves); j=0; XtSetArg(args[j], XtNstring, buf); j++; XtSetValues(tcData, args, j); - tcInc = False; + tcInc = 0; return; } if (strcmp(name, _("incremental")) == 0) { - if(tcInc) return; + if(tcInc == 1) return; j=0; XtSetArg(args[j], XtNlabel, _("minutes, plus")); j++; XtSetValues(tcMess1, args, j); j=0; XtSetArg(args[j], XtNlabel, _("sec/move")); j++; XtSetValues(tcMess2, args, j); - j=0; - XtSetArg(args[j], XtNstring, &name); j++; - XtGetValues(tcData, args, j); - tcMoves = appData.movesPerSession; sscanf(name, "%d", &tcMoves); + if(tcInc == 0) { + j=0; + XtSetArg(args[j], XtNstring, &name); j++; + XtGetValues(tcData, args, j); + tcMoves = appData.movesPerSession; sscanf(name, "%d", &tcMoves); + } sprintf(buf, "%d", tcIncrement); j=0; XtSetArg(args[j], XtNstring, buf); j++; XtSetValues(tcData, args, j); - tcInc = True; + tcInc = 1; + return; + } + if (strcmp(name, _("fixed time")) == 0) { + if(tcInc == 2) return; + j=0; + XtSetArg(args[j], XtNlabel, _("sec/move (max)")); j++; + XtSetValues(tcMess1, args, j); + j=0; + XtSetArg(args[j], XtNlabel, _("")); j++; + XtSetValues(tcMess2, args, j); + j=0; + XtSetArg(args[j], XtNstring, ""); j++; + XtSetValues(tcData, args, j); + tcInc = 2; return; } if (strcmp(name, _(" OK ")) == 0) { int inc, mps, tc, ok; XtSetArg(args[0], XtNstring, &txt); XtGetValues(tcData, args, 1); - if(tcInc) { + switch(tcInc) { + case 1: ok = sscanf(txt, "%d", &inc); mps = 0; if(!ok && txt[0] == 0) { inc = 0; ok = 1; } // accept empty string as zero ok &= (inc >= 0); - } else { + break; + case 0: ok = sscanf(txt, "%d", &mps); inc = -1; ok &= (mps > 0); + break; + case 2: + ok = 1; inc = -1; mps = 40; } if(ok != 1) { XtSetArg(args[0], XtNstring, ""); // erase any offending input @@ -322,15 +346,26 @@ void TimeControlCallback(w, client_data, call_data) } XtSetArg(args[0], XtNstring, &txt); XtGetValues(tcTime, args, 1); - if(!ParseTimeControl(txt, inc, mps)) { - XtSetArg(args[0], XtNstring, ""); // erase any offending input - XtSetValues(tcTime, args, 1); - DisplayError(_("Bad Time-Control String"), 0); - return; + if(tcInc == 2) { + if(sscanf(txt, "%d", &inc) != 1) { + XtSetArg(args[0], XtNstring, ""); // erase any offending input + XtSetValues(tcTime, args, 1); + DisplayError(_("Bad Time-Control String"), 0); + return; + } + searchTime = inc; + } else { + if(!ParseTimeControl(txt, inc, mps)) { + XtSetArg(args[0], XtNstring, ""); // erase any offending input + XtSetValues(tcTime, args, 1); + DisplayError(_("Bad Time-Control String"), 0); + return; + } + searchTime = 0; + appData.movesPerSession = mps; + appData.timeIncrement = inc; + appData.timeControl = strdup(txt); } - appData.movesPerSession = mps; - appData.timeIncrement = inc; - appData.timeControl = strdup(txt); XtSetArg(args[0], XtNstring, &txt); XtGetValues(tcOdds1, args, 1); appData.firstTimeOdds = first.timeOdds @@ -355,7 +390,7 @@ void TimeControlPopUp() unsigned int mask; char def[80]; - tcInc = (appData.timeIncrement >= 0); + tcInc = searchTime > 0 ? 2 : (appData.timeIncrement >= 0); tcMoves = appData.movesPerSession; tcIncrement = appData.timeIncrement; if(!tcInc) tcIncrement = 0; sprintf(def, "%d", tcInc ? tcIncrement : tcMoves); @@ -397,7 +432,7 @@ void TimeControlPopUp() XtAddEventHandler(tcTime, ButtonPressMask, False, SetFocus, (XtPointer) popup); j= 0; - XtSetArg(args[j], XtNlabel, tcInc ? _(" minutes, plus ") : _("minutes for each")); j++; + XtSetArg(args[j], XtNlabel, tcInc ? tcInc == 2 ? _("sec/move (max) ") : _(" minutes, plus ") : _("minutes for each")); j++; XtSetArg(args[j], XtNborderWidth, 0); j++; XtSetArg(args[j], XtNfromHoriz, tcTime); j++; XtSetArg(args[j], XtNtop, XtChainTop); j++; @@ -426,7 +461,7 @@ void TimeControlPopUp() XtAddEventHandler(tcData, ButtonPressMask, False, SetFocus, (XtPointer) popup); j= 0; - XtSetArg(args[j], XtNlabel, tcInc ? _("sec/move") : _("moves ")); j++; + XtSetArg(args[j], XtNlabel, tcInc ? tcInc == 2 ? _(" ") : _("sec/move") : _("moves ")); j++; XtSetArg(args[j], XtNjustify, XtJustifyLeft); j++; XtSetArg(args[j], XtNborderWidth, 0); j++; XtSetArg(args[j], XtNfromHoriz, tcData); j++; @@ -494,18 +529,34 @@ void TimeControlPopUp() XtSetArg(args[j], XtNtop, XtChainBottom); j++; XtSetArg(args[j], XtNleft, XtChainLeft); j++; XtSetArg(args[j], XtNright, XtChainLeft); j++; - b_clas= XtCreateManagedWidget(_("classical"), commandWidgetClass, + XtSetArg(args[j], XtNstate, tcInc==0); j++; + b_clas= XtCreateManagedWidget(_("classical"), toggleWidgetClass, form, args, j); XtAddCallback(b_clas, XtNcallback, TimeControlCallback, (XtPointer) 0); j=0; + XtSetArg(args[j], XtNradioGroup, b_clas); j++; XtSetArg(args[j], XtNfromVert, tcOdds1); j++; XtSetArg(args[j], XtNfromHoriz, b_clas); j++; XtSetArg(args[j], XtNbottom, XtChainBottom); j++; XtSetArg(args[j], XtNtop, XtChainBottom); j++; XtSetArg(args[j], XtNleft, XtChainLeft); j++; XtSetArg(args[j], XtNright, XtChainLeft); j++; - b_inc = XtCreateManagedWidget(_("incremental"), commandWidgetClass, + XtSetArg(args[j], XtNstate, tcInc==1); j++; + b_inc = XtCreateManagedWidget(_("incremental"), toggleWidgetClass, + form, args, j); + XtAddCallback(b_inc, XtNcallback, TimeControlCallback, (XtPointer) 0); + + j=0; + XtSetArg(args[j], XtNradioGroup, b_inc); j++; + XtSetArg(args[j], XtNfromVert, tcOdds1); j++; + XtSetArg(args[j], XtNfromHoriz, b_inc); j++; + XtSetArg(args[j], XtNbottom, XtChainBottom); j++; + XtSetArg(args[j], XtNtop, XtChainBottom); j++; + XtSetArg(args[j], XtNleft, XtChainLeft); j++; + XtSetArg(args[j], XtNright, XtChainLeft); j++; + XtSetArg(args[j], XtNstate, tcInc==2); j++; + b_inc = XtCreateManagedWidget(_("fixed time"), toggleWidgetClass, form, args, j); XtAddCallback(b_inc, XtNcallback, TimeControlCallback, (XtPointer) 0); @@ -695,7 +746,7 @@ void EnginePopUp() XtSetArg(args[j-3], XtNstate, appData.secondScoreIsAbsolute); w4 = XtCreateManagedWidget(_("Engine #2 Score is Absolute"), toggleWidgetClass, form, args, j); - s1 = XtCreateManagedWidget(_("\nEngine-Engine Adjudications:"), labelWidgetClass, form, args, 3); + s1 = XtCreateManagedWidget(_("\nAdjudications in non-ICS games:"), labelWidgetClass, form, args, 3); XtSetArg(args[j-1], XtNfromVert, (XtArgVal) s1); XtSetArg(args[j-3], XtNstate, appData.testClaims);