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)
{ "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 },
{ "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 },
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)
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;
/*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));
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));
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 */
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)
{
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);
}
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<nrOfSeekAds; k++) if(k != i) {
+ int xx = xList[k], yy = yList[k];
+ if(xx <= x+d && xx > x-d && yy <= y+d && yy > y-d)
+ DrawSeekDot(xx, yy, colorList[k]);
+ }
+}
+
+void
+RemoveSeekAd(int nr)
+{
+ int i;
+ for(i=0; i<nrOfSeekAds; i++) if(seekNrList[i] == nr) {
+ EraseSeekDot(i);
+ if(seekAdList[i]) free(seekAdList[i]);
+ seekAdList[i] = seekAdList[--nrOfSeekAds];
+ seekNrList[i] = seekNrList[nrOfSeekAds];
+ ratingList[i] = ratingList[nrOfSeekAds];
+ colorList[i] = colorList[nrOfSeekAds];
+ tcList[i] = tcList[nrOfSeekAds];
+ xList[i] = xList[nrOfSeekAds];
+ yList[i] = yList[nrOfSeekAds];
+ zList[i] = zList[nrOfSeekAds];
+ seekAdList[nrOfSeekAds] = NULL;
+ break;
+ }
+}
+
+Boolean
+MatchSoughtLine(char *line)
+{
+ char handle[MSG_SIZ], rating[MSG_SIZ], type[MSG_SIZ];
+ int nr, base, inc, u=0; char dummy;
+
+ if(sscanf(line, "%d %s %s %d %d rated %s", &nr, rating, handle, &base, &inc, type) == 6 ||
+ sscanf(line, "%d %s %s %s %d %d rated %c", &nr, rating, handle, type, &base, &inc, &dummy) == 7 ||
+ (u=1) &&
+ (sscanf(line, "%d %s %s %d %d unrated %s", &nr, rating, handle, &base, &inc, type) == 6 ||
+ sscanf(line, "%d %s %s %s %d %d unrated %c", &nr, rating, handle, type, &base, &inc, &dummy) == 7) ) {
+ // match: compact and save the line
+ AddAd(handle, rating, base, inc, u ? 'u' : 'r', type, nr, FALSE);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int
+DrawSeekGraph()
+{
+ if(!seekGraphUp) return FALSE;
+ int i;
+ h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
+ w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
+
+ DrawSeekBackground(0, 0, w, h);
+ DrawSeekAxis(hMargin, h-1-vMargin, w-5, h-1-vMargin);
+ DrawSeekAxis(hMargin, h-1-vMargin, hMargin, 5);
+ for(i=0; i<4000; i+= 100) if(i>=minRating && i<maxRating) {
+ int yy =((double)i - minRating)/(maxRating - minRating)*(h-vMargin-squareSize/8-1) + vMargin;
+ yy = h-1-yy;
+ DrawSeekAxis(hMargin+5*(i%500==0), yy, hMargin-5, yy); // rating ticks
+ if(i%500 == 0) {
+ char buf[MSG_SIZ];
+ sprintf(buf, "%d", i);
+ DrawSeekText(buf, hMargin+squareSize/8+7, yy);
+ }
+ }
+ DrawSeekText("unrated", hMargin+squareSize/8+7, h-1-vMargin-squareSize/4);
+ for(i=1; i<100; i+=(i<10?1:5)) {
+ int xx = (w-hMargin)* log((double)i)/log(100.) + hMargin;
+ DrawSeekAxis(xx, h-1-vMargin, xx, h-6-vMargin-3*(i%10==0)); // TC ticks
+ if(i<=5 || (i>40 ? 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; i<nrOfSeekAds; i++) PlotSeekAd(i);
+ return TRUE;
+}
+
+int SeekGraphClick(ClickType click, int x, int y, int moving)
+{
+ static int lastDown = 0, displayed = 0, lastSecond;
+ if(!seekGraphUp) { // initiate cration of seek graph by requesting seek-ad list
+ if(click == Release || moving) return FALSE;
+ nrOfSeekAds = 0;
+ soughtPending = TRUE;
+ SendToICS(ics_prefix);
+ SendToICS("sought\n"); // should this be "sought all"?
+ } else { // issue challenge based on clicked ad
+ int dist = 10000; int i, closest = 0, second = 0;
+ for(i=0; i<nrOfSeekAds; i++) {
+ int d = (x-xList[i])*(x-xList[i]) + (y-yList[i])*(y-yList[i]) + zList[i];
+ if(d < dist) { dist = d; closest = i; }
+ second += (d - zList[i] < 120); // count in-range ads
+ if(click == Press && moving != 1 && zList[i]>0) 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;
char talker[MSG_SIZ]; // [HGM] chat
int channel;
+ connectionAlive = TRUE; // [HGM] alive: I think, therefore I am...
+
if (appData.debugMode) {
if (!error) {
fprintf(debugFP, "<ICS: ");
sprintf(str,
"/set-quietly interface %s\n/set-quietly style 12\n",
programVersion);
+ if(appData.seekGraph && appData.autoRefresh) // [HGM] seekgraph
+ strcat(str, "/set-2 51 1\n/set seek 1\n");
} else if (ics_type == ICS_CHESSNET) {
sprintf(str, "/style 12\n");
} else {
strcpy(str, "alias $ @\n$set interface ");
strcat(str, programVersion);
strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
+ if(appData.seekGraph && appData.autoRefresh) // [HGM] seekgraph
+ strcat(str, "$iset seekremove 1\n$set seek 1\n");
#ifdef WIN32
strcat(str, "$iset nohighlight 1\n");
#endif
continue;
}
+ // [HGM] seekgraph: recognize sought lines and end-of-sought message
+ if(appData.seekGraph) {
+ if(soughtPending && MatchSoughtLine(buf+i)) {
+ i = strstr(buf+i, "rated") - buf;
+ next_out = leftover_start = i;
+ started = STARTED_CHATTER;
+ suppressKibitz = TRUE;
+ continue;
+ }
+ if((gameMode == IcsIdle || gameMode == BeginningOfGame)
+ && looking_at(buf, &i, "* ads displayed")) {
+ soughtPending = FALSE;
+ seekGraphUp = TRUE;
+ DrawSeekGraph();
+ continue;
+ }
+ if(appData.autoRefresh) {
+ if(looking_at(buf, &i, "* (*) seeking * * * * *\"play *\" to respond)\n")) {
+ int s = (ics_type == ICS_ICC); // ICC format differs
+ if(seekGraphUp)
+ AddAd(star_match[0], star_match[1], atoi(star_match[2+s]), atoi(star_match[3+s]),
+ star_match[4+s][0], star_match[5-3*s], atoi(star_match[7]), TRUE);
+ looking_at(buf, &i, "*% "); // eat prompt
+ next_out = i; // suppress
+ continue;
+ }
+ if(looking_at(buf, &i, "Ads removed: *\n") || looking_at(buf, &i, "\031(51 * *\031)")) {
+ char *p = star_match[0];
+ while(*p) {
+ if(seekGraphUp) RemoveSeekAd(atoi(p));
+ while(*p && *p++ != ' '); // next
+ }
+ looking_at(buf, &i, "*% "); // eat prompt
+ next_out = i;
+ continue;
+ }
+ }
+ }
+
/* skip formula vars */
if (started == STARTED_NONE &&
buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {
} else
if(looking_at(buf, &i, "kibitzed to *\n") && atoi(star_match[0])) {
// suppress the acknowledgements of our own autoKibitz
+ char *p;
+ if(p = strchr(star_match[0], ' ')) p[1] = NULLCHAR; // clip off "players)" on FICS
SendToPlayer(star_match[0], strlen(star_match[0]));
looking_at(buf, &i, "*% "); // eat prompt
next_out = i;
if(channel >= 0) // channel broadcast; look if there is a chatbox for this channel
for(p=0; p<MAX_CHAT; p++) {
if(channel == atoi(chatPartner[p])) {
- talker[0] = '['; strcat(talker, "]");
+ talker[0] = '['; strcat(talker, "] ");
chattingPartner = p; break;
}
} else
if(buf[i-3] == 'r') // whisper; look if there is a WHISPER chatbox
for(p=0; p<MAX_CHAT; p++) {
if(!strcmp("WHISPER", chatPartner[p])) {
- talker[0] = '['; strcat(talker, "]");
+ talker[0] = '['; strcat(talker, "] ");
chattingPartner = p; break;
}
}
if(chattingPartner<0) i = oldi; else {
started = STARTED_COMMENT;
parse_pos = 0; parse[0] = NULLCHAR;
- savingComment = TRUE;
+ savingComment = 3 + chattingPartner; // counts as TRUE
suppressKibitz = TRUE;
}
} // [HGM] chat: end of patch
memcpy(parse, &buf[oldi], parse_pos);
parse[parse_pos] = NULLCHAR;
started = STARTED_COMMENT;
+ if(savingComment >= 3) // [HGM] chat: continuation of line for chat box
+ chattingPartner = savingComment - 3; // kludge to remember the box
} else {
started = STARTED_CHATTER;
}
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;
((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)) ||
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;
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),
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)
{
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*/
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;
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);
return;
if (fromX == -1) {
+ if(!appData.oneClick || !OnlyMove(&x, &y)) {
if (clickType == Press) {
/* First square */
if (OKToStartUserMove(x, y)) {
}
}
return;
+ }
}
/* fromX != -1 */
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]++;
}
}
}
+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;
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<BOARD_HEIGHT; i++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++)
+ { ChessSquare p = boards[forwardMostMove][i][j];
+ int m=i;
+
+ switch((int) p)
+ { /* count B,N,R and other of each side */
+ case WhiteKing:
+ case BlackKing:
+ NrK++; break; // [HGM] atomic: count Kings
+ case WhiteKnight:
+ NrWN++; break;
+ case WhiteBishop:
+ case WhiteFerz: // [HGM] shatranj: kludge to mke it work in shatranj
+ bishopsColor |= 1 << ((i^j)&1);
+ NrWB++; break;
+ case BlackKnight:
+ NrBN++; break;
+ case BlackBishop:
+ case BlackFerz: // [HGM] shatranj: kludge to mke it work in shatranj
+ bishopsColor |= 1 << ((i^j)&1);
+ NrBB++; break;
+ case WhiteRook:
+ NrWR++; break;
+ case BlackRook:
+ NrBR++; break;
+ case WhiteQueen:
+ NrWQ++; break;
+ case BlackQueen:
+ NrBQ++; break;
+ case EmptySquare:
+ break;
+ case BlackPawn:
+ m = 7-i;
+ case WhitePawn:
+ PawnAdvance += m; NrPawns++;
+ }
+ NrPieces += (p != EmptySquare);
+ NrW += ((int)p < (int)BlackPawn);
+ if(gameInfo.variant == VariantXiangqi &&
+ (p == WhiteFerz || p == WhiteAlfil || p == BlackFerz || p == BlackAlfil)) {
+ NrPieces--; // [HGM] XQ: do not count purely defensive pieces
+ NrW -= ((int)p < (int)BlackPawn);
+ }
+ }
+
+ /* Some material-based adjudications that have to be made before stalemate test */
+ if(gameInfo.variant == VariantAtomic && NrK < 2) {
+ // [HGM] atomic: stm must have lost his King on previous move, as destroying own K is illegal
+ boards[forwardMostMove][EP_STATUS] = EP_CHECKMATE; // make claimable as if stm is checkmated
+ if(canAdjudicate && appData.checkMates) {
+ if(engineOpponent)
+ SendMoveToProgram(forwardMostMove-1, engineOpponent); // make sure opponent gets move
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+ GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins,
+ "Xboard adjudication: King destroyed", GE_XBOARD );
+ return 1;
+ }
+ }
+
+ /* Bare King in Shatranj (loses) or Losers (wins) */
+ if( NrW == 1 || NrPieces - NrW == 1) {
+ if( gameInfo.variant == VariantLosers) { // [HGM] losers: bare King wins (stm must have it first)
+ boards[forwardMostMove][EP_STATUS] = EP_WINS; // mark as win, so it becomes claimable
+ if(canAdjudicate && appData.checkMates) {
+ if(engineOpponent)
+ SendMoveToProgram(forwardMostMove-1, engineOpponent); // make sure opponent gets to see move
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+ GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins,
+ "Xboard adjudication: Bare king", GE_XBOARD );
+ return 1;
+ }
+ } else
+ if( gameInfo.variant == VariantShatranj && --bare < 0)
+ { /* bare King */
+ boards[forwardMostMove][EP_STATUS] = EP_WINS; // make claimable as win for stm
+ if(canAdjudicate && appData.checkMates) {
+ /* but only adjudicate if adjudication enabled */
+ if(engineOpponent)
+ SendMoveToProgram(forwardMostMove-1, engineOpponent); // make sure opponent gets move
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+ GameEnds( NrW > 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;
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
if (gameMode == TwoMachinesPlay) {
GameEnds(machineWhite ? BlackWins : WhiteWins,
buf1, GE_XBOARD);
+<<<<<<< HEAD
}
return;
}
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;
(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 */
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 */
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]++;
}
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 &&
#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
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 ));\r
+Boolean SearchPattern P(( const char * text, const char * pattern ));\r
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));
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));
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 */
char *icsHelper;
Boolean icsInputBox;
Boolean useTelnet;
+ Boolean seekGraph;
+ Boolean autoRefresh;
char *telnetProgram;
char *gateway;
char *loadGameFile;
char *cmailGameName; /* xboard only */
Boolean alwaysPromoteToQueen;
Boolean oldSaveStyle;
+ Boolean oneClick;
Boolean quietPlay;
Boolean showThinking;
Boolean ponderNextMove;
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])
echo " Configurations summary:"
echo ""
echo " prefix: $prefix"
+echo " datarootdir: $datarootdir"
+echo " infodir: $infodir"
+echo " sysconfdir: $sysconfdir"
echo ""
echo " Xaw3d: $with_xaw3d"
echo ""
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));
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));
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));
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)
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' );
+}
+
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <string.h>
+
+#include "common.h"
+#include "frontend.h"
+#include "backend.h"
+
+/* templates for low-level front-end tasks (requiring platform-dependent implementation) */
+void ClearHistoryMemo P((void)); // essential
+int AppendToHistoryMemo P(( char * text, int bold, int colorNr )); // essential (coloring / styling optional)
+void HighlightMove P(( int from, int to, Boolean highlight )); // optional (can be dummy)
+void ScrollToCurrent P((int caretPos)); // optional (can be dummy)
+
+/* templates for front-end entry point to allow inquiring about front-end state */
+Boolean MoveHistoryDialogExists P((void));
+Boolean MoveHistoryIsUp P((void));
+
+/* Module globals */
+typedef char MoveHistoryString[ MOVE_LEN*2 ];
+
+static int lastFirst = 0;
+static int lastLast = 0;
+static int lastCurrent = -1;
+
+static 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<currLast; i++ ) {
+ AppendMoveToMemo( i );
+ }
+}
+
+// back-end part taken out of HighlightMove to determine character positions
+static void DoHighlight(int index, int onoff)
+{
+ if( index >= 0 && index < MAX_MOVES ) {
+ HighlightMove( histMoves[index].memoOffset,
+ histMoves[index].memoOffset + histMoves[index].memoLength, onoff );
+ }
+}
+
+// back-end, now that a wrapper is provided for the front-end code to do the actual scrolling
+void MemoContentUpdated()
+{
+ int caretPos;
+
+ 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<currLast; index++ ) {
+ if( char_index >= histMoves[index].memoOffset &&
+ char_index < (histMoves[index].memoOffset + histMoves[index].memoLength) )
+ {
+ ToNrEvent( index + 1 ); // moved here from call-back
+ }
+ }
+}
+
+// back-end. In WinBoard called by call-back, but could be called directly by SetIfExists?
+void UpdateMoveHistory()
+{
+ /* Update the GUI */
+ if( OnlyCurrentPositionChanged() ) {
+ /* Only "cursor" changed, no need to update memo content */
+ }
+ else if( OneMoveAppended() ) {
+ AppendMoveToMemo( currCurrent );
+ }
+ else {
+ RefreshMemoContent();
+ }
+
+ MemoContentUpdated();
+}
+
+// back-end
+void MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo )
+{
+ /* [AS] Danger! For now we rely on the movelist parameter being a static variable! */
+
+ currMovelist = movelist;
+ currFirst = first;
+ currLast = last;
+ currCurrent = current;
+ currPvInfo = pvInfo;
+
+ if(MoveHistoryDialogExists())
+ UpdateMoveHistory(); // [HGM] call this directly, in stead of through call-back
+}
+
#define PACKAGE_NAME "WinBoard"\r
\r
/* Define to the full name and version of this package. */\r
-#define PACKAGE_STRING "WinBoard master-20091122"\r
+#define PACKAGE_STRING "WinBoard master-20100118"\r
\r
/* Define to the one symbol short name of this package. */\r
#define PACKAGE_TARNAME "winboard"\r
\r
/* Define to the version of this package. */\r
-#define PACKAGE_VERSION "master-20091122"\r
+#define PACKAGE_VERSION "master-20100118"\r
\r
/* Define the Windows-specific FILE version info. this *MUST* be four comma separated 16-bit integers */\r
-#define PACKAGE_FILEVERSION 4,4,2,0\r
+#define PACKAGE_FILEVERSION 4,4,2,1\r
\r
#define PTY_ITERATION\r
\r
\r
OBJS=backend.o book.o gamelist.o lists.o moves.o pgntags.o uci.o zippy.o\\r
parser.o wbres.o wclipbrd.o wedittags.o wengineoutput.o wevalgraph.o\\r
- wgamelist.o whistory.o winboard.o wlayout.o woptions.o wsnap.o\\r
+ wgamelist.o whistory.o history.o winboard.o wlayout.o woptions.o wsnap.o\\r
wsockerr.o help.o wsettings.o wchat.o engineoutput.o evalgraph.o\r
\r
\r
\r
# set up for cygwin or not\r
ifeq ($(USE_MINGW),1)\r
-CFCYG = -mno-cygwin\r
+CFCYG = -mno-cygwin -DUSE_I64\r
LFCYG = -mno-cygwin -lmsvcrt\r
endif\r
\r
../backend.h ../lists.h\r
$(call compile, $<)\r
\r
-wgamelist.o: wgamelist.c config.h. ../common.h winboard.h resource.h ../frontend.h \\r
+wgamelist.o: wgamelist.c config.h ../common.h winboard.h resource.h ../frontend.h \\r
../backend.h ../lists.h\r
$(call compile, $<)\r
\r
../lists.h winboard.h resource.h wsnap.h\r
$(call compile, $<)\r
\r
+history.o: ../history.c config.h ../common.h ../frontend.h ../backend.h \\r
+ ../lists.h\r
+ $(call compile, $<)\r
+\r
wevalgraph.o: wevalgraph.c ../evalgraph.h config.h ../common.h ../frontend.h \\r
../backend.h ../lists.h winboard.h resource.h wsnap.h\r
$(call compile, $<)\r
\r
OBJS=backend.obj book.obj gamelist.obj lists.obj moves.obj pgntags.obj uci.obj\\r
zippy.obj parser.obj wclipbrd.obj wedittags.obj wengineoutput.obj wevalgraph.obj\\r
- wgamelist.obj whistory.obj winboard.obj wlayout.obj woptions.obj wsnap.obj\\r
+ wgamelist.obj whistory.obj history.obj winboard.obj wlayout.obj woptions.obj wsnap.obj\\r
wsockerr.obj help.obj wsettings.obj wchat.obj engineoutput.obj evalgraph.obj\r
\r
\r
../backend.h ../lists.h\r
$(CC) $(CFLAGS) wedittags.c\r
\r
-wgamelist.obj: wgamelist.c config.h. ../common.h winboard.h resource.h ../frontend.h \\r
+wgamelist.obj: wgamelist.c config.h ../common.h winboard.h resource.h ../frontend.h \\r
../backend.h ../lists.h\r
$(CC) $(CFLAGS) wgamelist.c\r
\r
../lists.h winboard.h resource.h wsnap.h\r
$(CC) $(CFLAGS) whistory.c\r
\r
+history.obj: ../history.c config.h ../common.h ../frontend.h ../backend.h \\r
+ ../lists.h\r
+ $(CC) $(CFLAGS) whistory.c\r
+\r
wevalgraph.obj: wevalgraph.c config.h ../common.h ../frontend.h ../backend.h \\r
../lists.h winboard.h resource.h wsnap.h\r
$(CC) $(CFLAGS) wevalgraph.c\r
static SnapData sd;\r
char buf[MSG_SIZ], mess[MSG_SIZ];\r
int partner = -1, i;\r
+ static BOOL filterHasFocus[MAX_CHAT];\r
\r
for(i=0; i<MAX_CHAT; i++) if(hDlg == chatHandle[i]) { partner = i; break; }\r
\r
SetWindowText(hDlg, buf);\r
}\r
chatPartner[partner][0] = 0;\r
+ filterHasFocus[partner] = FALSE;\r
\r
return FALSE;\r
\r
case WM_COMMAND:\r
+ /* \r
+ [AS]\r
+ If <Enter> is pressed while editing the filter, it's better to apply\r
+ the filter rather than selecting the current game.\r
+ */\r
+ if( LOWORD(wParam) == IDC_ChatPartner ) {\r
+ switch( HIWORD(wParam) ) {\r
+ case EN_SETFOCUS:\r
+ filterHasFocus[partner] = TRUE;\r
+ break;\r
+ case EN_KILLFOCUS:\r
+ filterHasFocus[partner] = FALSE;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if( filterHasFocus[partner] && (LOWORD(wParam) == IDC_Send) ) {\r
+ SetFocus(GetDlgItem(hDlg, OPT_ChatInput));\r
+ wParam = IDC_Change;\r
+ }\r
+ /* [AS] End command replacement */\r
+\r
switch (LOWORD(wParam)) {\r
\r
case IDCANCEL:\r
int unfinished;\r
};\r
\r
-/* [AS] Wildcard pattern matching */\r
-static BOOL HasPattern( const char * text, const char * pattern )\r
-{\r
- while( *pattern != '\0' ) {\r
- if( *pattern == '*' ) {\r
- while( *pattern == '*' ) {\r
- pattern++;\r
- }\r
-\r
- if( *pattern == '\0' ) {\r
- return TRUE;\r
- }\r
-\r
- while( *text != '\0' ) {\r
- if( HasPattern( text, pattern ) ) {\r
- return TRUE;\r
- }\r
- text++;\r
- }\r
- }\r
- else if( (*pattern == *text) || ((*pattern == '?') && (*text != '\0')) ) {\r
- pattern++;\r
- text++;\r
- continue;\r
- }\r
-\r
- return FALSE;\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-static BOOL SearchPattern( const char * text, const char * pattern )\r
-{\r
- BOOL result = TRUE;\r
-\r
- if( pattern != NULL && *pattern != '\0' ) {\r
- if( *pattern == '*' ) {\r
- result = HasPattern( text, pattern );\r
- }\r
- else {\r
- result = FALSE;\r
-\r
- while( *text != '\0' ) {\r
- if( HasPattern( text, pattern ) ) {\r
- result = TRUE;\r
- break;\r
- }\r
- text++;\r
- }\r
- }\r
- }\r
-\r
- return result;\r
-}\r
-\r
/* [AS] Setup the game list according to the specified filter */\r
static int GameListToListBox( HWND hDlg, BOOL boReset, char * pszFilter, struct GameListStats * stats )\r
{\r
* Move history for WinBoard\r
*\r
* Author: Alessandro Scotti (Dec 2005)\r
+ * front-end code split off by HGM\r
*\r
* Copyright 2005 Alessandro Scotti\r
*\r
\r
#include "config.h"\r
\r
-#include <windows.h> /* required for all Windows applications */\r
-#include <richedit.h>\r
#include <stdio.h>\r
#include <stdlib.h>\r
#include <malloc.h>\r
+#include <windows.h> /* required for all Windows applications */\r
+#include <richedit.h>\r
#include <commdlg.h>\r
#include <dlgs.h>\r
\r
#include "frontend.h"\r
#include "backend.h"\r
#include "winboard.h"\r
-\r
#include "wsnap.h"\r
\r
-/* Module globals */\r
-typedef char MoveHistoryString[ MOVE_LEN*2 ];\r
-static BOOLEAN moveHistoryDialogUp = FALSE;\r
-\r
-static int lastFirst = 0;\r
-static int lastLast = 0;\r
-static int lastCurrent = -1;\r
-\r
-static char lastLastMove[ MOVE_LEN ];\r
-\r
-static MoveHistoryString * currMovelist;\r
-static ChessProgramStats_Move * currPvInfo;\r
-static int currFirst = 0;\r
-static int currLast = 0;\r
-static int currCurrent = -1;\r
-\r
-typedef struct {\r
- int memoOffset;\r
- int memoLength;\r
-} HistoryMove;\r
-\r
-static HistoryMove histMoves[ MAX_MOVES ];\r
-\r
-#define WM_REFRESH_HISTORY (WM_USER+4657)\r
+// templates for calls into back-end\r
+void RefreshMemoContent P((void));\r
+void MemoContentUpdated P((void));\r
+void FindMoveByCharIndex P(( int char_index ));\r
\r
#define DEFAULT_COLOR 0xFFFFFFFF\r
\r
#define H_MARGIN 2\r
#define V_MARGIN 2\r
\r
-/* Note: in the following code a "Memo" is a Rich Edit control (it's Delphi lingo) */\r
+static BOOLEAN moveHistoryDialogUp = FALSE;\r
\r
-static VOID HighlightMove( int index, BOOL highlight )\r
+// ------------- low-level front-end actions called by MoveHistory back-end -----------------\r
+\r
+// low-level front-end, after calculating from & to is left to caller\r
+// it task is to highlight the indicated characters. (In WinBoard it makes them bold and blue.)\r
+void HighlightMove( int from, int to, Boolean highlight )\r
{\r
- if( index >= 0 && index < MAX_MOVES ) {\r
CHARFORMAT cf;\r
HWND hMemo = GetDlgItem( moveHistoryDialog, IDC_MoveHistory );\r
\r
- SendMessage( hMemo, \r
- EM_SETSEL, \r
- histMoves[index].memoOffset, \r
- histMoves[index].memoOffset + histMoves[index].memoLength );\r
+ SendMessage( hMemo, EM_SETSEL, from, to);\r
\r
\r
/* Set style */\r
}\r
\r
SendMessage( hMemo, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf );\r
- }\r
-}\r
-\r
-static BOOL OnlyCurrentPositionChanged()\r
-{\r
- BOOL result = FALSE;\r
-\r
- if( lastFirst >= 0 &&\r
- lastLast >= lastFirst &&\r
- lastCurrent >= lastFirst && \r
- currFirst == lastFirst &&\r
- currLast == lastLast &&\r
- currCurrent >= 0 &&\r
- TRUE )\r
- {\r
- result = TRUE;\r
-\r
- /* Special case: last move changed */\r
- if( currCurrent == currLast-1 ) {\r
- if( strcmp( currMovelist[currCurrent], lastLastMove ) != 0 ) {\r
- result = FALSE;\r
- }\r
- }\r
- }\r
-\r
- return result;\r
-}\r
-\r
-static BOOL OneMoveAppended()\r
-{\r
- BOOL result = FALSE;\r
-\r
- if( lastCurrent >= 0 && lastCurrent >= lastFirst && lastLast >= lastFirst &&\r
- currCurrent >= 0 && currCurrent >= currFirst && currLast >= currFirst &&\r
- lastFirst == currFirst &&\r
- lastLast == (currLast-1) &&\r
- lastCurrent == (currCurrent-1) &&\r
- currCurrent == (currLast-1) &&\r
- TRUE )\r
- {\r
- result = TRUE;\r
- }\r
-\r
- return result;\r
}\r
\r
-static VOID ClearMemo()\r
+// low-level front-end, but replace Windows data types to make it callable from back-end\r
+// its task is to clear the contents of the move-history text edit\r
+void ClearHistoryMemo()\r
{\r
SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, WM_SETTEXT, 0, (LPARAM) "" );\r
}\r
\r
-static int AppendToMemo( char * text, DWORD flags, DWORD color )\r
+// low-level front-end, made callable from back-end by passing flags and color numbers\r
+// its task is to append the given text to the text edit\r
+// the bold argument says 0 = normal, 1 = bold typeface\r
+// the colorNr argument says 0 = font-default, 1 = gray\r
+int AppendToHistoryMemo( char * text, int bold, int colorNr )\r
{\r
CHARFORMAT cf;\r
+ DWORD flags = bold ? CFE_BOLD :0;\r
+ DWORD color = colorNr ? GetSysColor(COLOR_GRAYTEXT) : DEFAULT_COLOR;\r
\r
HWND hMemo = GetDlgItem( moveHistoryDialog, IDC_MoveHistory );\r
\r
return cbTextLen;\r
}\r
\r
-static VOID AppendMoveToMemo( int index )\r
-{\r
- char buf[64];\r
- DWORD flags = 0;\r
- DWORD color = DEFAULT_COLOR;\r
-\r
- if( index < 0 || index >= MAX_MOVES ) {\r
- return;\r
- }\r
-\r
- buf[0] = '\0';\r
-\r
- /* Move number */\r
- if( (index % 2) == 0 ) {\r
- sprintf( buf, "%d.%s ", (index / 2)+1, index & 1 ? ".." : "" );\r
- AppendToMemo( buf, CFE_BOLD, DEFAULT_COLOR );\r
- }\r
-\r
- /* Move text */\r
- strcpy( buf, SavePart( currMovelist[index] ) );\r
- strcat( buf, " " );\r
-\r
- histMoves[index].memoOffset = AppendToMemo( buf, flags, color );\r
- histMoves[index].memoLength = strlen(buf)-1;\r
-\r
- /* PV info (if any) */\r
- if( appData.showEvalInMoveHistory && currPvInfo[index].depth > 0 ) {\r
- sprintf( buf, "{%s%.2f/%d} ", \r
- currPvInfo[index].score >= 0 ? "+" : "",\r
- currPvInfo[index].score / 100.0,\r
- currPvInfo[index].depth );\r
-\r
- AppendToMemo( buf, flags, \r
- color == DEFAULT_COLOR ? GetSysColor(COLOR_GRAYTEXT) : color );\r
- }\r
-}\r
-\r
-static void RefreshMemoContent()\r
+// low-level front-end; wrapper for the code to scroll the mentioned character in view (-1 = end)\r
+void ScrollToCurrent(int caretPos)\r
{\r
- int i;\r
-\r
- ClearMemo();\r
-\r
- for( i=currFirst; i<currLast; i++ ) {\r
- AppendMoveToMemo( i );\r
- }\r
-}\r
-\r
-static void MemoContentUpdated()\r
-{\r
- int caretPos;\r
-\r
- HighlightMove( lastCurrent, FALSE );\r
- HighlightMove( currCurrent, TRUE );\r
-\r
- lastFirst = currFirst;\r
- lastLast = currLast;\r
- lastCurrent = currCurrent;\r
- lastLastMove[0] = '\0';\r
-\r
- if( lastLast > 0 ) {\r
- strcpy( lastLastMove, SavePart( currMovelist[lastLast-1] ) );\r
- }\r
-\r
- /* Deselect any text, move caret to end of memo */\r
- if( currCurrent >= 0 ) {\r
- caretPos = histMoves[currCurrent].memoOffset + histMoves[currCurrent].memoLength;\r
- }\r
- else {\r
+ if(caretPos < 0)\r
caretPos = (int) SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, WM_GETTEXTLENGTH, 0, 0 );\r
- }\r
-\r
SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SETSEL, caretPos, caretPos );\r
\r
SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SCROLLCARET, 0, 0 );\r
}\r
\r
-int FindMoveByCharIndex( int char_index )\r
-{\r
- int index;\r
-\r
- for( index=currFirst; index<currLast; index++ ) {\r
- if( char_index >= histMoves[index].memoOffset &&\r
- char_index < (histMoves[index].memoOffset + histMoves[index].memoLength) )\r
- {\r
- return index;\r
- }\r
- }\r
\r
- return -1;\r
-}\r
+// ------------------------------ call backs --------------------------\r
\r
+// front-end. Universal call-back for any event. Recognized vents are dialog creation, OK and cancel button-press\r
+// (dead code, as these buttons do not exist?), mouse clicks on the text edit, and moving / sizing\r
LRESULT CALLBACK HistoryDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )\r
{\r
static SnapData sd;\r
\r
index = SendDlgItemMessage( hDlg, IDC_MoveHistory, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
\r
- index = FindMoveByCharIndex( index );\r
-\r
- if( index >= 0 ) {\r
- ToNrEvent( index + 1 );\r
- }\r
+ FindMoveByCharIndex( index ); // [HGM] also does the actual moving to it, now\r
\r
/* Zap the message for good: apparently, returning non-zero is not enough */\r
lpMF->msg = WM_USER;\r
}\r
break;\r
\r
- case WM_REFRESH_HISTORY:\r
- /* Update the GUI */\r
- if( OnlyCurrentPositionChanged() ) {\r
- /* Only "cursor" changed, no need to update memo content */\r
- }\r
- else if( OneMoveAppended() ) {\r
- AppendMoveToMemo( currCurrent );\r
- }\r
- else {\r
- RefreshMemoContent();\r
- }\r
-\r
- MemoContentUpdated();\r
-\r
- break;\r
-\r
case WM_SIZE:\r
SetWindowPos( GetDlgItem( moveHistoryDialog, IDC_MoveHistory ),\r
HWND_TOP,\r
return FALSE;\r
}\r
\r
+// ------------ standard entry points into MoveHistory code -----------\r
+\r
+// front-end\r
VOID MoveHistoryPopUp()\r
{\r
FARPROC lpProc;\r
}\r
\r
moveHistoryDialogUp = TRUE;\r
+\r
+// Note that in WIndows creating the dialog causes its call-back to perform\r
+// RefreshMemoContent() and MemoContentUpdated() immediately after it is realized.\r
+// To port this to X we might have to do that from here.\r
}\r
\r
+// front-end\r
VOID MoveHistoryPopDown()\r
{\r
CheckMenuItem(GetMenu(hwndMain), IDM_ShowMoveHistory, MF_UNCHECKED);\r
moveHistoryDialogUp = FALSE;\r
}\r
\r
-VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo )\r
+// front-end\r
+Boolean MoveHistoryIsUp()\r
{\r
- /* [AS] Danger! For now we rely on the movelist parameter being a static variable! */\r
-\r
- currMovelist = movelist;\r
- currFirst = first;\r
- currLast = last;\r
- currCurrent = current;\r
- currPvInfo = pvInfo;\r
-\r
- if( moveHistoryDialog ) {\r
- SendMessage( moveHistoryDialog, WM_REFRESH_HISTORY, 0, 0 );\r
- }\r
+ return moveHistoryDialogUp;\r
}\r
\r
-Boolean MoveHistoryIsUp()\r
+// front-end\r
+Boolean MoveHistoryDialogExists()\r
{\r
- return moveHistoryDialogUp;\r
+ return moveHistoryDialog != NULL;\r
}\r
Boolean chessProgram;\r
//static int boardX, boardY;\r
int minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
-static int squareSize, lineGap, minorSize;\r
+int squareSize, lineGap, minorSize;\r
static int winW, winH;\r
static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
static int logoHeight = 0;\r
blackRect.right = blackRect.left + boardWidth/2 - 1;\r
blackRect.top = whiteRect.top;\r
blackRect.bottom = whiteRect.bottom;\r
+\r
+ logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
}\r
\r
messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
DeleteDC(tmphdc);\r
}\r
\r
+static HDC hdcSeek;\r
+\r
+// [HGM] seekgraph\r
+void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
+{\r
+ POINT stPt;\r
+ HPEN hp = SelectObject( hdcSeek, gridPen );\r
+ MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
+ LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
+ SelectObject( hdcSeek, hp );\r
+}\r
+\r
+// front-end wrapper for drawing functions to do rectangles\r
+void DrawSeekBackground( int left, int top, int right, int bottom )\r
+{\r
+ HPEN hp;\r
+ RECT rc;\r
+\r
+ if (hdcSeek == NULL) {\r
+ hdcSeek = GetDC(hwndMain);\r
+ if (!appData.monoMode) {\r
+ SelectPalette(hdcSeek, hPal, FALSE);\r
+ RealizePalette(hdcSeek);\r
+ }\r
+ }\r
+ hp = SelectObject( hdcSeek, gridPen );\r
+ rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
+ rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
+ FillRect( hdcSeek, &rc, lightSquareBrush );\r
+ SelectObject( hdcSeek, hp );\r
+}\r
+\r
+// front-end wrapper for putting text in graph\r
+void DrawSeekText(char *buf, int x, int y)\r
+{\r
+ SIZE stSize;\r
+ SetBkMode( hdcSeek, TRANSPARENT );\r
+ GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
+ TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
+}\r
+\r
+void DrawSeekDot(int x, int y, int color)\r
+{\r
+ int square = color & 0x80;\r
+ color &= 0x7F;\r
+ HBRUSH oldBrush = SelectObject(hdcSeek, \r
+ color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
+ if(square)\r
+ Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
+ boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
+ else\r
+ Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
+ boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
+ SelectObject(hdcSeek, oldBrush);\r
+}\r
+\r
VOID\r
HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
{\r
*/\r
Boolean fullrepaint = repaint;\r
\r
+ if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
+\r
if( DrawPositionNeedsFullRepaint() ) {\r
fullrepaint = TRUE;\r
}\r
VOID\r
MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
{\r
- int x, y;\r
+ int x, y, menuNr;\r
POINT pt;\r
static int recursive = 0;\r
HMENU hmenu;\r
break;\r
\r
case WM_MOUSEMOVE:\r
- 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;\r
+ MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
if ((appData.animateDragging || appData.highlightDragging)\r
&& (wParam & MK_LBUTTON)\r
&& dragInfo.from.x >= 0) \r
}\r
break;\r
\r
- case WM_MBUTTONUP:
- case WM_RBUTTONUP:
- ReleaseCapture();
- UnLoadPV();
- break;
-
+ case WM_MBUTTONUP:\r
+ case WM_RBUTTONUP:\r
+ ReleaseCapture();\r
+ RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
+ break;\r
+ \r
case WM_MBUTTONDOWN:\r
case WM_RBUTTONDOWN:\r
ErrorPopDown();\r
}\r
DrawPosition(TRUE, NULL);\r
\r
- switch (gameMode) {\r
- case IcsExamining:\r
- if(x < BOARD_LEFT || x >= BOARD_RGHT) break;\r
- case EditPosition:\r
- if (x == BOARD_LEFT-1 || x == BOARD_RGHT) break;\r
- if (x < 0 || y < 0) break;\r
- fromX = x;\r
- fromY = y;\r
+ menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
+ switch (menuNr) {\r
+ case 0:\r
if (message == WM_MBUTTONDOWN) {\r
buttonCount = 3; /* even if system didn't think so */\r
if (wParam & MK_SHIFT) \r
MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
}\r
break;\r
- 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;\r
- fromY = y;\r
+ case 2:\r
+ SetCapture(hwndMain);
+ break;\r
+ case 1:\r
hmenu = LoadMenu(hInst, "DropPieceMenu");\r
SetupDropMenu(hmenu);\r
MenuPopup(hwnd, pt, hmenu, -1);\r
- break;\r
default:\r
break;\r
}\r
return result;\r
}\r
\r
-/* [AS] Game list options */\r
-typedef struct {\r
- char id;\r
- char * name;\r
-} GLT_Item;\r
-\r
-static GLT_Item GLT_ItemInfo[] = {\r
- { GLT_EVENT, "Event" },\r
- { GLT_SITE, "Site" },\r
- { GLT_DATE, "Date" },\r
- { GLT_ROUND, "Round" },\r
- { GLT_PLAYERS, "Players" },\r
- { GLT_RESULT, "Result" },\r
- { GLT_WHITE_ELO, "White Rating" },\r
- { GLT_BLACK_ELO, "Black Rating" },\r
- { GLT_TIME_CONTROL,"Time Control" },\r
- { GLT_VARIANT, "Variant" },\r
- { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
- { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
- { 0, 0 }\r
-};\r
+/* [AS] Game list options. Refactored by HGM */\r
\r
-const char * GLT_FindItem( char id )\r
-{\r
- const char * result = 0;\r
-\r
- GLT_Item * list = GLT_ItemInfo;\r
-\r
- while( list->id != 0 ) {\r
- if( list->id == id ) {\r
- result = list->name;\r
- break;\r
- }\r
+HWND gameListOptionsDialog;\r
\r
- list++;\r
- }\r
-\r
- return result;\r
+// low-level front-end: clear text edit / list widget\r
+void\r
+GLT_ClearList()\r
+{\r
+ SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
}\r
\r
-void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
+// low-level front-end: clear text edit / list widget\r
+void\r
+GLT_DeSelectList()\r
{\r
- const char * name = GLT_FindItem( id );\r
-\r
- if( name != 0 ) {\r
- if( index >= 0 ) {\r
- SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
- }\r
- else {\r
- SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
- }\r
- }\r
+ SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
}\r
\r
-void GLT_TagsToList( HWND hDlg, char * tags )\r
+// low-level front-end: append line to text edit / list widget\r
+void\r
+GLT_AddToList( char *name )\r
{\r
- char * pc = tags;\r
-\r
- SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
-\r
- while( *pc ) {\r
- GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
- pc++;\r
- }\r
-\r
- SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
-\r
- pc = GLT_ALL_TAGS;\r
-\r
- while( *pc ) {\r
- if( strchr( tags, *pc ) == 0 ) {\r
- GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
- }\r
- pc++;\r
+ if( name != 0 ) {\r
+ SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
}\r
-\r
- SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
}\r
\r
-char GLT_ListItemToTag( HWND hDlg, int index )\r
+// low-level front-end: get line from text edit / list widget\r
+Boolean\r
+GLT_GetFromList( int index, char *name )\r
{\r
- char result = '\0';\r
- char name[128];\r
-\r
- GLT_Item * list = GLT_ItemInfo;\r
-\r
- if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
- while( list->id != 0 ) {\r
- if( strcmp( list->name, name ) == 0 ) {\r
- result = list->id;\r
- break;\r
- }\r
-\r
- list++;\r
- }\r
+ if( name != 0 ) {\r
+ if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
+ return TRUE;\r
}\r
-\r
- return result;\r
+ return FALSE;\r
}\r
\r
void GLT_MoveSelection( HWND hDlg, int delta )\r
\r
LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
{\r
- static char glt[64];\r
- static char * lpUserGLT;\r
-\r
switch( message )\r
{\r
case WM_INITDIALOG:\r
- lpUserGLT = (char *) lParam;\r
+ gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
\r
- strcpy( glt, lpUserGLT );\r
-\r
CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
\r
/* Initialize list */\r
- GLT_TagsToList( hDlg, glt );\r
+ GLT_TagsToList( lpUserGLT );\r
\r
SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
\r
case WM_COMMAND:\r
switch( LOWORD(wParam) ) {\r
case IDOK:\r
- {\r
- char * pc = lpUserGLT;\r
- int idx = 0;\r
-// int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
- char id;\r
-\r
- do {\r
- id = GLT_ListItemToTag( hDlg, idx );\r
-\r
- *pc++ = id;\r
- idx++;\r
- } while( id != '\0' );\r
- }\r
+ GLT_ParseList();\r
EndDialog( hDlg, 0 );\r
return TRUE;\r
case IDCANCEL:\r
return TRUE;\r
\r
case IDC_GLT_Default:\r
- strcpy( glt, GLT_DEFAULT_TAGS );\r
- GLT_TagsToList( hDlg, glt );\r
+ GLT_TagsToList( GLT_DEFAULT_TAGS );\r
return TRUE;\r
\r
case IDC_GLT_Restore:\r
- strcpy( glt, lpUserGLT );\r
- GLT_TagsToList( hDlg, glt );\r
+ GLT_TagsToList( appData.gameListTags );\r
return TRUE;\r
\r
case IDC_GLT_Up:\r
\r
int GameListOptions()\r
{\r
- char glt[64];\r
int result;\r
FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
\r
- strcpy( glt, appData.gameListTags );\r
+ strcpy( lpUserGLT, appData.gameListTags );\r
\r
- result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
+ result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
\r
if( result == 0 ) {\r
/* [AS] Memory leak here! */\r
- appData.gameListTags = strdup( glt ); \r
+ appData.gameListTags = strdup( lpUserGLT ); \r
}\r
\r
return result;\r
}\r
\r
-\r
VOID\r
DisplayIcsInteractionTitle(char *str)\r
{\r
CONTROL "Periodic Updates (for Analysis Mode)",\r
IDC_EpPeriodicUpdates,"Button",BS_AUTOCHECKBOX | \r
WS_TABSTOP,4,40,131,10\r
- GROUPBOX "Engine-engine matches",IDC_STATIC,4,56,200,98\r
+ GROUPBOX "Adjudications in non-ICS games",IDC_STATIC,4,56,200,98\r
LTEXT "Adjudicate draw after:",IDC_STATIC,10,72,70,8\r
EDITTEXT IDC_EpDrawMoveCount,116,68,40,14,ES_AUTOHSCROLL\r
LTEXT "moves",IDC_STATIC,158,72,22,8\r
-/*
+Xg/*
* xboard.c -- X front end for XBoard
*
* Copyright 1991 by Digital Equipment Corporation, Maynard,
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(());
{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},
// { "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 },
// { "FileNamePopDown", (XtActionProc) FileNamePopDown },
{ "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
{ "GameListPopDown", (XtActionProc) GameListPopDown },
+ { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
{ "PromotionPopDown", (XtActionProc) PromotionPopDown },
// { "HistoryPopDown", (XtActionProc) HistoryPopDown },
{ "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
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 && size<MAX_SIZE) { // for now, fixed limit
+ fontTable[number][size] = strdup(strchr(name, ':')+1);
+ fontValid[number][size] = True;
+ }
+ return;
+ }
switch(number) {
case 0: // CLOCK_FONT
appData.clockFont = strdup(name);
default:
return;
}
+ fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
}
void
void
SaveFontArg(FILE *f, ArgDescriptor *ad)
{
- char *name;
- switch((int)ad->argLoc) {
+ char *name, buf[MSG_SIZ];
+ int i, n = (int)ad->argLoc;
+ switch(n) {
case 0: // CLOCK_FONT
name = appData.clockFont;
break;
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; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
+ if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
+ fontTable[n][squareSize] = strdup(name);
+ fontValid[n][squareSize] = True;
+ break;
+ }
+ for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
+ fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]);
}
void
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) {
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));
}
{
drawHighlight(hi1X, hi1Y, LINE_TYPE_NORMAL);
}
- if (fromX >= 0 && fromY >= 0)
- {
- drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT);
- }
}
if (hi2X != toX || hi2Y != 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);
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);
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];
/*
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;
#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));
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[] =
- "<Btn1Up>(2): LoadSelectedProc() \n \
- <Key>Return: LoadSelectedProc() \n";
+ "<Btn1Up>(2): LoadSelectedProc(0) \n \
+ <Key>Home: LoadSelectedProc(-2) \n \
+ <Key>End: LoadSelectedProc(2) \n \
+ <Key>Up: LoadSelectedProc(-1) \n \
+ <Key>Down: LoadSelectedProc(1) \n \
+ <Key>Left: LoadSelectedProc(-1) \n \
+ <Key>Right: LoadSelectedProc(1) \n \
+ <Key>Return: LoadSelectedProc(0) \n";
+char filterTranslations[] =
+ "<Key>Return: SetFilterProc() \n";
typedef struct {
Widget shell;
char *filename;
char **strings;
} GameListClosure;
+static GameListClosure *glc = NULL;
static Arg layoutArgs[] = {
{ XtNborderWidth, 0 },
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;
}
} 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;
}
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 {
}
}
-static GameListClosure *glc = NULL;
-
void
GameListPopUp(fp, filename)
FILE *fp;
lg = (ListGame *) lg->node.succ;
}
-
/* show widget */
gtk_widget_show (GUI_GameList);
{
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 {
}
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 */
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)<index) st++,i++;
+ XawListHighlight(listwidg, i);
}
Boolean
return gtk_widget_get_visible (GUI_GameList);
}
+
+//--------------------------------- Game-List options dialog ------------------------------------------
+
+Widget gameListOptShell, listwidg;
+
+char *strings[20];
+int stringPtr;
+
+void GLT_ClearList()
+{
+ strings[0] = NULL;
+ stringPtr = 0;
+}
+
+void GLT_AddToList(char *name)
+{
+ strings[stringPtr++] = name;
+ strings[stringPtr] = NULL;
+}
+
+Boolean GLT_GetFromList(int index, char *name)
+{
+ strcpy(name, strings[index]);
+}
+
+void GLT_DeSelectList()
+{
+ XawListChange(listwidg, strings, 0, 0, True);
+ XawListHighlight(listwidg, 0);
+}
+
+void
+GameListOptionsPopDown()
+{
+ Arg args[16];
+ int j;
+
+ if (gameListOptShell == NULL) return;
+ XtPopdown(gameListOptShell);
+ XtDestroyWidget(gameListOptShell);
+ gameListOptShell = 0;
+ XtSetKeyboardFocus(shellWidget, formWidget);
+}
+
+void
+GameListOptionsCallback(w, client_data, call_data)
+ Widget w;
+ XtPointer client_data, call_data;
+{
+ String name;
+ Arg args[16];
+ int j;
+ Widget listwidg;
+ XawListReturnStruct *rs;
+ int index;
+ char *p;
+
+ j = 0;
+ XtSetArg(args[j], XtNlabel, &name); j++;
+ XtGetValues(w, args, j);
+
+ if (strcmp(name, _("OK")) == 0) {
+ GLT_ParseList();
+ appData.gameListTags = strdup(lpUserGLT);
+ GameListOptionsPopDown();
+ return;
+ } else
+ if (strcmp(name, _("cancel")) == 0) {
+ GameListOptionsPopDown();
+ return;
+ }
+ listwidg = XtNameToWidget(gameListOptShell, "*form.list");
+ rs = XawListShowCurrent(listwidg);
+ index = rs->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);
+}
+
+
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 */
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
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
}
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
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);
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++;
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++;
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);
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);