changes from Alessandro Scotti from 20051231
authorA. Scotti <dev@ascotti.org>
Thu, 16 Apr 2009 21:04:02 +0000 (14:04 -0700)
committerArun Persaud <arun@nubati.net>
Thu, 16 Apr 2009 21:04:02 +0000 (14:04 -0700)
18 files changed:
backend.c
backend.h
common.h
frontend.h
gamelist.c
pgntags.c
winboard/wengineoutput.c [new file with mode: 0644]
winboard/wevalgraph.c [new file with mode: 0644]
winboard/wgamelist.c
winboard/whistory.c [new file with mode: 0644]
winboard/winboard.c
winboard/winboard.h
winboard/winboard.rc
winboard/wlayout.c [new file with mode: 0644]
winboard/woptions.c
winboard/woptions.h
winboard/wsnap.c [new file with mode: 0644]
winboard/wsnap.h [new file with mode: 0644]

index b347a3e..d545851 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -47,7 +47,7 @@
  *
  * See the file ChangeLog for a revision history.  */
 
-/* [AS] For debugging purposes */
+/* [AS] Also useful here for debugging */
 #ifdef WIN32
 #include <windows.h>
 
@@ -144,12 +144,6 @@ typedef struct {
   int seen_stat;          /* 1 if we've seen the stat01: line */
 } ChessProgramStats;
 
-/* [AS] Search stats from chessprogram, for the played move */
-typedef struct {
-    int score;
-    int depth;
-} ChessProgramStats_Move;
-
 int establish P((void));
 void read_from_player P((InputSourceRef isr, VOIDSTAR closure,
                         char *buf, int count, int error));
@@ -222,6 +216,8 @@ void InitBackEnd3 P((void));
 void FeatureDone P((ChessProgramState* cps, int val));
 void InitChessProgram P((ChessProgramState *cps));
 
+void GetInfoFromComment( int, char * );
+
 extern int tinyLayout, smallLayout;
 static ChessProgramStats programStats;
 
@@ -2905,6 +2901,7 @@ ParseBoard12(string)
        movesPerSession = 0;
        gameInfo.timeControl = TimeControlTagValue();
        gameInfo.variant = StringToVariant(gameInfo.event);
+        gameInfo.outOfBook = NULL;
        
        /* Do we have the ratings? */
        if (strcmp(player1Name, white) == 0 &&
@@ -3573,7 +3570,7 @@ InitPosition(redraw)
     }
 
     if (redraw)
-      DrawPosition(FALSE, boards[currentMove]);
+      DrawPosition(TRUE, boards[currentMove]);
 }
 
 void
@@ -4037,6 +4034,16 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
   }
 }
 
+void SendProgramStatsToFrontend( ChessProgramState * cps )
+{
+    SetProgramStats( cps == &first ? 0 : 1,
+        programStats.depth,
+        programStats.nodes,
+        programStats.score,
+        programStats.time,
+        programStats.movelist );
+}
+
 void
 HandleMachineMove(message, cps)
      char *message;
@@ -4080,11 +4087,9 @@ HandleMachineMove(message, cps)
     /*
      * Look for machine move.
      */
-    if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 &&
-        strcmp(buf2, "...") == 0) ||
-       (sscanf(message, "%s %s", buf1, machineMove) == 2 &&
-        strcmp(buf1, "move") == 0)) {
-
+    if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 && strcmp(buf2, "...") == 0) ||
+       (sscanf(message, "%s %s", buf1, machineMove) == 2 && strcmp(buf1, "move") == 0))
+    {
         /* This method is only useful on engines that support ping */
         if (cps->lastPing != cps->lastPong) {
          if (gameMode == BeginningOfGame) {
@@ -4184,6 +4189,7 @@ HandleMachineMove(message, cps)
         /* [AS] Save move info and clear stats for next move */
         pvInfoList[ forwardMostMove ].score = programStats.score;
         pvInfoList[ forwardMostMove ].depth = programStats.depth;
+        pvInfoList[ forwardMostMove ].time = -1;
         ClearProgramStats();
         thinkOutput[0] = NULLCHAR;
         hiddenThinkOutputState = 0;
@@ -4219,6 +4225,14 @@ HandleMachineMove(message, cps)
             }
         }
 
+        if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) {
+           ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+
+            GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD );
+
+            return;
+        }
+
        if (gameMode == TwoMachinesPlay) {
            if (cps->other->sendTime) {
                SendTimeRemaining(cps->other,
@@ -4702,19 +4716,21 @@ HandleMachineMove(message, cps)
                if (plyext != ' ' && plyext != '\t') {
                    time *= 100;
                }
-               programStats.depth = plylev;
-               programStats.nodes = nodes;
-               programStats.time = time;
-               programStats.score = curscore;
-               programStats.got_only_move = 0;
 
-                /* [AS] Negate score if machine is playing black and it's reporting absolute scores */
+                /* [AS] Negate score if machine is playing black and reporting absolute scores */
                 if( cps->scoreIsAbsolute &&
                     ((gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b')) )
                 {
-                    programStats.score = -curscore;
+                    curscore = -curscore;
                 }
 
+
+               programStats.depth = plylev;
+               programStats.nodes = nodes;
+               programStats.time = time;
+               programStats.score = curscore;
+               programStats.got_only_move = 0;
+
                /* Buffer overflow protection */
                if (buf1[0] != NULLCHAR) {
                    if (strlen(buf1) >= sizeof(programStats.movelist)
@@ -4741,6 +4757,8 @@ HandleMachineMove(message, cps)
                    programStats.line_is_book = 0;
                }
                  
+                SendProgramStatsToFrontend( cps );
+
                 /*
                     [AS] Protect the thinkOutput buffer from overflow... this
                     is only useful if buf1 hasn't overflowed first!
@@ -4791,6 +4809,8 @@ HandleMachineMove(message, cps)
                   isn't searching, so stats won't change) */
                programStats.line_is_book = 1;
                  
+                SendProgramStatsToFrontend( cps );
+
                if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) {
                    DisplayMove(currentMove - 1);
                    DisplayAnalysis();
@@ -4814,6 +4834,9 @@ HandleMachineMove(message, cps)
                programStats.nr_moves = mvtot;
                strcpy(programStats.move_name, mvname);
                programStats.ok_to_send = 1;
+
+                SendProgramStatsToFrontend( cps );
+
                DisplayAnalysis();
                return;
 
@@ -5262,6 +5285,7 @@ ShowMove(fromX, fromY, toX, toY)
     }
 
     if (instant) return;
+
     DisplayMove(currentMove - 1);
     DrawPosition(FALSE, boards[currentMove]);
     DisplayBothClocks();
@@ -5812,6 +5836,10 @@ AutoPlayOneMove()
     if (currentMove >= forwardMostMove) {
       gameMode = EditGame;
       ModeHighlight();
+
+      /* [AS] Clear current move marker at the end of a game */
+      /* HistorySet(parseList, backwardMostMove, forwardMostMove, -1); */
+
       return FALSE;
     }
     
@@ -5825,6 +5853,9 @@ AutoPlayOneMove()
     } else {
        fromX = moveList[currentMove][0] - 'a';
        fromY = moveList[currentMove][1] - '1';
+
+        HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove); /* [AS] */
+
        AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
 
        if (appData.highlightLastMove) {
@@ -6546,10 +6577,12 @@ LoadGame(f, gameNumber, title, useList)
          gameInfo.variant = StringToVariant(gameInfo.event);
        }
        if (!matchMode) {
+          if( appData.autoDisplayTags ) {
          tags = PGNTags(&gameInfo);
          TagsPopUp(tags, CmailMsg());
          free(tags);
        }
+       }
     } else {
        /* Make something up, but don't display it now */
        SetGameInfo();
@@ -6951,6 +6984,80 @@ SavePart(str)
 
 #define PGN_MAX_LINE 75
 
+#define PGN_SIDE_WHITE  0
+#define PGN_SIDE_BLACK  1
+
+static int FindFirstMoveOutOfBook( int side )
+{
+    int result = -1;
+
+    if( backwardMostMove == 0 && ! startedFromSetupPosition) {
+        int index = backwardMostMove;
+        int has_book_hit = 0;
+
+        if( (index % 2) != side ) {
+            index++;
+        }
+
+        while( index < forwardMostMove ) {
+            /* Check to see if engine is in book */
+            int depth = pvInfoList[index].depth;
+            int score = pvInfoList[index].score;
+            int in_book = 0;
+
+            if( depth == 0 ) {
+                in_book = 1; /* Yace */
+            }
+            if( score == 0 ) {
+                if( depth <= 1 || depth == 63 /* Zappa */ ) {
+                    in_book = 1;
+                }
+            }
+
+            has_book_hit += in_book;
+
+            if( ! in_book ) {
+                result = index;
+
+                break;
+            }
+
+            index += 2;
+        }
+    }
+
+    return result;
+}
+
+void GetOutOfBookInfo( char * buf )
+{
+    int oob[2];
+    int i;
+    int offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
+
+    oob[0] = FindFirstMoveOutOfBook( PGN_SIDE_WHITE );
+    oob[1] = FindFirstMoveOutOfBook( PGN_SIDE_BLACK );
+
+    *buf = '\0';
+
+    if( oob[0] >= 0 || oob[1] >= 0 ) {
+        for( i=0; i<2; i++ ) {
+            int idx = oob[i];
+
+            if( idx >= 0 ) {
+                if( i > 0 && oob[0] >= 0 ) {
+                    strcat( buf, "   " );
+                }
+
+                sprintf( buf+strlen(buf), "%d%s. ", (idx - offset)/2 + 1, idx & 1 ? ".." : "" );
+                sprintf( buf+strlen(buf), "%s%.2f",
+                    pvInfoList[idx].score >= 0 ? "+" : "",
+                    pvInfoList[idx].score / 100.0 );
+            }
+        }
+    }
+}
+
 /* Save game in PGN style and close the file */
 int
 SaveGamePGN(f)
@@ -6963,6 +7070,8 @@ SaveGamePGN(f)
     int movelen, numlen, blank;
     char move_buffer[100]; /* [AS] Buffer for move+PV info */
     
+    offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
+
     tm = time((time_t *) NULL);
     
     PrintPGNTags(f, &gameInfo);
@@ -6974,12 +7083,23 @@ SaveGamePGN(f)
        PrintPosition(f, backwardMostMove);
        fprintf(f, "--------------}\n");
         free(fen);
-    } else {
+    }
+    else {
+        /* [AS] Out of book annotation */
+        if( appData.saveOutOfBookInfo ) {
+            char buf[64];
+
+            GetOutOfBookInfo( buf );
+
+            if( buf[0] != '\0' ) {
+                fprintf( f, "[%s \"%s\"]\n", PGN_OUT_OF_BOOK, buf );
+            }
+        }
+
        fprintf(f, "\n");
     }
 
     i = backwardMostMove;
-    offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
     linelen = 0;
     newblock = TRUE;
 
@@ -8204,6 +8324,7 @@ EditPositionDone()
     gameMode = EditGame;
     ModeHighlight();
     HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
+    ClearHighlights(); /* [AS] */
 }
 
 /* Pause for `ms' milliseconds */
@@ -8711,6 +8832,8 @@ void
 BackwardInner(target)
      int target;
 {
+    int full_redraw = TRUE; /* [AS] Was FALSE, had to change it! */
+
     if (appData.debugMode)
        fprintf(debugFP, "BackwardInner(%d), current %d, forward %d\n",
                target, currentMove, forwardMostMove);
@@ -8718,7 +8841,7 @@ BackwardInner(target)
     if (gameMode == EditPosition) return;
     if (currentMove <= backwardMostMove) {
        ClearHighlights();
-       DrawPosition(FALSE, boards[currentMove]);
+       DrawPosition(full_redraw, boards[currentMove]);
        return;
     }
     if (gameMode == PlayFromGameFile && !pausing)
@@ -8759,7 +8882,7 @@ BackwardInner(target)
     }
     DisplayBothClocks();
     DisplayMove(currentMove - 1);
-    DrawPosition(FALSE, boards[currentMove]);
+    DrawPosition(full_redraw, boards[currentMove]);
     HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
     if (commentList[currentMove] != NULL) {
        DisplayComment(currentMove - 1, commentList[currentMove]);
@@ -8851,7 +8974,7 @@ RetractMoveEvent()
        DisplayBothClocks();
        DisplayMove(currentMove - 1);
        ClearHighlights();/*!! could figure this out*/
-       DrawPosition(FALSE, boards[currentMove]);
+       DrawPosition(TRUE, boards[currentMove]); /* [AS] Changed to full redraw! */
        SendToProgram("remove\n", &first);
        /*first.maybeThinking = TRUE;*/ /* GNU Chess does not ponder here */
        break;
@@ -9210,6 +9333,8 @@ AppendComment(index, text)
     int oldlen, len;
     char *old;
 
+    GetInfoFromComment( index, text );
+
     CrushCRs(text);
     while (*text == '\n') text++;
     len = strlen(text);
@@ -9234,6 +9359,78 @@ AppendComment(index, text)
     }
 }
 
+static char * FindStr( char * text, char * sub_text )
+{
+    char * result = strstr( text, sub_text );
+
+    if( result != NULL ) {
+        result += strlen( sub_text );
+    }
+
+    return result;
+}
+
+/* [AS] Try to extract PV info from PGN comment */
+void GetInfoFromComment( int index, char * text )
+{
+    if( text != NULL && index > 0 ) {
+        int score = 0;
+        int depth = 0;
+        int time = -1;
+        char * s_eval = FindStr( text, "[%eval " );
+        char * s_emt = FindStr( text, "[%emt " );
+
+        if( s_eval != NULL || s_emt != NULL ) {
+            /* New style */
+            char delim;
+
+            if( s_eval != NULL ) {
+                if( sscanf( s_eval, "%d,%d%c", &score, &depth, &delim ) != 3 ) {
+                    return;
+                }
+
+                if( delim != ']' ) {
+                    return;
+                }
+            }
+
+            if( s_emt != NULL ) {
+            }
+        }
+        else {
+            /* We expect something like: [+|-]nnn.nn/dd */
+            char * sep = strchr( text, '/' );
+            int score_lo = 0;
+
+            if( sep == NULL || sep < (text+4) ) {
+                return;
+            }
+
+            if( sscanf( text, "%d.%d/%d", &score, &score_lo, &depth ) != 3 ) {
+                return;
+            }
+
+            if( score_lo < 0 || score_lo >= 100 ) {
+                return;
+            }
+
+            score = score >= 0 ? score*100 + score_lo : score*100 - score_lo;
+        }
+
+        if( depth <= 0 ) {
+            return;
+        }
+
+        if( time < 0 ) {
+            time = -1;
+        }
+
+        pvInfoList[index-1].depth = depth;
+        pvInfoList[index-1].score = score;
+        pvInfoList[index-1].time = time;
+    }
+}
+
 void
 SendToProgram(message, cps)
      char *message;
@@ -9763,6 +9960,7 @@ DisplayComment(moveNumber, text)
 {
     char title[MSG_SIZ];
 
+    if( appData.autoDisplayComment ) {
     if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
        strcpy(title, "Comment");
     } else {
@@ -9772,6 +9970,7 @@ DisplayComment(moveNumber, text)
     }
 
     CommentPopUp(title, text);
+    }
 }
 
 /* This routine sends a ^C interrupt to gnuchess, to awaken it if it
index 9d74c57..b08a092 100644 (file)
--- a/backend.h
+++ b/backend.h
@@ -196,11 +196,11 @@ void ClearGameInfo P((GameInfo *));
 int GameListBuild P((FILE *));
 void GameListInitGameInfo P((GameInfo *));
 char *GameListLine P((int, GameInfo *));
+char * GameListLineFull P(( int, GameInfo *));
 
 extern char* StripHighlight P((char *));  /* returns static data */
 extern char* StripHighlightAndTitle P((char *));  /* returns static data */
 
-
 typedef struct _CPS {
     char *which;
     int maybeThinking;
@@ -241,12 +241,21 @@ typedef struct _CPS {
 
     /* Added by Tord: */
     int useFEN960;   /* 0=use "KQkq" style FENs, 1=use "HAha" style FENs */
-    int useOOCastle; /* 0="O-O" notation for castling, 1="king capture rook"
-                     * notation */
+    int useOOCastle; /* 0="O-O" notation for castling, 1="king capture rook" notation */
     /* End of additions by Tord */
+
     int scoreIsAbsolute; /* [AS] 0=don't know (standard), 1=score is always from white side */
 } ChessProgramState;
 
 extern ChessProgramState first, second;
 
+/* [AS] Search stats from chessprogram, for the played move */
+typedef struct {
+    int score;  /* Centipawns */
+    int depth;  /* Plies */
+    int time;   /* Milliseconds */
+} ChessProgramStats_Move;
+
+extern ChessProgramStats_Move pvInfoList[MAX_MOVES];
+
 #endif /* _BACKEND */
index 2cca437..140f94f 100644 (file)
--- a/common.h
+++ b/common.h
@@ -407,7 +407,7 @@ typedef struct {
     int firstProtocolVersion;
     int secondProtocolVersion;
     Boolean showButtonBar;
-    /* [AS] New properties */
+    /* [AS] New properties (down to the "ZIPPY" part) */
     Boolean firstScoreIsAbsolute;  /* If true, engine score is always from white side */
     Boolean secondScoreIsAbsolute; /* If true, engine score is always from white side */
     Boolean saveExtendedInfoInPGN; /* If true, saved PGN games contain extended info */
@@ -430,6 +430,17 @@ typedef struct {
     char * nameOfDebugFile;
     char * pgnEventHeader;
     int defaultFrcPosition;
+    char * gameListTags;
+    Boolean saveOutOfBookInfo;
+    Boolean showEvalInMoveHistory;
+    int evalHistColorWhite;
+    int evalHistColorBlack;
+    Boolean highlightMoveWithArrow;
+    int highlightArrowColor;
+    Boolean useStickyWindows;
+    int adjudicateDrawMoves;
+    Boolean autoDisplayComment;
+    Boolean autoDisplayTags;
 #if ZIPPY
     char *zippyLines;
     char *zippyPinhead;
@@ -450,6 +461,25 @@ typedef struct {
 #endif
 } AppData, *AppDataPtr;
 
+/* [AS] PGN tags (for showing in the game list) */
+#define GLT_EVENT           'e'
+#define GLT_SITE            's'
+#define GLT_DATE            'd'
+#define GLT_ROUND           'o'
+#define GLT_PLAYERS         'p'     /* I.e. white "-" black */
+#define GLT_RESULT          'r'
+#define GLT_WHITE_ELO       'w'
+#define GLT_BLACK_ELO       'b'
+#define GLT_TIME_CONTROL    't'
+#define GLT_VARIANT         'v'
+#define GLT_OUT_OF_BOOK     'a'
+
+#define GLT_DEFAULT_TAGS    "eprd"  /* Event, players, result, date */
+
+#define GLT_ALL_TAGS        "esdoprwbtva"
+
+#define PGN_OUT_OF_BOOK     "Annotator"
+
 extern AppData appData;
 
 typedef struct {
@@ -469,6 +499,7 @@ typedef struct {
     int whiteRating;    /* -1 if unknown */
     int blackRating;    /* -1 if unknown */
     VariantClass variant;
+    char *outOfBook;    /* [AS] Move and score when engine went out of book */
 } GameInfo;
 
 
index 732b316..ef79f23 100644 (file)
@@ -164,10 +164,11 @@ void ClearPremoveHighlights P((void));
 void ShutDownFrontEnd P((void));
 void BoardToTop P((void));
 void AnimateMove P((Board board, int fromX, int fromY, int toX, int toY));
-void HistorySet P((char movelist[][2*MOVE_LEN],
-                  int first, int last, int current));
+void HistorySet P((char movelist[][2*MOVE_LEN], int first, int last, int current));
 void FreezeUI P((void));
 void ThawUI P((void));
 extern char *programName;
 
+void SetProgramStats P(( int which, int depth, unsigned long nodes, int score, int time, char * pv )); /* [AS] */
+
 #endif
index e8d4132..2be1fe8 100644 (file)
@@ -70,6 +70,7 @@ static void GameListDeleteGame(listGame)
        if (listGame->gameInfo.resultDetails) free(listGame->gameInfo.resultDetails);
        if (listGame->gameInfo.timeControl) free(listGame->gameInfo.timeControl);
        if (listGame->gameInfo.extraTags) free(listGame->gameInfo.extraTags);
+        if (listGame->gameInfo.outOfBook) free(listGame->gameInfo.outOfBook);
        ListNodeFree((ListNode *) listGame);
     }
 }
@@ -107,6 +108,7 @@ void GameListInitGameInfo(gameInfo)
     gameInfo->whiteRating = -1; /* unknown */
     gameInfo->blackRating = -1; /* unknown */
     gameInfo->variant = VariantNormal;
+    gameInfo->outOfBook = NULL;
 }
 
 
@@ -295,12 +297,16 @@ void ClearGameInfo(gameInfo)
     if (gameInfo->extraTags != NULL) {
        free(gameInfo->extraTags);
     }
+    if (gameInfo->outOfBook != NULL) {
+        free(gameInfo->outOfBook);
+    }
 
     GameListInitGameInfo(gameInfo);
 }
 
+/* [AS] Replaced by "dynamic" tag selection below */
 char *
-GameListLine(number, gameInfo)
+GameListLineOld(number, gameInfo)
      int number;
      GameInfo *gameInfo;
 {
@@ -317,3 +323,97 @@ GameListLine(number, gameInfo)
     return ret;
 }
 
+#define MAX_FIELD_LEN   64  /* To avoid overflowing the buffer */
+
+char * GameListLine( int number, GameInfo * gameInfo )
+{
+    char buffer[1024];
+    char * buf = buffer;
+    char * glt = appData.gameListTags;
+
+    buf += sprintf( buffer, "%d.", number );
+
+    while( *glt != '\0' ) {
+        *buf++ = ' ';
+
+        switch( *glt ) {
+        case GLT_EVENT:
+            strncpy( buf, gameInfo->event ? gameInfo->event : "?", MAX_FIELD_LEN );
+            break;
+        case GLT_SITE:
+            strncpy( buf, gameInfo->site ? gameInfo->site : "?", MAX_FIELD_LEN );
+            break;
+        case GLT_DATE:
+            strncpy( buf, gameInfo->date ? gameInfo->date : "?", MAX_FIELD_LEN );
+            break;
+        case GLT_ROUND:
+            strncpy( buf, gameInfo->round ? gameInfo->round : "?", MAX_FIELD_LEN );
+            break;
+        case GLT_PLAYERS:
+            strncpy( buf, gameInfo->white ? gameInfo->white : "?", MAX_FIELD_LEN );
+            buf[ MAX_FIELD_LEN-1 ] = '\0';
+            buf += strlen( buf );
+            *buf++ = '-';
+            strncpy( buf, gameInfo->black ? gameInfo->black : "?", MAX_FIELD_LEN );
+            break;
+        case GLT_RESULT:
+            strcpy( buf, PGNResult(gameInfo->result) );
+            break;
+        case GLT_WHITE_ELO:
+            if( gameInfo->whiteRating > 0 )
+                sprintf( buf, "%d", gameInfo->whiteRating );
+            else
+                strcpy( buf, "?" );
+            break;
+        case GLT_BLACK_ELO:
+            if( gameInfo->blackRating > 0 )
+                sprintf( buf, "%d", gameInfo->blackRating );
+            else
+                strcpy( buf, "?" );
+            break;
+        case GLT_TIME_CONTROL:
+            strncpy( buf, gameInfo->timeControl ? gameInfo->timeControl : "?", MAX_FIELD_LEN );
+            break;
+        case GLT_VARIANT:
+            break;
+        case GLT_OUT_OF_BOOK:
+            strncpy( buf, gameInfo->outOfBook ? gameInfo->outOfBook : "?", MAX_FIELD_LEN );
+            break;
+        default:
+            break;
+        }
+
+        buf[MAX_FIELD_LEN-1] = '\0';
+
+        buf += strlen( buf );
+
+        glt++;
+
+        if( *glt != '\0' ) {
+            *buf++ = ',';
+        }
+    }
+
+    *buf = '\0';
+
+    return strdup( buffer );
+}
+
+char * GameListLineFull( int number, GameInfo * gameInfo )
+{
+    char * event = gameInfo->event ? gameInfo->event : "?";
+    char * site = gameInfo->site ? gameInfo->site : "?";
+    char * white = gameInfo->white ? gameInfo->white : "?";
+    char * black = gameInfo->black ? gameInfo->black : "?";
+    char * round = gameInfo->round ? gameInfo->round : "?";
+    char * date = gameInfo->date ? gameInfo->date : "?";
+    char * oob = gameInfo->outOfBook ? gameInfo->outOfBook : "";
+
+    int len = 64 + strlen(event) + strlen(site) + strlen(white) + strlen(black) + strlen(date) + strlen(oob);
+
+    char *ret = (char *) malloc(len);
+
+    sprintf(ret, "%d, \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\"", number, event, site, round, white, black, PGNResult(gameInfo->result), date, oob );
+
+    return ret;
+}
index cd8f0d8..6317c8b 100644 (file)
--- a/pgntags.c
+++ b/pgntags.c
@@ -114,6 +114,9 @@ int ParsePGNTag(tag, gameInfo)
         /* xboard-defined extension */
         gameInfo->variant = StringToVariant(value);
        success = TRUE;
+    } else if (StrCaseCmp(name, PGN_OUT_OF_BOOK) == 0) {
+        /* [AS] Out of book annotation */
+        success = StrSavePtr(value, &gameInfo->outOfBook) != NULL;
     } else {
        if (gameInfo->extraTags == NULL) {
            oldTags = "";
diff --git a/winboard/wengineoutput.c b/winboard/wengineoutput.c
new file mode 100644 (file)
index 0000000..6486508
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Engine output (PV)
+ *
+ * Author: Alessandro Scotti
+ *
+ * ------------------------------------------------------------------------
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ------------------------------------------------------------------------
+ */
+#include "config.h"
+
+#include <windows.h> /* required for all Windows applications */
+#include <richedit.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <commdlg.h>
+#include <dlgs.h>
+
+#include "common.h"
+#include "winboard.h"
+#include "frontend.h"
+#include "backend.h"
+
+#include "wsnap.h"
+
+VOID EngineOutputPopUp();
+VOID EngineOutputPopDown();
+BOOL EngineOutputIsUp();
+
+/* Imports from backend.c */
+char * SavePart(char *str);
+
+/* Imports from winboard.c */
+extern HWND engineOutputDialog;
+extern BOOLEAN engineOutputDialogUp;
+
+extern HINSTANCE hInst;
+extern HWND hwndMain;
+
+extern WindowPlacement wpEngineOutput;
+
+/* Module variables */
+#define H_MARGIN            2
+#define V_MARGIN            2
+#define LABEL_V_DISTANCE    1   /* Distance between label and memo */
+#define SPLITTER_SIZE       4   /* Distance between first memo and second label */
+
+static int  windowMode = 1;
+
+static int  lastDepth[2] = { -1, -1 };
+
+static VOID SetControlPos( HWND hDlg, int id, int x, int y, int width, int height )
+{
+    HWND hControl = GetDlgItem( hDlg, id );
+
+    SetWindowPos( hControl, HWND_TOP, x, y, width, height, SWP_NOZORDER );
+}
+
+static VOID HideControl( HWND hDlg, int id )
+{
+    /* TODO: we should also hide/disable it!!! what about tab stops?!?! */
+    SetControlPos( hDlg, id, 20000, 20000, 100, 100 );
+}
+
+static int GetControlWidth( HWND hDlg, int id )
+{
+    RECT rc;
+
+    GetWindowRect( GetDlgItem( hDlg, IDC_EngineLabel1 ), &rc );
+
+    return rc.right - rc.left;
+}
+
+static VOID ResizeWindowControls( HWND hDlg, int mode )
+{
+    RECT rc;
+    int labelHeight;
+    int clientWidth;
+    int clientHeight;
+    int maxControlWidth;
+    int npsWidth;
+
+    /* Initialize variables */
+    GetWindowRect( GetDlgItem( hDlg, IDC_EngineLabel1 ), &rc );
+
+    labelHeight = rc.bottom - rc.top;
+
+    GetClientRect( hDlg, &rc );
+
+    clientWidth = rc.right - rc.left;
+    clientHeight = rc.bottom - rc.top;
+
+    maxControlWidth = clientWidth - 2*H_MARGIN;
+
+    npsWidth = GetControlWidth( hDlg, IDC_Engine1_NPS );
+
+    /* Resize controls */
+    if( mode == 0 ) {
+        /* One engine */
+        int memo_y = V_MARGIN + labelHeight + LABEL_V_DISTANCE;
+        int memo_h = clientHeight - memo_y - V_MARGIN;
+
+        SetControlPos( hDlg, IDC_EngineLabel1, H_MARGIN, V_MARGIN, maxControlWidth / 2, labelHeight );
+        SetControlPos( hDlg, IDC_EngineMemo1, H_MARGIN, memo_y, maxControlWidth, memo_h );
+
+        /* Hide controls for the second engine */
+        HideControl( hDlg, IDC_EngineLabel2 );
+        HideControl( hDlg, IDC_Engine2_NPS );
+        HideControl( hDlg, IDC_EngineMemo2 );
+        /* TODO: we should also hide/disable them!!! what about tab stops?!?! */
+    }
+    else {
+        /* Two engines */
+        int memo1_y = V_MARGIN + labelHeight + LABEL_V_DISTANCE;
+        int memo_h = (clientHeight - memo1_y - V_MARGIN - labelHeight - LABEL_V_DISTANCE - SPLITTER_SIZE) / 2;
+        int label2_y = memo1_y + memo_h + SPLITTER_SIZE;
+        int memo2_y = label2_y + labelHeight + LABEL_V_DISTANCE;
+        int nps_x = clientWidth - H_MARGIN - npsWidth;
+
+        SetControlPos( hDlg, IDC_EngineLabel1, H_MARGIN, V_MARGIN, maxControlWidth / 2, labelHeight );
+        SetControlPos( hDlg, IDC_Engine1_NPS, nps_x, V_MARGIN, npsWidth, labelHeight );
+        SetControlPos( hDlg, IDC_EngineMemo1, H_MARGIN, memo1_y, maxControlWidth, memo_h );
+
+        SetControlPos( hDlg, IDC_EngineLabel2, H_MARGIN, label2_y, maxControlWidth / 2, labelHeight );
+        SetControlPos( hDlg, IDC_Engine2_NPS, nps_x, label2_y, npsWidth, labelHeight );
+        SetControlPos( hDlg, IDC_EngineMemo2, H_MARGIN, memo2_y, maxControlWidth, memo_h );
+    }
+
+    InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo1), NULL, FALSE );
+    InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo2), NULL, FALSE );
+}
+
+static VOID SetDisplayMode( int mode )
+{
+    if( windowMode != mode ) {
+        windowMode = mode;
+
+        ResizeWindowControls( engineOutputDialog, mode );
+    }
+}
+
+static VOID VerifyDisplayMode()
+{
+    int mode;
+
+    /* Get proper mode for current game */
+    switch( gameMode ) {
+    case AnalyzeMode:
+    case AnalyzeFile:
+    case MachinePlaysWhite:
+    case MachinePlaysBlack:
+        mode = 0;
+        break;
+    case TwoMachinesPlay:
+        mode = 1;
+        break;
+    default:
+        /* Do not change */
+        return;
+    }
+
+    SetDisplayMode( mode );
+}
+
+static VOID InsertIntoMemo( HWND hMemo, char * text )
+{
+    SendMessage( hMemo, EM_SETSEL, 0, 0 );
+
+    SendMessage( hMemo, EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );
+}
+
+#define MAX_NAME_LENGTH 32
+
+static VOID UpdateControls( HWND hLabel, HWND hLabelNPS, HWND hMemo, char * name, int depth, unsigned long nodes, int score, int time, char * pv )
+{
+    char s_label[MAX_NAME_LENGTH + 64];
+
+    /* Label */
+    if( name == 0 || *name == '\0' ) {
+        name = "?";
+    }
+
+    strncpy( s_label, name, MAX_NAME_LENGTH );
+    s_label[ MAX_NAME_LENGTH-1 ] = '\0';
+
+    SetWindowText( hLabel, s_label );
+
+    s_label[0] = '\0';
+
+    if( time > 0 && nodes > 0 ) {
+        unsigned long nps_100 = nodes / time;
+
+        if( nps_100 < 100000 ) {
+            sprintf( s_label, "NPS: %lu", nps_100 * 100 );
+        }
+        else {
+            sprintf( s_label, "NPS: %.1fk", nps_100 / 10.0 );
+        }
+    }
+
+    SetWindowText( hLabelNPS, s_label );
+
+    /* Memo */
+    if( pv != 0 && *pv != '\0' ) {
+        char s_nodes[24];
+        char s_score[16];
+        char s_time[24];
+        char buf[256];
+        int buflen;
+        int time_secs = time / 100;
+        int time_cent = time % 100;
+
+        /* Nodes */
+        if( nodes < 1000000 ) {
+            sprintf( s_nodes, "%lu", nodes );
+        }
+        else {
+            sprintf( s_nodes, "%.1fM", nodes / 1000000.0 );
+        }
+
+        /* Score */
+        if( score > 0 ) {
+            sprintf( s_score, "+%.2f", score / 100.0 );
+        }
+        else {
+            sprintf( s_score, "%.2f", score / 100.0 );
+        }
+
+        /* Time */
+        sprintf( s_time, "%d:%02d.%02d", time_secs / 60, time_secs % 60, time_cent );
+
+        /* Put all together... */
+        sprintf( buf, "%3d\t%s\t%s\t%s\t", depth, s_score, s_nodes, s_time );
+
+        /* Add PV */
+        buflen = strlen(buf);
+
+        strncpy( buf + buflen, pv, sizeof(buf) - buflen );
+
+        buf[ sizeof(buf) - 3 ] = '\0';
+
+        strcat( buf + buflen, "\r\n" );
+
+        /* Update memo */
+        InsertIntoMemo( hMemo, buf );
+    }
+}
+
+LRESULT CALLBACK EngineOutputProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
+{
+    static SnapData sd;
+
+    switch (message) {
+    case WM_INITDIALOG:
+        if( engineOutputDialog == NULL ) {
+            engineOutputDialog = hDlg;
+
+            RestoreWindowPlacement( hDlg, &wpEngineOutput ); /* Restore window placement */
+
+            ResizeWindowControls( hDlg, windowMode );
+        }
+
+        return FALSE;
+
+    case WM_COMMAND:
+        switch (LOWORD(wParam)) {
+        case IDOK:
+          EndDialog(hDlg, TRUE);
+          return TRUE;
+
+        case IDCANCEL:
+          EndDialog(hDlg, FALSE);
+          return TRUE;
+
+        default:
+          break;
+        }
+
+        break;
+
+    case WM_GETMINMAXINFO:
+        {
+            MINMAXINFO * mmi = (MINMAXINFO *) lParam;
+
+            mmi->ptMinTrackSize.x = 100;
+            mmi->ptMinTrackSize.y = 160;
+        }
+        break;
+
+    case WM_CLOSE:
+        EngineOutputPopDown();
+        break;
+
+    case WM_SIZE:
+        ResizeWindowControls( hDlg, windowMode );
+        break;
+
+    case WM_ENTERSIZEMOVE:
+        return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
+
+    case WM_SIZING:
+        return OnSizing( &sd, hDlg, wParam, lParam );
+
+    case WM_MOVING:
+        return OnMoving( &sd, hDlg, wParam, lParam );
+
+    case WM_EXITSIZEMOVE:
+        return OnExitSizeMove( &sd, hDlg, wParam, lParam );
+    }
+
+    return FALSE;
+}
+
+VOID EngineOutputPopUp()
+{
+  FARPROC lpProc;
+
+  CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_CHECKED);
+
+  if( engineOutputDialog ) {
+    SendMessage( engineOutputDialog, WM_INITDIALOG, 0, 0 );
+
+    if( ! engineOutputDialogUp ) {
+        ShowWindow(engineOutputDialog, SW_SHOW);
+    }
+  }
+  else {
+    lpProc = MakeProcInstance( (FARPROC) EngineOutputProc, hInst );
+
+    /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
+    CreateDialog( hInst, MAKEINTRESOURCE(DLG_EngineOutput), hwndMain, (DLGPROC)lpProc );
+
+    FreeProcInstance(lpProc);
+  }
+
+  engineOutputDialogUp = TRUE;
+}
+
+VOID EngineOutputPopDown()
+{
+  CheckMenuItem(GetMenu(hwndMain), IDM_ShowEngineOutput, MF_UNCHECKED);
+
+  if( engineOutputDialog ) {
+      ShowWindow(engineOutputDialog, SW_HIDE);
+  }
+
+  engineOutputDialogUp = FALSE;
+}
+
+BOOL EngineOutputIsUp()
+{
+    return engineOutputDialogUp;
+}
+
+VOID EngineOutputUpdate( int which, int depth, unsigned long nodes, int score, int time, char * pv )
+{
+    HWND hLabel;
+    HWND hLabelNPS;
+    HWND hMemo;
+    char * name;
+
+    if( which < 0 || which > 1 || depth < 0 || time < 0 || pv == 0 || *pv == '\0' ) {
+        return;
+    }
+
+    if( engineOutputDialog == NULL ) {
+        return;
+    }
+
+    VerifyDisplayMode();
+
+    /* Get target control */
+    if( which == 0 ) {
+        hLabel = GetDlgItem( engineOutputDialog, IDC_EngineLabel1 );
+        hLabelNPS = GetDlgItem( engineOutputDialog, IDC_Engine1_NPS );
+        hMemo  = GetDlgItem( engineOutputDialog, IDC_EngineMemo1 );
+        name = first.tidy;
+    }
+    else {
+        hLabel = GetDlgItem( engineOutputDialog, IDC_EngineLabel2 );
+        hLabelNPS = GetDlgItem( engineOutputDialog, IDC_Engine2_NPS );
+        hMemo  = GetDlgItem( engineOutputDialog, IDC_EngineMemo2 );
+        name = second.tidy;
+    }
+
+    /* Clear memo if needed */
+    if( lastDepth[which] > depth || (lastDepth[which] == depth && depth <= 1) ) {
+        SendMessage( hMemo, WM_SETTEXT, 0, (LPARAM) "" );
+    }
+
+    /* Update */
+    lastDepth[which] = depth;
+
+    if( pv[0] == ' ' ) {
+        if( strncmp( pv, " no PV", 6 ) == 0 ) { /* Hack on hack! :-O */
+            pv = "";
+        }
+    }
+
+    UpdateControls( hLabel, hLabelNPS, hMemo, name, depth, nodes, score, time, pv );
+}
diff --git a/winboard/wevalgraph.c b/winboard/wevalgraph.c
new file mode 100644 (file)
index 0000000..2efb15e
--- /dev/null
@@ -0,0 +1,620 @@
+/*
+ * Evaluation graph
+ *
+ * Author: Alessandro Scotti
+ *
+ * ------------------------------------------------------------------------
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ------------------------------------------------------------------------
+ */
+#include "config.h"
+
+#include <windows.h> /* required for all Windows applications */
+#include <richedit.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <commdlg.h>
+#include <dlgs.h>
+
+#include "common.h"
+#include "winboard.h"
+#include "frontend.h"
+#include "backend.h"
+
+#include "wsnap.h"
+
+VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );
+VOID EvalGraphPopUp();
+VOID EvalGraphPopDown();
+BOOL EvalGraphIsUp();
+
+#define WM_REFRESH_GRAPH    (WM_USER + 1)
+
+/* Imports from backend.c */
+char * SavePart(char *str);
+
+/* Imports from winboard.c */
+extern HWND evalGraphDialog;
+extern BOOLEAN evalGraphDialogUp;
+
+extern HINSTANCE hInst;
+extern HWND hwndMain;
+
+extern WindowPlacement wpEvalGraph;
+
+/* Module globals */
+static ChessProgramStats_Move * currPvInfo;
+static int currFirst = 0;
+static int currLast = 0;
+static int currCurrent = -1;
+
+static COLORREF crWhite = RGB( 0xFF, 0xFF, 0xB0 );
+static COLORREF crBlack = RGB( 0xAD, 0x5D, 0x3D );
+
+static HDC hdcPB = NULL;
+static HBITMAP hbmPB = NULL;
+static int nWidthPB = 0;
+static int nHeightPB = 0;
+static HPEN hpenDotted = NULL;
+static HPEN hpenBlueDotted = NULL;
+static HPEN hpenBold[2] = { NULL, NULL };
+static HBRUSH hbrHist[2] = { NULL, NULL };
+
+static int MarginX = 18;
+static int MarginW = 4;
+static int MarginH = 4;
+
+#define MIN_HIST_WIDTH  4
+#define MAX_HIST_WIDTH  10
+
+static int GetPvScore( int index )
+{
+    int score = currPvInfo[ index ].score;
+
+    if( index & 1 ) score = -score; /* Flip score for black */
+
+    return score;
+}
+
+static VOID DrawLine( int x1, int y1, int x2, int y2 )
+{
+    MoveToEx( hdcPB, x1, y1, NULL );
+
+    LineTo( hdcPB, x2, y2 );
+}
+
+static VOID DrawLineEx( int x1, int y1, int x2, int y2 )
+{
+    POINT stPT;
+
+    MoveToEx( hdcPB, x1, y1, &stPT );
+
+    LineTo( hdcPB, x2, y2 );
+
+    MoveToEx( hdcPB, stPT.x, stPT.y, NULL );
+}
+
+static HBRUSH CreateBrush( UINT style, COLORREF color )
+{
+    LOGBRUSH stLB;
+
+    stLB.lbStyle = style;
+    stLB.lbColor = color;
+    stLB.lbHatch = 0;
+
+    return CreateBrushIndirect( &stLB );
+}
+
+/*
+    For a centipawn value, this function returns the height of the corresponding
+    histogram, centered on the reference axis.
+
+    Note: height can be negative!
+*/
+static int GetValueY( int value )
+{
+    if( value < -700 ) value = -700;
+    if( value > +700 ) value = +700;
+
+    return (nHeightPB / 2) - (int)(value * (nHeightPB - 2*MarginH) / 1400.0);
+}
+
+static VOID DrawAxisSegmentHoriz( int value, BOOL drawValue )
+{
+    int y = GetValueY( value*100 );
+
+    SelectObject( hdcPB, GetStockObject(BLACK_PEN) );
+    DrawLine( MarginX, y, MarginX + MarginW, y );
+    SelectObject( hdcPB, hpenDotted );
+    DrawLine( MarginX + MarginW, y, nWidthPB - MarginW, y );
+
+    if( drawValue ) {
+        SIZE stSize;
+        char buf[8];
+        int cbBuf;
+
+        if( value > 0 ) {
+            buf[0] = '+';
+            itoa( value, buf+1, 10 );
+        }
+        else {
+            itoa( value, buf, 10 );
+        }
+
+        cbBuf = strlen( buf );
+
+        GetTextExtentPoint32( hdcPB, buf, cbBuf, &stSize );
+
+        TextOut( hdcPB, MarginX - stSize.cx - 2, y - stSize.cy / 2, buf, cbBuf );
+    }
+}
+
+static VOID DrawAxis()
+{
+    int cy = nHeightPB / 2;
+
+    SelectObject( hdcPB, GetStockObject(NULL_BRUSH) );
+
+    SetBkMode( hdcPB, TRANSPARENT );
+
+    DrawAxisSegmentHoriz( +5, TRUE );
+    DrawAxisSegmentHoriz( +3, FALSE );
+    DrawAxisSegmentHoriz( +1, FALSE );
+    DrawAxisSegmentHoriz(  0, TRUE );
+    DrawAxisSegmentHoriz( -1, FALSE );
+    DrawAxisSegmentHoriz( -3, FALSE );
+    DrawAxisSegmentHoriz( -5, TRUE );
+
+    SelectObject( hdcPB, GetStockObject(BLACK_PEN) );
+
+    DrawLine( MarginX + MarginW, cy, nWidthPB - MarginW, cy );
+    DrawLine( MarginX + MarginW, MarginH, MarginX + MarginW, nHeightPB - MarginH );
+}
+
+static VOID DrawHistogram( int x, int y, int width, int value, int side )
+{
+    RECT rc;
+
+    if( value > -25 && value < +25 ) return;
+
+    rc.left = x;
+    rc.right = rc.left + width + 1;
+
+    if( value > 0 ) {
+        rc.top = GetValueY( value );
+        rc.bottom = y+1;
+    }
+    else {
+        rc.top = y;
+        rc.bottom = GetValueY( value ) + 1;
+    }
+
+
+    if( width == MIN_HIST_WIDTH ) {
+        rc.right--;
+        FillRect( hdcPB, &rc, hbrHist[side] );
+    }
+    else {
+        SelectObject( hdcPB, hbrHist[side] );
+        Rectangle( hdcPB, rc.left, rc.top, rc.right, rc.bottom );
+    }
+}
+
+static VOID DrawSeparator( int index, int x )
+{
+    if( index > 0 ) {
+        if( index == currCurrent ) {
+            HPEN hp = SelectObject( hdcPB, hpenBlueDotted );
+            DrawLineEx( x, MarginH, x, nHeightPB - MarginH );
+            SelectObject( hdcPB, hp );
+        }
+        else if( (index % 20) == 0 ) {
+            HPEN hp = SelectObject( hdcPB, hpenDotted );
+            DrawLineEx( x, MarginH, x, nHeightPB - MarginH );
+            SelectObject( hdcPB, hp );
+        }
+    }
+}
+
+/* Actually draw histogram as a diagram, cause there's too much data */
+static VOID DrawHistogramAsDiagram( int cy, int paint_width, int hist_count )
+{
+    double step;
+    int i;
+
+    /* Rescale the graph every few moves (as opposed to every move) */
+    hist_count -= hist_count % 8;
+    hist_count += 8;
+    hist_count /= 2;
+
+    step = (double) paint_width / (hist_count + 1);
+
+    for( i=0; i<2; i++ ) {
+        int index = currFirst;
+        int side = (currCurrent + i + 1) & 1; /* Draw current side last */
+        double x = MarginX + MarginW;
+
+        if( (index & 1) != side ) {
+            x += step / 2;
+            index++;
+        }
+
+        SelectObject( hdcPB, hpenBold[side] );
+
+        MoveToEx( hdcPB, (int) x, cy, NULL );
+
+        index += 2;
+
+        while( index < currLast ) {
+            x += step;
+
+            DrawSeparator( index, (int) x );
+
+            /* Extend line up to current point */
+            if( currPvInfo[index].depth > 0 ) {
+                LineTo( hdcPB, (int) x, GetValueY( GetPvScore(index) ) );
+            }
+
+            index += 2;
+        }
+    }
+}
+
+static VOID DrawHistogramFull( int cy, int hist_width, int hist_count )
+{
+    int i;
+
+    SelectObject( hdcPB, GetStockObject(BLACK_PEN) );
+
+    for( i=0; i<hist_count; i++ ) {
+        int index = currFirst + i;
+        int x = MarginX + MarginW + index * hist_width;
+
+        /* Draw a separator every 10 moves */
+        DrawSeparator( index, x );
+
+        /* Draw histogram */
+        if( currPvInfo[i].depth > 0 ) {
+            DrawHistogram( x, cy, hist_width, GetPvScore(index), index & 1 );
+        }
+    }
+}
+
+typedef struct {
+    int cy;
+    int hist_width;
+    int hist_count;
+    int paint_width;
+} VisualizationData;
+
+static BOOL InitVisualization( VisualizationData * vd )
+{
+    BOOL result = FALSE;
+
+    vd->cy = nHeightPB / 2;
+    vd->hist_width = MIN_HIST_WIDTH;
+    vd->hist_count = currLast - currFirst;
+    vd->paint_width = nWidthPB - MarginX - 2*MarginW;
+
+    if( vd->hist_count > 0 ) {
+        result = TRUE;
+
+        /* Compute width */
+        vd->hist_width = vd->paint_width / vd->hist_count;
+
+        if( vd->hist_width > MAX_HIST_WIDTH ) vd->hist_width = MAX_HIST_WIDTH;
+
+        vd->hist_width -= vd->hist_width % 2;
+    }
+
+    return result;
+}
+
+static VOID DrawHistograms()
+{
+    VisualizationData vd;
+
+    if( InitVisualization( &vd ) ) {
+        if( vd.hist_width < MIN_HIST_WIDTH ) {
+            DrawHistogramAsDiagram( vd.cy, vd.paint_width, vd.hist_count );
+        }
+        else {
+            DrawHistogramFull( vd.cy, vd.hist_width, vd.hist_count );
+        }
+    }
+}
+
+static int GetMoveIndexFromPoint( int x, int y )
+{
+    int result = -1;
+    int start_x = MarginX + MarginW;
+    VisualizationData vd;
+
+    if( x >= start_x && InitVisualization( &vd ) ) {
+        /* Almost an hack here... we duplicate some of the paint logic */
+        if( vd.hist_width < MIN_HIST_WIDTH ) {
+            double step;
+
+            vd.hist_count -= vd.hist_count % 8;
+            vd.hist_count += 8;
+            vd.hist_count /= 2;
+
+            step = (double) vd.paint_width / (vd.hist_count + 1);
+            step /= 2;
+
+            result = (int) (0.5 + (double) (x - start_x) / step);
+        }
+        else {
+            result = (x - start_x) / vd.hist_width;
+        }
+    }
+
+    if( result >= currLast ) {
+        result = -1;
+    }
+
+    return result;
+}
+
+static VOID DrawBackground()
+{
+    HBRUSH hbr;
+    RECT rc;
+
+    hbr = CreateBrush( BS_SOLID, GetSysColor( COLOR_3DFACE ) );
+
+    rc.left = 0;
+    rc.top = 0;
+    rc.right = nWidthPB;
+    rc.bottom = nHeightPB;
+
+    FillRect( hdcPB, &rc, hbr );
+
+    DeleteObject( hbr );
+}
+
+static VOID PaintEvalGraph( HWND hWnd, HDC hDC )
+{
+    RECT rcClient;
+    int width;
+    int height;
+
+    /* Get client area */
+    GetClientRect( hWnd, &rcClient );
+
+    width = rcClient.right - rcClient.left;
+    height = rcClient.bottom - rcClient.top;
+
+    /* Create or recreate paint box if needed */
+    if( hbmPB == NULL || width != nWidthPB || height != nHeightPB ) {
+        if( hpenDotted == NULL ) {
+            hpenDotted = CreatePen( PS_DOT, 0, RGB(0xA0,0xA0,0xA0) );
+            hpenBlueDotted = CreatePen( PS_DOT, 0, RGB(0x00,0x00,0xFF) );
+            hpenBold[0] = CreatePen( PS_SOLID, 2, crWhite );
+            hpenBold[1] = CreatePen( PS_SOLID, 2, crBlack );
+            hbrHist[0] = CreateBrush( BS_SOLID, crWhite );
+            hbrHist[1] = CreateBrush( BS_SOLID, crBlack );
+        }
+
+        if( hdcPB != NULL ) {
+            DeleteDC( hdcPB );
+            hdcPB = NULL;
+        }
+
+        if( hbmPB != NULL ) {
+            DeleteObject( hbmPB );
+            hbmPB = NULL;
+        }
+
+        hdcPB = CreateCompatibleDC( hDC );
+
+        nWidthPB = width;
+        nHeightPB = height;
+        hbmPB = CreateCompatibleBitmap( hDC, nWidthPB, nHeightPB );
+
+        SelectObject( hdcPB, hbmPB );
+    }
+
+    /* Draw */
+    DrawBackground();
+    DrawAxis();
+    DrawHistograms();
+
+    /* Copy bitmap into destination DC */
+    BitBlt( hDC, 0, 0, width, height, hdcPB, 0, 0, SRCCOPY );
+}
+
+LRESULT CALLBACK EvalGraphProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
+{
+    static SnapData sd;
+
+    PAINTSTRUCT stPS;
+    HDC hDC;
+
+    switch (message) {
+    case WM_INITDIALOG:
+        if( evalGraphDialog == NULL ) {
+            evalGraphDialog = hDlg;
+
+            RestoreWindowPlacement( hDlg, &wpEvalGraph ); /* Restore window placement */
+        }
+
+        return FALSE;
+
+    case WM_COMMAND:
+        switch (LOWORD(wParam)) {
+        case IDOK:
+          EndDialog(hDlg, TRUE);
+          return TRUE;
+
+        case IDCANCEL:
+          EndDialog(hDlg, FALSE);
+          return TRUE;
+
+        default:
+          break;
+        }
+
+        break;
+
+    case WM_ERASEBKGND:
+        return TRUE;
+
+    case WM_PAINT:
+        hDC = BeginPaint( hDlg, &stPS );
+        PaintEvalGraph( hDlg, hDC );
+        EndPaint( hDlg, &stPS );
+        break;
+
+    case WM_REFRESH_GRAPH:
+        hDC = GetDC( hDlg );
+        PaintEvalGraph( hDlg, hDC );
+        ReleaseDC( hDlg, hDC );
+        break;
+
+    case WM_LBUTTONDBLCLK:
+        if( wParam == 0 || wParam == MK_LBUTTON ) {
+            int index = GetMoveIndexFromPoint( LOWORD(lParam), HIWORD(lParam) );
+
+            if( index >= 0 && index < currLast ) {
+                ToNrEvent( index + 1 );
+            }
+        }
+        return TRUE;
+
+    case WM_SIZE:
+        InvalidateRect( hDlg, NULL, FALSE );
+        break;
+
+    case WM_GETMINMAXINFO:
+        {
+            MINMAXINFO * mmi = (MINMAXINFO *) lParam;
+
+            mmi->ptMinTrackSize.x = 100;
+            mmi->ptMinTrackSize.y = 100;
+        }
+        break;
+
+    /* Support for captionless window */
+#if 0
+    case WM_NCLBUTTONDBLCLK:
+        if( wParam == HTCAPTION ) {
+            int index;
+            POINT mouse_xy;
+            POINTS pts = MAKEPOINTS(lParam);
+
+            mouse_xy.x = pts.x;
+            mouse_xy.y = pts.y;
+            ScreenToClient( hDlg, &mouse_xy );
+
+            index = GetMoveIndexFromPoint( mouse_xy.x, mouse_xy.y );
+
+            if( index >= 0 && index < currLast ) {
+                ToNrEvent( index + 1 );
+            }
+        }
+        break;
+
+    case WM_NCHITTEST:
+        {
+            LRESULT res = DefWindowProc( hDlg, message, wParam, lParam );
+
+            if( res == HTCLIENT ) res = HTCAPTION;
+
+            SetWindowLong( hDlg, DWL_MSGRESULT, res );
+
+            return TRUE;
+        }
+        break;
+#endif
+
+    case WM_CLOSE:
+        EvalGraphPopDown();
+        break;
+
+    case WM_ENTERSIZEMOVE:
+        return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
+
+    case WM_SIZING:
+        return OnSizing( &sd, hDlg, wParam, lParam );
+
+    case WM_MOVING:
+        return OnMoving( &sd, hDlg, wParam, lParam );
+
+    case WM_EXITSIZEMOVE:
+        return OnExitSizeMove( &sd, hDlg, wParam, lParam );
+    }
+
+    return FALSE;
+}
+
+VOID EvalGraphPopUp()
+{
+  FARPROC lpProc;
+
+  CheckMenuItem(GetMenu(hwndMain), IDM_ShowEvalGraph, MF_CHECKED);
+
+  if( evalGraphDialog ) {
+    SendMessage( evalGraphDialog, WM_INITDIALOG, 0, 0 );
+
+    if( ! evalGraphDialogUp ) {
+        ShowWindow(evalGraphDialog, SW_SHOW);
+    }
+  }
+  else {
+    crWhite = appData.evalHistColorWhite;
+    crBlack = appData.evalHistColorBlack;
+
+    lpProc = MakeProcInstance( (FARPROC) EvalGraphProc, hInst );
+
+    /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
+    CreateDialog( hInst, MAKEINTRESOURCE(DLG_EvalGraph), hwndMain, (DLGPROC)lpProc );
+
+    FreeProcInstance(lpProc);
+  }
+
+  evalGraphDialogUp = TRUE;
+}
+
+VOID EvalGraphPopDown()
+{
+  CheckMenuItem(GetMenu(hwndMain), IDM_ShowEvalGraph, MF_UNCHECKED);
+
+  if( evalGraphDialog ) {
+      ShowWindow(evalGraphDialog, SW_HIDE);
+  }
+
+  evalGraphDialogUp = FALSE;
+}
+
+VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo )
+{
+    /* [AS] Danger! For now we rely on the pvInfo parameter being a static variable! */
+
+    currFirst = first;
+    currLast = last;
+    currCurrent = current;
+    currPvInfo = pvInfo;
+
+    if( evalGraphDialog ) {
+        SendMessage( evalGraphDialog, WM_REFRESH_GRAPH, 0, 0 );
+    }
+}
+
+BOOL EvalGraphIsUp()
+{
+    return evalGraphDialogUp;
+}
index 34339d4..5fa191c 100644 (file)
@@ -20,7 +20,6 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
  * ------------------------------------------------------------------------\r
  */\r
-\r
 #include "config.h"\r
 \r
 #include <windows.h> /* required for all Windows applications */\r
@@ -37,6 +36,8 @@
 #include "frontend.h"\r
 #include "backend.h"\r
 \r
+#include "wsnap.h"
+
 /* Module globals */\r
 HWND gameListDialog = NULL;\r
 BOOLEAN gameListUp = FALSE;\r
@@ -206,6 +207,7 @@ GameListDialog(HWND hDlg, UINT message,     WPARAM wParam, LPARAM lParam)
   static BOOL filterHasFocus = FALSE;
   int count;
   struct GameListStats stats;
+  static SnapData sd;
 \r
   switch (message) {\r
   case WM_INITDIALOG: \r
@@ -269,6 +271,18 @@ GameListDialog(HWND hDlg, UINT message,    WPARAM wParam, LPARAM lParam)
     sizeY = newSizeY;\r
     break;\r
 \r
+  case WM_ENTERSIZEMOVE:
+    return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
+
+  case WM_SIZING:
+    return OnSizing( &sd, hDlg, wParam, lParam );
+
+  case WM_MOVING:
+    return OnMoving( &sd, hDlg, wParam, lParam );
+
+  case WM_EXITSIZEMOVE:
+    return OnExitSizeMove( &sd, hDlg, wParam, lParam );
+
   case WM_GETMINMAXINFO:\r
     /* Prevent resizing window too small */\r
     mmi = (MINMAXINFO *) lParam;\r
@@ -469,3 +483,56 @@ VOID ShowGameListProc()
     }\r
   }\r
 }\r
+
+HGLOBAL ExportGameListAsText()
+{
+    HGLOBAL result = NULL;
+    LPVOID lpMem = NULL;
+    ListGame * lg = (ListGame *) gameList.head;
+    int nItem;
+    DWORD dwLen = 0;
+
+    if( ! gameFileName || ((ListGame *) gameList.tailPred)->number <= 0 ) {
+        DisplayError("Game list not loaded or empty", 0);
+        return NULL;
+    }
+
+    /* Get list size */
+    for (nItem = 0; nItem < ((ListGame *) gameList.tailPred)->number; nItem++){
+        char * st = GameListLineFull(lg->number, &lg->gameInfo);
+
+        dwLen += strlen(st) + 2; /* Add extra characters for "\r\n" */
+
+        free(st);
+        lg = (ListGame *) lg->node.succ;
+    }
+
+    /* Allocate memory for the list */
+    result = GlobalAlloc(GHND, dwLen+1 );
+
+    if( result != NULL ) {
+        lpMem = GlobalLock(result);
+    }
+
+    /* Copy the list into the global memory block */
+    if( lpMem != NULL ) {
+        char * dst = (char *) lpMem;
+        size_t len;
+
+        lg = (ListGame *) gameList.head;
+
+        for (nItem = 0; nItem < ((ListGame *) gameList.tailPred)->number; nItem++){
+            char * st = GameListLineFull(lg->number, &lg->gameInfo);
+
+            len = sprintf( dst, "%s\r\n", st );
+            dst += len;
+
+            free(st);
+            lg = (ListGame *) lg->node.succ;
+        }
+
+        GlobalUnlock( result );
+    }
+
+    return result;
+}
diff --git a/winboard/whistory.c b/winboard/whistory.c
new file mode 100644 (file)
index 0000000..ba69a63
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+ * Move history for WinBoard
+ *
+ * Author: Alessandro Scotti
+ *
+ * ------------------------------------------------------------------------
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ------------------------------------------------------------------------
+ */
+#include "config.h"
+
+#include <windows.h> /* required for all Windows applications */
+#include <richedit.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <commdlg.h>
+#include <dlgs.h>
+
+#include "common.h"
+#include "winboard.h"
+#include "frontend.h"
+#include "backend.h"
+
+#include "wsnap.h"
+
+VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );
+VOID MoveHistoryPopUp();
+VOID MoveHistoryPopDown();
+BOOL MoveHistoryIsUp();
+
+/* Imports from backend.c */
+char * SavePart(char *str);
+
+/* Imports from winboard.c */
+extern HWND moveHistoryDialog;
+extern BOOLEAN moveHistoryDialogUp;
+
+extern HINSTANCE hInst;
+extern HWND hwndMain;
+
+extern WindowPlacement wpMoveHistory;
+
+/* Module globals */
+typedef char MoveHistoryString[ MOVE_LEN*2 ];
+
+static int lastFirst = 0;
+static int lastLast = 0;
+static int lastCurrent = -1;
+
+static MoveHistoryString * currMovelist;
+static ChessProgramStats_Move * currPvInfo;
+static int currFirst = 0;
+static int currLast = 0;
+static int currCurrent = -1;
+
+typedef struct {
+    int memoOffset;
+    int memoLength;
+} HistoryMove;
+
+static HistoryMove histMoves[ MAX_MOVES ];
+
+#define WM_REFRESH_HISTORY  (WM_USER+4657)
+
+#define DEFAULT_COLOR       0xFFFFFFFF
+
+#define H_MARGIN            2
+#define V_MARGIN            2
+
+/* Note: in the following code a "Memo" is a Rich Edit control (it's Delphi lingo) */
+
+static VOID HighlightMove( int index, BOOL highlight )
+{
+    if( index >= 0 && index < MAX_MOVES ) {
+        CHARFORMAT cf;
+        HWND hMemo = GetDlgItem( moveHistoryDialog, IDC_MoveHistory );
+
+        SendMessage( hMemo,
+            EM_SETSEL,
+            histMoves[index].memoOffset,
+            histMoves[index].memoOffset + histMoves[index].memoLength );
+
+
+        /* Set style */
+        ZeroMemory( &cf, sizeof(cf) );
+
+        cf.cbSize = sizeof(cf);
+        cf.dwMask = CFM_BOLD | CFM_COLOR;
+
+        if( highlight ) {
+            cf.dwEffects |= CFE_BOLD;
+            cf.crTextColor = RGB( 0x00, 0x00, 0xFF );
+        }
+        else {
+            cf.dwEffects |= CFE_AUTOCOLOR;
+        }
+
+        SendMessage( hMemo, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf );
+    }
+}
+
+static BOOL OnlyCurrentPositionChanged()
+{
+    BOOL result = FALSE;
+
+    if( lastFirst >= 0 &&
+        lastLast >= lastFirst &&
+        lastCurrent >= lastFirst &&
+        currFirst == lastFirst &&
+        currLast == lastLast &&
+        currCurrent >= 0 &&
+        TRUE )
+    {
+        result = TRUE;
+    }
+
+    return result;
+}
+
+static BOOL OneMoveAppended()
+{
+    BOOL result = FALSE;
+
+    if( lastCurrent >= 0 && lastCurrent >= lastFirst && lastLast >= lastFirst &&
+        currCurrent >= 0 && currCurrent >= currFirst && currLast >= currFirst &&
+        lastFirst == currFirst &&
+        lastLast == (currLast-1) &&
+        lastCurrent == (currCurrent-1) &&
+        currCurrent == (currLast-1) &&
+        TRUE )
+    {
+        result = TRUE;
+    }
+
+    return result;
+}
+
+static VOID ClearMemo()
+{
+    SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, WM_SETTEXT, 0, (LPARAM) "" );
+}
+
+static int AppendToMemo( char * text, DWORD flags, DWORD color )
+{
+    CHARFORMAT cf;
+
+    HWND hMemo = GetDlgItem( moveHistoryDialog, IDC_MoveHistory );
+
+    /* Select end of text */
+    int cbTextLen = (int) SendMessage( hMemo, WM_GETTEXTLENGTH, 0, 0 );
+
+    SendMessage( hMemo, EM_SETSEL, cbTextLen, cbTextLen );
+
+    /* Set style */
+    ZeroMemory( &cf, sizeof(cf) );
+
+    cf.cbSize = sizeof(cf);
+    cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_COLOR | CFM_UNDERLINE;
+    cf.dwEffects = flags;
+
+    if( color != DEFAULT_COLOR ) {
+        cf.crTextColor = color;
+    }
+    else {
+        cf.dwEffects |= CFE_AUTOCOLOR;
+    }
+
+    SendMessage( hMemo, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf );
+
+    /* Append text */
+    SendMessage( hMemo, EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );
+
+    /* Return offset of appended text */
+    return cbTextLen;
+}
+
+static VOID AppendMoveToMemo( int index )
+{
+    char buf[64];
+    DWORD flags = 0;
+    DWORD color = DEFAULT_COLOR;
+
+    if( index < 0 || index >= MAX_MOVES ) {
+        return;
+    }
+
+    buf[0] = '\0';
+
+    /* Move number */
+    if( (index % 2) == 0 ) {
+        sprintf( buf, "%d.%s ", (index / 2)+1, index & 1 ? ".." : "" );
+        AppendToMemo( buf, CFE_BOLD, DEFAULT_COLOR );
+    }
+
+    /* Move text */
+    strcpy( buf, SavePart( currMovelist[index] ) );
+    strcat( buf, " " );
+
+    histMoves[index].memoOffset = AppendToMemo( buf, flags, color );
+    histMoves[index].memoLength = strlen(buf)-1;
+
+    /* PV info (if any) */
+    if( appData.showEvalInMoveHistory && currPvInfo[index].depth > 0 ) {
+        sprintf( buf, "%{%s%.2f/%d} ",
+            currPvInfo[index].score >= 0 ? "+" : "",
+            currPvInfo[index].score / 100.0,
+            currPvInfo[index].depth );
+
+        AppendToMemo( buf, flags,
+            color == DEFAULT_COLOR ? GetSysColor(COLOR_GRAYTEXT) : color );
+    }
+}
+
+static void RefreshMemoContent()
+{
+    int i;
+
+    ClearMemo();
+
+    for( i=currFirst; i<currLast; i++ ) {
+        AppendMoveToMemo( i );
+    }
+}
+
+static void MemoContentUpdated()
+{
+    int caretPos;
+
+    HighlightMove( lastCurrent, FALSE );
+    HighlightMove( currCurrent, TRUE );
+
+    lastFirst = currFirst;
+    lastLast = currLast;
+    lastCurrent = currCurrent;
+
+    /* Deselect any text, move caret to end of memo */
+    if( currCurrent >= 0 ) {
+        caretPos = histMoves[currCurrent].memoOffset + histMoves[currCurrent].memoLength;
+    }
+    else {
+        caretPos = (int) SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, WM_GETTEXTLENGTH, 0, 0 );
+    }
+
+    SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SETSEL, caretPos, caretPos );
+
+    SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SCROLLCARET, 0, 0 );
+}
+
+int FindMoveByCharIndex( int char_index )
+{
+    int index;
+
+    for( index=currFirst; index<currLast; index++ ) {
+        if( char_index >= histMoves[index].memoOffset &&
+            char_index <  (histMoves[index].memoOffset + histMoves[index].memoLength) )
+        {
+            return index;
+        }
+    }
+
+    return -1;
+}
+
+LRESULT CALLBACK HistoryDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
+{
+    static SnapData sd;
+
+    switch (message) {
+    case WM_INITDIALOG:
+        if( moveHistoryDialog == NULL ) {
+            moveHistoryDialog = hDlg;
+
+            /* Enable word wrapping and notifications */
+            SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SETTARGETDEVICE, 0, 0 );
+
+            SendDlgItemMessage( moveHistoryDialog, IDC_MoveHistory, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
+
+            /* Restore window placement */
+            RestoreWindowPlacement( hDlg, &wpMoveHistory );
+        }
+
+        /* Update memo */
+        RefreshMemoContent();
+
+        MemoContentUpdated();
+
+        return FALSE;
+
+    case WM_COMMAND:
+        switch (LOWORD(wParam)) {
+        case IDOK:
+          EndDialog(hDlg, TRUE);
+          return TRUE;
+
+        case IDCANCEL:
+          EndDialog(hDlg, FALSE);
+          return TRUE;
+
+        default:
+          break;
+        }
+
+        break;
+
+    case WM_NOTIFY:
+        if( wParam == IDC_MoveHistory ) {
+            MSGFILTER * lpMF = (MSGFILTER *) lParam;
+
+            if( lpMF->msg == WM_LBUTTONDBLCLK && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
+                POINTL pt;
+                LRESULT index;
+
+                pt.x = LOWORD( lpMF->lParam );
+                pt.y = HIWORD( lpMF->lParam );
+
+                index = SendDlgItemMessage( hDlg, IDC_MoveHistory, EM_CHARFROMPOS, 0, (LPARAM) &pt );
+
+                index = FindMoveByCharIndex( index );
+
+                if( index >= 0 ) {
+                    ToNrEvent( index + 1 );
+                }
+
+                /* Zap the message for good: apparently, returning non-zero is not enough */
+                lpMF->msg = WM_USER;
+
+                return TRUE;
+            }
+        }
+        break;
+
+    case WM_REFRESH_HISTORY:
+        /* Update the GUI */
+        if( OnlyCurrentPositionChanged() ) {
+            /* Only "cursor" changed, no need to update memo content */
+        }
+        else if( OneMoveAppended() ) {
+            AppendMoveToMemo( currCurrent );
+        }
+        else {
+            RefreshMemoContent();
+        }
+
+        MemoContentUpdated();
+
+        break;
+
+    case WM_SIZE:
+        SetWindowPos( GetDlgItem( moveHistoryDialog, IDC_MoveHistory ),
+            HWND_TOP,
+            H_MARGIN, V_MARGIN,
+            LOWORD(lParam) - 2*H_MARGIN,
+            HIWORD(lParam) - 2*V_MARGIN,
+            SWP_NOZORDER );
+        break;
+
+    case WM_GETMINMAXINFO:
+        {
+            MINMAXINFO * mmi = (MINMAXINFO *) lParam;
+
+            mmi->ptMinTrackSize.x = 100;
+            mmi->ptMinTrackSize.y = 100;
+        }
+        break;
+
+    case WM_CLOSE:
+        MoveHistoryPopDown();
+        break;
+
+    case WM_ENTERSIZEMOVE:
+        return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
+
+    case WM_SIZING:
+        return OnSizing( &sd, hDlg, wParam, lParam );
+
+    case WM_MOVING:
+        return OnMoving( &sd, hDlg, wParam, lParam );
+
+    case WM_EXITSIZEMOVE:
+        return OnExitSizeMove( &sd, hDlg, wParam, lParam );
+    }
+
+    return FALSE;
+}
+
+VOID MoveHistoryPopUp()
+{
+  FARPROC lpProc;
+
+  CheckMenuItem(GetMenu(hwndMain), IDM_ShowMoveHistory, MF_CHECKED);
+
+  if( moveHistoryDialog ) {
+    SendMessage( moveHistoryDialog, WM_INITDIALOG, 0, 0 );
+
+    if( ! moveHistoryDialogUp ) {
+        ShowWindow(moveHistoryDialog, SW_SHOW);
+    }
+  }
+  else {
+    lpProc = MakeProcInstance( (FARPROC) HistoryDialogProc, hInst );
+
+    /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
+    CreateDialog( hInst, MAKEINTRESOURCE(DLG_MoveHistory), hwndMain, (DLGPROC)lpProc );
+
+    FreeProcInstance(lpProc);
+  }
+
+  moveHistoryDialogUp = TRUE;
+}
+
+VOID MoveHistoryPopDown()
+{
+  CheckMenuItem(GetMenu(hwndMain), IDM_ShowMoveHistory, MF_UNCHECKED);
+
+  if( moveHistoryDialog ) {
+      ShowWindow(moveHistoryDialog, SW_HIDE);
+  }
+
+  moveHistoryDialogUp = FALSE;
+}
+
+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( moveHistoryDialog ) {
+        SendMessage( moveHistoryDialog, WM_REFRESH_HISTORY, 0, 0 );
+    }
+}
+
+BOOL MoveHistoryIsUp()
+{
+    return moveHistoryDialogUp;
+}
index 751e67e..368c68d 100644 (file)
@@ -84,6 +84,8 @@
 #include "wsockerr.h"\r
 #include "defaults.h"\r
 \r
+#include "wsnap.h"
+
 int myrandom(void);
 void mysrandom(unsigned int seed);
 
@@ -144,7 +146,7 @@ char *icsNames;
 char *firstChessProgramNames;\r
 char *secondChessProgramNames;\r
 \r
-#define ARG_MAX 64*1024 /* [AS] For Roger Brown's very long list! */
+#define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */
 \r
 #define PALETTESIZE 256\r
 \r
@@ -415,7 +417,40 @@ VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
 void ParseIcsTextMenu(char *icsTextMenuString);\r
 VOID PopUpMoveDialog(char firstchar);\r
 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
+
+/* [AS] */
 int NewGameFRC();
+int GameListOptions();
+
+HWND moveHistoryDialog = NULL;
+BOOLEAN moveHistoryDialogUp = FALSE;
+
+WindowPlacement wpMoveHistory;
+
+HWND evalGraphDialog = NULL;
+BOOLEAN evalGraphDialogUp = FALSE;
+
+WindowPlacement wpEvalGraph;
+
+HWND engineOutputDialog = NULL;
+BOOLEAN engineOutputDialogUp = FALSE;
+
+WindowPlacement wpEngineOutput;
+
+VOID MoveHistoryPopUp();
+VOID MoveHistoryPopDown();
+VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );
+BOOL MoveHistoryIsUp();
+
+VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );
+VOID EvalGraphPopUp();
+VOID EvalGraphPopDown();
+BOOL EvalGraphIsUp();
+
+VOID EngineOutputPopUp();
+VOID EngineOutputPopDown();
+BOOL EngineOutputIsUp();
+VOID EngineOutputUpdate( int which, int depth, unsigned long nodes, int score, int time, char * pv );
 \r
 /*\r
  * Setting "frozen" should disable all user input other than deleting\r
@@ -463,7 +498,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpCmdLine, int nCmdShow)\r
 {\r
   MSG msg;\r
-  HANDLE hAccelMain, hAccelNoAlt;\r
+  HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
 \r
   debugFP = stderr;\r
 \r
@@ -479,6 +514,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 \r
   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
+  hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
 \r
   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
 \r
@@ -488,10 +524,14 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    0))   /* highest message to examine */\r
     {\r
       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
+          !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
+          !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
+          !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
          !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
          !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
          !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
          !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
+          !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
          !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
        TranslateMessage(&msg); /* Translates virtual key codes */\r
        DispatchMessage(&msg);  /* Dispatches message to window */\r
@@ -639,6 +679,19 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
     ConsoleCreate();\r
   }\r
 \r
+  /* [AS] Restore layout */
+  if( wpMoveHistory.visible ) {
+      MoveHistoryPopUp();
+  }
+
+  if( wpEvalGraph.visible ) {
+      EvalGraphPopUp();
+  }
+
+  if( wpEngineOutput.visible ) {
+      EngineOutputPopUp();
+  }
+
   InitBackEnd2();\r
 \r
   /* Make the window visible; update its client area; and return "success" */\r
@@ -1058,8 +1111,8 @@ ArgDescriptor argDescriptors[] = {
   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
   /* [AS] New features */
-  { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, TRUE },
-  { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, TRUE },
+  { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },
+  { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },
   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },
   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },
   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },
@@ -1081,6 +1134,37 @@ ArgDescriptor argDescriptors[] = {
   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },
   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },
   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },
+  { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },
+  { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },
+  { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },
+  { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },
+  { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },
+  { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },
+  { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },
+  { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },
+  { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },
+  { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },
+  { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },
+
+  /* [AS] Layout stuff */
+  { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },
+  { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },
+  { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },
+  { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },
+  { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },
+
+  { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },
+  { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },
+  { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },
+  { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },
+  { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },
+
+  { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },
+  { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },
+  { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },
+  { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },
+  { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },
+
 #ifdef ZIPPY\r
   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
@@ -1759,6 +1843,21 @@ InitAppData(LPSTR lpCmdLine)
   appData.nameOfDebugFile = "winboard.debug";
   appData.pgnEventHeader = "Computer Chess Game";
   appData.defaultFrcPosition = -1;
+  appData.gameListTags = GLT_DEFAULT_TAGS;
+  appData.saveOutOfBookInfo = TRUE;
+  appData.showEvalInMoveHistory = TRUE;
+  appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );
+  appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );
+  appData.highlightMoveWithArrow = FALSE;
+  appData.highlightArrowColor = ParseColorName( "#FFFF80" );
+  appData.useStickyWindows = TRUE;
+  appData.adjudicateDrawMoves = 0;
+  appData.autoDisplayComment = TRUE;
+  appData.autoDisplayTags = TRUE;
+
+  InitWindowPlacement( &wpMoveHistory );
+  InitWindowPlacement( &wpEvalGraph );
+  InitWindowPlacement( &wpEngineOutput );
 
 #ifdef ZIPPY\r
   appData.zippyTalk = ZIPPY_TALK;\r
@@ -1948,6 +2047,39 @@ SaveSettings(char* name)
     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
   }\r
 \r
+  /* [AS] Move history */
+  wpMoveHistory.visible = MoveHistoryIsUp();
+
+  if( moveHistoryDialog ) {
+    GetWindowPlacement(moveHistoryDialog, &wp);
+    wpMoveHistory.x = wp.rcNormalPosition.left;
+    wpMoveHistory.y = wp.rcNormalPosition.top;
+    wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
+    wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
+  }
+
+  /* [AS] Eval graph */
+  wpEvalGraph.visible = EvalGraphIsUp();
+
+  if( evalGraphDialog ) {
+    GetWindowPlacement(evalGraphDialog, &wp);
+    wpEvalGraph.x = wp.rcNormalPosition.left;
+    wpEvalGraph.y = wp.rcNormalPosition.top;
+    wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
+    wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
+  }
+
+  /* [AS] Engine output */
+  wpEngineOutput.visible = EngineOutputIsUp();
+
+  if( engineOutputDialog ) {
+    GetWindowPlacement(engineOutputDialog, &wp);
+    wpEngineOutput.x = wp.rcNormalPosition.left;
+    wpEngineOutput.y = wp.rcNormalPosition.top;
+    wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
+    wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
+  }
+
   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
     if (!ad->save) continue;\r
     switch (ad->argType) {\r
@@ -2334,6 +2466,10 @@ static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
 
     SelectObject( hdc, hbm_old );
 
+    if( hPieceFace[index] != NULL ) {
+        DeleteObject( hPieceFace[index] );
+    }
+
     hPieceFace[index] = hbm;
 }
 
@@ -2398,6 +2534,7 @@ void CreatePiecesFromFont()
         else {
             for( i=0; i<12; i++ ) {
                 hPieceMask[i] = NULL;
+                hPieceFace[i] = NULL;
             }
         }
 
@@ -2965,7 +3102,7 @@ DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y,
 \r
   /* [AS] Use font-based pieces if needed */
   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
-    /* Create piece bitmaps, or do nothing if piece set is up to data */
+    /* Create piece bitmaps, or do nothing if piece set is up to date */
     CreatePiecesFromFont();
 
     if( fontBitmapSquareSize == squareSize ) {
@@ -3110,6 +3247,295 @@ VOID RebuildTextureSquareInfo()
     }
 }
 
+/* [AS] Arrow highlighting support */
+
+static int A_WIDTH = 5; /* Width of arrow body */
+
+#define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */
+#define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */
+
+static double Sqr( double x )
+{
+    return x*x;
+}
+
+static int Round( double x )
+{
+    return (int) (x + 0.5);
+}
+
+/* Draw an arrow between two points using current settings */
+VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
+{
+    POINT arrow[7];
+    double dx, dy, j, k, x, y;
+
+    if( d_x == s_x ) {
+        int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
+
+        arrow[0].x = s_x + A_WIDTH;
+        arrow[0].y = s_y;
+
+        arrow[1].x = s_x + A_WIDTH;
+        arrow[1].y = d_y - h;
+
+        arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
+        arrow[2].y = d_y - h;
+
+        arrow[3].x = d_x;
+        arrow[3].y = d_y;
+
+        arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
+        arrow[4].y = d_y - h;
+
+        arrow[5].x = s_x - A_WIDTH;
+        arrow[5].y = d_y - h;
+
+        arrow[6].x = s_x - A_WIDTH;
+        arrow[6].y = s_y;
+    }
+    else if( d_y == s_y ) {
+        int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
+
+        arrow[0].x = s_x;
+        arrow[0].y = s_y + A_WIDTH;
+
+        arrow[1].x = d_x - w;
+        arrow[1].y = s_y + A_WIDTH;
+
+        arrow[2].x = d_x - w;
+        arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
+
+        arrow[3].x = d_x;
+        arrow[3].y = d_y;
+
+        arrow[4].x = d_x - w;
+        arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
+
+        arrow[5].x = d_x - w;
+        arrow[5].y = s_y - A_WIDTH;
+
+        arrow[6].x = s_x;
+        arrow[6].y = s_y - A_WIDTH;
+    }
+    else {
+        /* [AS] Needed a lot of paper for this! :-) */
+        dy = (double) (d_y - s_y) / (double) (d_x - s_x);
+        dx = (double) (s_x - d_x) / (double) (s_y - d_y);
+
+        j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
+
+        k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
+
+        x = s_x;
+        y = s_y;
+
+        arrow[0].x = Round(x - j);
+        arrow[0].y = Round(y + j*dx);
+
+        arrow[1].x = Round(x + j);
+        arrow[1].y = Round(y - j*dx);
+
+        if( d_x > s_x ) {
+            x = (double) d_x - k;
+            y = (double) d_y - k*dy;
+        }
+        else {
+            x = (double) d_x + k;
+            y = (double) d_y + k*dy;
+        }
+
+        arrow[2].x = Round(x + j);
+        arrow[2].y = Round(y - j*dx);
+
+        arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
+        arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
+
+        arrow[4].x = d_x;
+        arrow[4].y = d_y;
+
+        arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
+        arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
+
+        arrow[6].x = Round(x - j);
+        arrow[6].y = Round(y + j*dx);
+    }
+
+    Polygon( hdc, arrow, 7 );
+}
+
+/* [AS] Draw an arrow between two squares */
+VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
+{
+    int s_x, s_y, d_x, d_y;
+    HPEN hpen;
+    HPEN holdpen;
+    HBRUSH hbrush;
+    HBRUSH holdbrush;
+    LOGBRUSH stLB;
+
+    if( s_col == d_col && s_row == d_row ) {
+        return;
+    }
+
+    /* Get source and destination points */
+    SquareToPos( s_row, s_col, &s_x, &s_y);
+    SquareToPos( d_row, d_col, &d_x, &d_y);
+
+    if( d_y > s_y ) {
+        d_y += squareSize / 4;
+    }
+    else if( d_y < s_y ) {
+        d_y += 3 * squareSize / 4;
+    }
+    else {
+        d_y += squareSize / 2;
+    }
+
+    if( d_x > s_x ) {
+        d_x += squareSize / 4;
+    }
+    else if( d_x < s_x ) {
+        d_x += 3 * squareSize / 4;
+    }
+    else {
+        d_x += squareSize / 2;
+    }
+
+    s_x += squareSize / 2;
+    s_y += squareSize / 2;
+
+    /* Adjust width */
+    A_WIDTH = squareSize / 14;
+
+    /* Draw */
+    stLB.lbStyle = BS_SOLID;
+    stLB.lbColor = appData.highlightArrowColor;
+    stLB.lbHatch = 0;
+
+    hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
+    holdpen = SelectObject( hdc, hpen );
+    hbrush = CreateBrushIndirect( &stLB );
+    holdbrush = SelectObject( hdc, hbrush );
+
+    DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
+
+    SelectObject( hdc, holdpen );
+    SelectObject( hdc, holdbrush );
+    DeleteObject( hpen );
+    DeleteObject( hbrush );
+}
+
+BOOL HasHighlightInfo()
+{
+    BOOL result = FALSE;
+
+    if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
+        highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
+    {
+        result = TRUE;
+    }
+
+    return result;
+}
+
+BOOL IsDrawArrowEnabled()
+{
+    BOOL result = FALSE;
+
+    if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
+        result = TRUE;
+    }
+
+    return result;
+}
+
+VOID DrawArrowHighlight( HDC hdc )
+{
+    if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
+        DrawArrowBetweenSquares( hdc,
+            highlightInfo.sq[0].x, highlightInfo.sq[0].y,
+            highlightInfo.sq[1].x, highlightInfo.sq[1].y );
+    }
+}
+
+HRGN GetArrowHighlightClipRegion( HDC hdc )
+{
+    HRGN result = NULL;
+
+    if( HasHighlightInfo() ) {
+        int x1, y1, x2, y2;
+        int sx, sy, dx, dy;
+
+        SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
+        SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
+
+        sx = MIN( x1, x2 );
+        sy = MIN( y1, y2 );
+        dx = MAX( x1, x2 ) + squareSize;
+        dy = MAX( y1, y2 ) + squareSize;
+
+        result = CreateRectRgn( sx, sy, dx, dy );
+    }
+
+    return result;
+}
+
+/*
+    Warning: this function modifies the behavior of several other functions.
+
+    Basically, Winboard is optimized to avoid drawing the whole board if not strictly
+    needed. Unfortunately, the decision whether or not to perform a full or partial
+    repaint is scattered all over the place, which is not good for features such as
+    "arrow highlighting" that require a full repaint of the board.
+
+    So, I've tried to patch the code where I thought it made sense (e.g. after or during
+    user interaction, when speed is not so important) but especially to avoid errors
+    in the displayed graphics.
+
+    In such patched places, I always try refer to this function so there is a single
+    place to maintain knowledge.
+
+    To restore the original behavior, just return FALSE unconditionally.
+*/
+BOOL IsFullRepaintPreferrable()
+{
+    BOOL result = FALSE;
+
+    if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
+        /* Arrow may appear on the board */
+        result = TRUE;
+    }
+
+    return result;
+}
+
+/*
+    This function is called by DrawPosition to know whether a full repaint must
+    be forced or not.
+
+    Only DrawPosition may directly call this function, which makes use of
+    some state information. Other function should call DrawPosition specifying
+    the repaint flag, and can use IsFullRepaintPreferrable if needed.
+*/
+BOOL DrawPositionNeedsFullRepaint()
+{
+    BOOL result = FALSE;
+
+    /*
+        Probably a slightly better policy would be to trigger a full repaint
+        when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
+        but animation is fast enough that it's difficult to notice.
+    */
+    if( animInfo.piece == EmptySquare ) {
+        if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {
+            result = TRUE;
+        }
+    }
+
+    return result;
+}
+
 VOID\r
 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
 {\r
@@ -3208,6 +3634,21 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
    */\r
   Boolean fullrepaint = repaint;\r
 \r
+  if( DrawPositionNeedsFullRepaint() ) {
+      fullrepaint = TRUE;
+  }
+
+#if 0
+  if( fullrepaint ) {
+      static int repaint_count = 0;
+      char buf[128];
+
+      repaint_count++;
+      sprintf( buf, "FULL repaint: %d\n", repaint_count );
+      OutputDebugString( buf );
+  }
+#endif
+
   if (board == NULL) {\r
     if (!lastReqValid) {\r
       return;\r
@@ -3410,6 +3851,11 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
   DrawGridOnDC(hdcmem);\r
   DrawHighlightsOnDC(hdcmem);\r
   DrawBoardOnDC(hdcmem, board, tmphdc);\r
+
+  if( appData.highlightMoveWithArrow ) {
+    DrawArrowHighlight(hdcmem);
+  }
+
   DrawCoordsOnDC(hdcmem);\r
 \r
   /* Put the dragged piece back into place and draw it */\r
@@ -3587,7 +4033,9 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   POINT pt;\r
   static int recursive = 0;\r
   HMENU hmenu;\r
+  BOOLEAN needsRedraw = FALSE;
   BOOLEAN saveAnimate;\r
+  BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
   static BOOLEAN sameAgain = FALSE;\r
 \r
   if (recursive) {\r
@@ -3636,7 +4084,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       }\r
       if (!appData.highlightLastMove) {\r
         ClearHighlights();\r
-       DrawPosition(FALSE, NULL);\r
+       DrawPosition(forceFullRepaint || FALSE, NULL);
       }\r
       fromX = fromY = -1;\r
       dragInfo.start.x = dragInfo.start.y = -1;\r
@@ -3647,7 +4095,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     } else if (fromX == x && fromY == y) {\r
       /* Downclick on same square again */\r
       ClearHighlights();\r
-      DrawPosition(FALSE, NULL);\r
+      DrawPosition(forceFullRepaint || FALSE, NULL);
       sameAgain = TRUE;  \r
     } else if (fromX != -1) {\r
       /* Downclick on different square */\r
@@ -3668,11 +4116,11 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            UserMoveEvent(fromX, fromY, toX, toY, 'q');\r
            if (!appData.highlightLastMove) {\r
              ClearHighlights();\r
-             DrawPosition(FALSE, NULL);\r
+             DrawPosition(forceFullRepaint || FALSE, NULL);
            }\r
          } else {\r
            SetHighlights(fromX, fromY, toX, toY);\r
-           DrawPosition(FALSE, NULL);\r
+           DrawPosition(forceFullRepaint || FALSE, NULL);
            PromotionPopup(hwnd);\r
          }\r
        } else {        /* not a promotion */\r
@@ -3684,7 +4132,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);\r
          if (appData.animate && !appData.highlightLastMove) {\r
            ClearHighlights();\r
-           DrawPosition(FALSE, NULL);\r
+           DrawPosition(forceFullRepaint || FALSE, NULL);
          }\r
        }\r
        if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
@@ -3692,7 +4140,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        break;\r
       }\r
       ClearHighlights();\r
-      DrawPosition(FALSE, NULL);\r
+      DrawPosition(forceFullRepaint || FALSE, NULL);
     }\r
     /* First downclick, or restart on a square with same color piece */\r
     if (!frozen && OKToStartUserMove(x, y)) {\r
@@ -3707,6 +4155,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       fromX = fromY = -1;\r
       dragInfo.start.x = dragInfo.start.y = -1;\r
       dragInfo.from = dragInfo.start;\r
+      DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
     }\r
     break;\r
 \r
@@ -3725,7 +4174,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        /* First square clicked: start click-click move */\r
        SetHighlights(fromX, fromY, -1, -1);\r
       }\r
-      DrawPosition(FALSE, NULL);\r
+      DrawPosition(forceFullRepaint || FALSE, NULL);
     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
       /* Errant click; ignore */\r
       break;\r
@@ -3740,7 +4189,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        if (appData.alwaysPromoteToQueen) {\r
          UserMoveEvent(fromX, fromY, toX, toY, 'q');\r
        } else {\r
-         DrawPosition(FALSE, NULL);\r
+         DrawPosition(forceFullRepaint || FALSE, NULL);
          PromotionPopup(hwnd);\r
        }\r
       } else {\r
@@ -3754,7 +4203,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       }\r
       if (appData.animate || appData.animateDragging ||\r
          appData.highlightDragging || gotPremove) {\r
-       DrawPosition(FALSE, NULL);\r
+       DrawPosition(forceFullRepaint || FALSE, NULL);
       }\r
     }\r
     dragInfo.start.x = dragInfo.start.y = -1; \r
@@ -3764,14 +4213,22 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   case WM_MOUSEMOVE:\r
     if ((appData.animateDragging || appData.highlightDragging)\r
        && (wParam & MK_LBUTTON)\r
-       && dragInfo.from.x >= 0) {\r
+       && dragInfo.from.x >= 0)
+    {
+      BOOL full_repaint = FALSE;
+
       if (appData.animateDragging) {\r
        dragInfo.pos = pt;\r
       }\r
       if (appData.highlightDragging) {\r
        SetHighlights(fromX, fromY, x, y);\r
+        if( IsDrawArrowEnabled() && (x < 0 || x > 7 || y < 0 || y > y) ) {
+            full_repaint = TRUE;
       }\r
-      DrawPosition(FALSE, NULL);\r
+      }
+
+      DrawPosition( full_repaint, NULL);
+
       dragInfo.lastpos = dragInfo.pos;\r
     }\r
     break;\r
@@ -4059,6 +4516,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   FILE *f;\r
   UINT number;\r
   char fileTitle[MSG_SIZ];\r
+  static SnapData sd;
 \r
   switch (message) {\r
 \r
@@ -4233,11 +4691,45 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       PasteGameFromClipboard();\r
       break;\r
 \r
+    case IDM_CopyGameListToClipboard:
+      CopyGameListToClipboard();
+      break;
+
     /* [AS] Autodetect FEN or PGN data */
-    case IDM_Paste:
+    case IDM_PasteAny:
       PasteGameOrFENFromClipboard();
       break;
 
+    /* [AS] Move history */
+    case IDM_ShowMoveHistory:
+        if( MoveHistoryIsUp() ) {
+            MoveHistoryPopDown();
+        }
+        else {
+            MoveHistoryPopUp();
+        }
+        break;
+
+    /* [AS] Eval graph */
+    case IDM_ShowEvalGraph:
+        if( EvalGraphIsUp() ) {
+            EvalGraphPopDown();
+        }
+        else {
+            EvalGraphPopUp();
+        }
+        break;
+
+    /* [AS] Engine output */
+    case IDM_ShowEngineOutput:
+        if( EngineOutputIsUp() ) {
+            EngineOutputPopDown();
+        }
+        else {
+            EngineOutputPopUp();
+        }
+        break;
+
     /* [AS] User adjudication */
     case IDM_UserAdjudication_White:
         UserAdjudicationEvent( +1 );
@@ -4251,6 +4743,11 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
         UserAdjudicationEvent( 0 );
         break;
 
+    /* [AS] Game list options dialog */
+    case IDM_GameListOptions:
+      GameListOptions();
+      break;
+
     case IDM_CopyPosition:\r
       CopyFENToClipboard();\r
       break;\r
@@ -4462,12 +4959,17 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 \r
     case IDM_GeneralOptions:\r
       GeneralOptionsPopup(hwnd);\r
+      DrawPosition(TRUE, NULL);
       break;\r
 \r
     case IDM_BoardOptions:\r
       BoardOptionsPopup(hwnd);\r
       break;\r
 \r
+    case IDM_EnginePlayOptions:
+      EnginePlayOptionsPopup(hwnd);
+      break;
+
     case IDM_IcsOptions:\r
       IcsOptionsPopup(hwnd);\r
       break;\r
@@ -4718,11 +5220,31 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     InputEvent(hwnd, message, wParam, lParam);\r
     break;\r
 \r
+  /* [AS] Also move "attached" child windows */
+  case WM_WINDOWPOSCHANGING:
+    if( hwnd == hwndMain && appData.useStickyWindows ) {
+        LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
+
+        if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
+            /* Window is moving */
+            RECT rcMain;
+
+            GetWindowRect( hwnd, &rcMain );
+
+            ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
+            ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
+            ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
+        }
+    }
+    break;
+
+  /* [AS] Snapping */
   case WM_ENTERSIZEMOVE:\r
     if (hwnd == hwndMain) {\r
       doingSizing = TRUE;\r
       lastSizing = 0;\r
     }\r
+    return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
     break;\r
 \r
   case WM_SIZING:\r
@@ -4731,6 +5253,9 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     }\r
     break;\r
 \r
+  case WM_MOVING:
+      return OnMoving( &sd, hwnd, wParam, lParam );
+
   case WM_EXITSIZEMOVE:\r
     if (hwnd == hwndMain) {\r
       RECT client;\r
@@ -4740,6 +5265,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       ResizeBoard(client.right, client.bottom, lastSizing);\r
       lastSizing = 0;\r
     }\r
+    return OnExitSizeMove( &sd, hwnd, wParam, lParam );
     break;\r
 \r
   case WM_DESTROY: /* message: window being destroyed */\r
@@ -5132,8 +5658,7 @@ ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX,
   EndDeferWindowPos(cl.hdwp);\r
 }\r
 \r
-/* Center one window over another */\r
-BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
+BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
 {\r
     RECT    rChild, rParent;\r
     int     wChild, hChild, wParent, hParent;\r
@@ -5165,7 +5690,13 @@ BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
     }\r
 \r
     /* Calculate new Y position, then adjust for screen */\r
+    if( mode == 0 ) {
     yNew = rParent.top  + ((hParent - hChild) /2);\r
+    }
+    else {
+        yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
+    }
+
     if (yNew < 0) {\r
        yNew = 0;\r
     } else if ((yNew+hChild) > hScreen) {\r
@@ -5177,6 +5708,12 @@ BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
                         xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
 }\r
 \r
+/* Center one window over another */
+BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
+{
+    return CenterWindowEx( hwndChild, hwndParent, 0 );
+}
+
 /*---------------------------------------------------------------------------*\\r
  *\r
  * Startup Dialog functions\r
@@ -5597,7 +6134,7 @@ TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
   case WM_INITDIALOG:\r
     move[0] = (char) lParam;\r
     move[1] = NULLCHAR;\r
-    CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
+    CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
     hInput = GetDlgItem(hDlg, OPT_Move);\r
     SetWindowText(hInput, move);\r
     SetFocus(hInput);\r
@@ -6244,6 +6781,7 @@ ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 LRESULT CALLBACK\r
 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
 {\r
+  static SnapData sd;
   static HWND hText, hInput, hFocus;\r
   InputSource *is = consoleInputSource;\r
   RECT rect;\r
@@ -6330,7 +6868,21 @@ ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
     mmi->ptMinTrackSize.x = 100;\r
     mmi->ptMinTrackSize.y = 100;\r
     break;\r
+
+  /* [AS] Snapping */
+  case WM_ENTERSIZEMOVE:
+    return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
+
+  case WM_SIZING:
+    return OnSizing( &sd, hDlg, wParam, lParam );
+
+  case WM_MOVING:
+    return OnMoving( &sd, hDlg, wParam, lParam );
+
+  case WM_EXITSIZEMOVE:
+    return OnExitSizeMove( &sd, hDlg, wParam, lParam );
   }\r
+
   return DefWindowProc(hDlg, message, wParam, lParam);\r
 }\r
 \r
@@ -7319,6 +7871,207 @@ int NewGameFRC()
     return result;
 }
 
+/* [AS] Game list options */
+typedef struct {
+    char id;
+    char * name;
+} GLT_Item;
+
+static GLT_Item GLT_ItemInfo[] = {
+    { GLT_EVENT,      "Event" },
+    { GLT_SITE,       "Site" },
+    { GLT_DATE,       "Date" },
+    { GLT_ROUND,      "Round" },
+    { GLT_PLAYERS,    "Players" },
+    { GLT_RESULT,     "Result" },
+    { GLT_WHITE_ELO,  "White Rating" },
+    { GLT_BLACK_ELO,  "Black Rating" },
+    { GLT_TIME_CONTROL,"Time Control" },
+    { GLT_VARIANT,    "Variant" },
+    { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },
+    { 0, 0 }
+};
+
+const char * GLT_FindItem( char id )
+{
+    const char * result = 0;
+
+    GLT_Item * list = GLT_ItemInfo;
+
+    while( list->id != 0 ) {
+        if( list->id == id ) {
+            result = list->name;
+            break;
+        }
+
+        list++;
+    }
+
+    return result;
+}
+
+void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )
+{
+    const char * name = GLT_FindItem( id );
+
+    if( name != 0 ) {
+        if( index >= 0 ) {
+            SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );
+        }
+        else {
+            SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );
+        }
+    }
+}
+
+void GLT_TagsToList( HWND hDlg, char * tags )
+{
+    char * pc = tags;
+
+    SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
+
+    while( *pc ) {
+        GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );
+        pc++;
+    }
+
+    SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );
+
+    pc = GLT_ALL_TAGS;
+
+    while( *pc ) {
+        if( strchr( tags, *pc ) == 0 ) {
+            GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );
+        }
+        pc++;
+    }
+
+    SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
+}
+
+char GLT_ListItemToTag( HWND hDlg, int index )
+{
+    char result = '\0';
+    char name[128];
+
+    GLT_Item * list = GLT_ItemInfo;
+
+    if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {
+        while( list->id != 0 ) {
+            if( strcmp( list->name, name ) == 0 ) {
+                result = list->id;
+                break;
+            }
+
+            list++;
+        }
+    }
+
+    return result;
+}
+
+void GLT_MoveSelection( HWND hDlg, int delta )
+{
+    int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
+    int idx2 = idx1 + delta;
+    int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
+
+    if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
+        char buf[128];
+
+        SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
+        SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
+        SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
+        SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
+    }
+}
+
+LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    static char glt[64];
+    static char * lpUserGLT;
+
+    switch( message )
+    {
+    case WM_INITDIALOG:
+        lpUserGLT = (char *) lParam;
+
+        strcpy( glt, lpUserGLT );
+
+        CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
+
+        /* Initialize list */
+        GLT_TagsToList( hDlg, glt );
+
+        SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
+
+        break;
+
+    case WM_COMMAND:
+        switch( LOWORD(wParam) ) {
+        case IDOK:
+            {
+                char * pc = lpUserGLT;
+                int idx = 0;
+                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
+                char id;
+
+                do {
+                    id = GLT_ListItemToTag( hDlg, idx );
+
+                    *pc++ = id;
+                    idx++;
+                } while( id != '\0' );
+            }
+            EndDialog( hDlg, 0 );
+            return TRUE;
+        case IDCANCEL:
+            EndDialog( hDlg, 1 );
+            return TRUE;
+
+        case IDC_GLT_Default:
+            strcpy( glt, GLT_DEFAULT_TAGS );
+            GLT_TagsToList( hDlg, glt );
+            return TRUE;
+
+        case IDC_GLT_Restore:
+            strcpy( glt, lpUserGLT );
+            GLT_TagsToList( hDlg, glt );
+            return TRUE;
+
+        case IDC_GLT_Up:
+            GLT_MoveSelection( hDlg, -1 );
+            return TRUE;
+
+        case IDC_GLT_Down:
+            GLT_MoveSelection( hDlg, +1 );
+            return TRUE;
+        }
+
+        break;
+    }
+
+    return FALSE;
+}
+
+int GameListOptions()
+{
+    char glt[64];
+    int result;
+    FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
+
+    strcpy( glt, appData.gameListTags );
+
+    result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );
+
+    if( result == 0 ) {
+        /* [AS] Memory leak here! */
+        appData.gameListTags = strdup( glt );
+    }
+
+    return result;
+}
+
 \r
 VOID\r
 DisplayIcsInteractionTitle(char *str)\r
@@ -8441,6 +9194,10 @@ AnalysisPopUp(char* title, char* str)
   FARPROC lpProc;\r
   char *p, *q;\r
 \r
+  /* [AS] */
+  EngineOutputPopUp();
+  return;
+
   if (str == NULL) str = "";\r
   p = (char *) malloc(2 * strlen(str) + 2);\r
   q = p;\r
@@ -8653,11 +9410,9 @@ Tween(start, mid, finish, factor, frames, nFrames)
 }\r
 \r
 void\r
-HistorySet(char movelist[][2*MOVE_LEN], int first, int last, int current)\r
+HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
 {\r
-  /* Currently not implemented in WinBoard */\r
-#if 1
-    /* [AS] Let's see what this function is for... */
+#if 0
     char buf[256];
 \r
     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",
@@ -8665,4 +9420,22 @@ HistorySet(char movelist[][2*MOVE_LEN], int first, int last, int current)
 \r
     OutputDebugString( buf );
 #endif
+
+    MoveHistorySet( movelist, first, last, current, pvInfoList );
+
+    EvalGraphSet( first, last, current, pvInfoList );
+}
+
+void SetProgramStats( int which, int depth, unsigned long nodes, int score, int time, char * pv )
+{
+#if 0
+    char buf[1024];
+
+    sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",
+        which, depth, nodes, score / 100.0, time / 100.0, pv == 0 ? "n/a" : pv );
+
+    OutputDebugString( buf );
+#endif
+
+    EngineOutputUpdate( which, depth, nodes, score, time, pv );
 }
index 5823b2e..890e071 100644 (file)
@@ -163,3 +163,18 @@ extern MyFont *font[NUM_SIZES][NUM_FONTS];
 \r
 #define COPY_TMP "wbcopy.tmp"\r
 #define PASTE_TMP "wbpaste.tmp"\r
+
+/* [AS] Layout management */
+typedef struct {
+    Boolean visible;
+    int x;
+    int y;
+    int width;
+    int height;
+} WindowPlacement;
+
+VOID InitWindowPlacement( WindowPlacement * wp );
+
+VOID RestoreWindowPlacement( HWND hWnd, WindowPlacement * wp );
+
+VOID ReattachAfterMove( LPRECT lprcOldPos, int new_x, int new_y, HWND hWndChild, WindowPlacement * pwpChild );
index 085f5b9..b4635b2 100644 (file)
@@ -30,20 +30,23 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
 // Dialog\r
 //\r
 \r
-ABOUTBOX DIALOG DISCARDABLE  22, 17, 167, 67\r
+ABOUTBOX DIALOG DISCARDABLE  22, 17, 180, 113
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
 CAPTION "About WinBoard"\r
 FONT 8, "MS Sans Serif"\r
 BEGIN\r
-    DEFPUSHBUTTON   "OK",IDOK,131,47,32,14,WS_GROUP\r
-    ICON            "icon_white",-1,3,6,20,20
+    DEFPUSHBUTTON   "OK",IDOK,126,94,50,14,WS_GROUP
+    ICON            "icon_white",IDC_STATIC,3,6,20,20
     LTEXT           "Chessboard for Windows",400,25,15,121,8\r
     LTEXT           "Copyright 1991 Digital Equipment Corporation",201,6,34,\r
                     149,8\r
     LTEXT           "Enhancements Copyright 1992-2003   Free Software Foundation",\r
-                    OPT_TCtext1,6,44,121,17\r
+                    OPT_TCtext1,6,45,121,17
     CONTROL         "",OPT_TCTime,"Static",SS_BLACKRECT,4,28,159,1\r
-    LTEXT           "WinBoard 0.0.0",ABOUTBOX_Version,25,5,142,8\r
+    LTEXT           "WinBoard 0.0.0",ABOUTBOX_Version,25,5,61,8
+    LTEXT           "Enhancements Copyright 2005\r\nAlessandro Scotti",
+                    IDC_STATIC,6,65,170,24
+    LTEXT           "(unofficial version ""X"")",IDC_STATIC,88,4,71,8
 END\r
 \r
 DLG_TimeControl DIALOG DISCARDABLE  6, 18, 165, 114
@@ -68,20 +71,20 @@ BEGIN
     PUSHBUTTON      "Cancel",IDCANCEL,90,95,40,14
 END\r
 \r
-DLG_LoadOptions DIALOG DISCARDABLE  10, 18, 144, 55\r
+DLG_LoadOptions DIALOG DISCARDABLE  10, 18, 136, 55
 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU\r
 CAPTION "Load Game Options"\r
 FONT 8, "MS Sans Serif"\r
 BEGIN\r
     CONTROL         "Load games with automatic stepping",OPT_Autostep,"Button",\r
-                    BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,4,4,136,10\r
-    EDITTEXT        OPT_ASTimeDelay,23,18,28,12,ES_AUTOHSCROLL\r
-    LTEXT           "seconds per move",OPT_AStext1,57,20,60,8,NOT WS_GROUP\r
-    PUSHBUTTON      "OK",IDOK,24,37,40,14,WS_GROUP\r
-    PUSHBUTTON      "Cancel",IDCANCEL,80,37,40,14\r
+                    BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,4,4,130,10
+    EDITTEXT        OPT_ASTimeDelay,16,16,28,14,ES_AUTOHSCROLL
+    LTEXT           "seconds per move",OPT_AStext1,46,20,60,8,NOT WS_GROUP
+    PUSHBUTTON      "OK",IDOK,26,36,50,14,WS_GROUP
+    PUSHBUTTON      "Cancel",IDCANCEL,82,36,50,14
 END\r
 \r
-DLG_SaveOptions DIALOG DISCARDABLE  6, 17, 133, 119\r
+DLG_SaveOptions DIALOG DISCARDABLE  6, 17, 178, 119
 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU\r
 CAPTION "Save Game Options"\r
 FONT 8, "MS Sans Serif"\r
@@ -89,17 +92,19 @@ BEGIN
     CONTROL         "Save games automatically",OPT_Autosave,"Button",\r
                     BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,4,4,97,10\r
     CONTROL         "Prompt for filename",OPT_AVPrompt,"Button",\r
-                    BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,18,18,75,10\r
-    CONTROL         "To file:",OPT_AVToFile,"Button",BS_AUTORADIOBUTTON,18,\r
-                    31,36,10\r
-    EDITTEXT        OPT_AVFilename,18,44,97,12,ES_AUTOHSCROLL\r
-    GROUPBOX        "Save Style",801,4,63,125,28,WS_GROUP\r
-    CONTROL         "PGN",OPT_PGN,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,\r
-                    18,75,39,10\r
-    CONTROL         "Old",OPT_Old,"Button",BS_AUTORADIOBUTTON,73,75,39,10\r
-    PUSHBUTTON      "OK",IDOK,18,98,40,14,WS_GROUP\r
-    PUSHBUTTON      "Cancel",IDCANCEL,74,98,40,14\r
-    PUSHBUTTON      "Browse...",OPT_AVBrowse,76,31,39,14\r
+                    BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,16,18,75,10
+    CONTROL         "To file:",OPT_AVToFile,"Button",BS_AUTORADIOBUTTON,16,
+                    32,36,10
+    EDITTEXT        OPT_AVFilename,54,30,97,14,ES_AUTOHSCROLL
+    PUSHBUTTON      "...",OPT_AVBrowse,156,30,18,14
+    GROUPBOX        "Save As: ",801,4,48,170,28,WS_GROUP
+    CONTROL         "PGN",OPT_PGN,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,8,
+                    60,39,10
+    CONTROL         "Old",OPT_Old,"Button",BS_AUTORADIOBUTTON,54,60,114,10
+    CONTROL         "Save out of book info in PGN",OPT_OutOfBookInfo,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,4,82,109,10
+    PUSHBUTTON      "OK",IDOK,68,100,50,14,WS_GROUP
+    PUSHBUTTON      "Cancel",IDCANCEL,124,100,50,14
 END\r
 \r
 1536 DIALOG DISCARDABLE  36, 24, 264, 134\r
@@ -132,32 +137,32 @@ BEGIN
     PUSHBUTTON      "Net&work...",1037,208,113,50,14,WS_GROUP\r
 END\r
 \r
-DLG_CommPort DIALOG DISCARDABLE  25, 30, 159, 98\r
+DLG_CommPort DIALOG DISCARDABLE  25, 30, 220, 79
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
 CAPTION "Communication Port Settings"\r
 FONT 8, "Helv"\r
 BEGIN\r
+    PUSHBUTTON      "OK",IDOK,106,60,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,164,60,50,14
     RTEXT           "&Port:",-1,4,6,40,10\r
     COMBOBOX        OPT_Port,49,4,55,60,CBS_DROPDOWN | WS_VSCROLL | \r
                     WS_TABSTOP\r
-    RTEXT           "Data &Rate:",-1,4,21,40,10,NOT WS_GROUP\r
-    COMBOBOX        OPT_DataRate,49,19,55,100,CBS_DROPDOWN | WS_VSCROLL | \r
+    RTEXT           "Data &Rate:",-1,114,6,40,10,NOT WS_GROUP
+    COMBOBOX        OPT_DataRate,159,4,55,100,CBS_DROPDOWN | WS_VSCROLL |
                     WS_TABSTOP\r
-    RTEXT           "Data &Bits:",-1,4,36,40,10,NOT WS_GROUP\r
-    COMBOBOX        OPT_Bits,49,34,55,60,CBS_DROPDOWNLIST | CBS_HASSTRINGS | \r
+    RTEXT           "Data &Bits:",-1,4,25,40,10,NOT WS_GROUP
+    COMBOBOX        OPT_Bits,49,22,55,60,CBS_DROPDOWNLIST | CBS_HASSTRINGS |
                     WS_VSCROLL | WS_TABSTOP\r
-    RTEXT           "P&arity:",-1,4,51,40,10,NOT WS_GROUP\r
-    COMBOBOX        OPT_Parity,49,49,55,60,CBS_DROPDOWNLIST | CBS_HASSTRINGS | \r
-                    WS_VSCROLL | WS_TABSTOP\r
-    RTEXT           "&Stop Bits:",-1,4,66,40,10,NOT WS_GROUP\r
-    COMBOBOX        OPT_StopBits,49,64,55,60,CBS_DROPDOWNLIST | \r
+    RTEXT           "P&arity:",-1,114,24,40,10,NOT WS_GROUP
+    COMBOBOX        OPT_Parity,159,22,55,60,CBS_DROPDOWNLIST |
+                    CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP
+    RTEXT           "&Stop Bits:",-1,4,42,40,10,NOT WS_GROUP
+    COMBOBOX        OPT_StopBits,49,40,55,60,CBS_DROPDOWNLIST |
                     CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP\r
-    RTEXT           "F&low:",-1,4,81,40,10,NOT WS_GROUP\r
-    DEFPUSHBUTTON   "OK",IDOK,115,4,40,14\r
-    PUSHBUTTON      "Cancel",IDCANCEL,115,24,40,14\r
-    COMBOBOX        OPT_Flow,49,79,55,60,CBS_DROPDOWNLIST | CBS_HASSTRINGS | \r
+    RTEXT           "F&low:",-1,114,42,40,10,NOT WS_GROUP
+    COMBOBOX        OPT_Flow,159,40,55,60,CBS_DROPDOWNLIST | CBS_HASSTRINGS |
                     WS_VSCROLL | WS_TABSTOP\r
-    PUSHBUTTON      "&Help",OPT_SerialHelp,115,80,40,14,NOT WS_VISIBLE\r
+    PUSHBUTTON      "Help",OPT_SerialHelp,4,60,50,14,NOT WS_VISIBLE
 END\r
 \r
 DLG_EditComment DIALOG DISCARDABLE  6, 18, 306, 104\r
@@ -188,20 +193,23 @@ BEGIN
     LTEXT           "Promote pawn to:",501,6,8,58,8\r
 END\r
 \r
-ABOUTBOX2 DIALOG DISCARDABLE  22, 17, 281, 198\r
+ABOUTBOX2 DIALOG DISCARDABLE  22, 17, 281, 223
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
 CAPTION "About WinBoard"\r
 FONT 8, "MS Sans Serif"\r
 BEGIN\r
-    DEFPUSHBUTTON   "OK",IDOK,244,180,32,14,WS_GROUP\r
     RTEXT           "Chessboard for Windows",DLG_TimeControl,196,154,80,8\r
-    LTEXT           "Copyright 1991 Digital Equipment Corporation",201,4,167,\r
+    LTEXT           "Copyright 1991 Digital Equipment Corporation",201,4,168,
                     151,8\r
     LTEXT           "Enhancements Copyright 1992-2003   Free Software Foundation",\r
-                    OPT_TCtext1,4,177,126,17\r
+                    OPT_TCtext1,4,179,126,17
     CONTROL         "",OPT_TCTime,"Static",SS_BLACKRECT,4,164,272,1\r
     LTEXT           "WinBoard 0.0.0",ABOUTBOX_Version,4,154,64,8\r
     CONTROL         "galactic",IDC_STATIC,"Static",SS_BITMAP,4,4,15,13\r
+    DEFPUSHBUTTON   "OK",IDOK,226,204,50,14
+    LTEXT           "Enhancements Copyright 2005\r\nAlessandro Scotti",
+                    IDC_STATIC,4,199,183,20
+    LTEXT           "(unofficial version ""X"")",IDC_STATIC,68,154,71,8
 END\r
 \r
 DLG_GameList DIALOG DISCARDABLE  6, 18, 259, 153
@@ -269,37 +277,37 @@ BEGIN
     LTEXT           "Sorry Charlie",OPT_ErrorText,27,4,130,25\r
 END\r
 \r
-DLG_Colorize DIALOGEX 0, 0, 183, 52\r
+DLG_Colorize DIALOGEX 0, 0, 174, 61
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
 CAPTION "ICS Interaction Colors"\r
 FONT 8, "MS Sans Serif", 0, 0, 0x1
 BEGIN\r
-    PUSHBUTTON      "&Choose Color...",OPT_ChooseColor,15,29,51,14,WS_GROUP\r
+    PUSHBUTTON      "&Color...",OPT_ChooseColor,119,4,51,14,WS_GROUP
     CONTROL         "&Bold",OPT_Bold,"Button",BS_AUTOCHECKBOX | WS_GROUP | \r
-                    WS_TABSTOP,93,4,30,10\r
-    CONTROL         "&Italic",OPT_Italic,"Button",BS_AUTOCHECKBOX,93,14,30,\r
+                    WS_TABSTOP,4,24,30,10
+    CONTROL         "&Italic",OPT_Italic,"Button",BS_AUTOCHECKBOX,40,24,30,
                     10\r
-    CONTROL         "&Underline",OPT_Underline,"Button",BS_AUTOCHECKBOX,93,\r
+    CONTROL         "&Underline",OPT_Underline,"Button",BS_AUTOCHECKBOX,76,
                     24,45,10\r
-    CONTROL         "&Strikeout",OPT_Strikeout,"Button",BS_AUTOCHECKBOX,93,\r
-                    34,42,10\r
-    DEFPUSHBUTTON   "OK",IDOK,145,7,31,14,WS_GROUP\r
-    PUSHBUTTON      "Cancel",IDCANCEL,145,24,31,14\r
+    CONTROL         "&Strikeout",OPT_Strikeout,"Button",BS_AUTOCHECKBOX,128,
+                    24,42,10
+    DEFPUSHBUTTON   "OK",IDOK,64,42,50,14,WS_GROUP
+    PUSHBUTTON      "Cancel",IDCANCEL,120,42,50,14
     CONTROL         "",OPT_Sample,"RICHEDIT",ES_CENTER | ES_MULTILINE | \r
-                    ES_READONLY | WS_GROUP,4,9,75,15,WS_EX_CLIENTEDGE\r
+                    ES_READONLY | WS_GROUP,4,4,106,15,WS_EX_CLIENTEDGE
 END\r
 \r
-DLG_Question DIALOG DISCARDABLE  0, 0, 187, 60\r
+DLG_Question DIALOG DISCARDABLE  0, 0, 187, 77
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
 CAPTION "Question"\r
 FONT 8, "MS Sans Serif"\r
 BEGIN\r
-    EDITTEXT        OPT_QuestionInput,4,44,179,12,ES_AUTOHSCROLL\r
-    DEFPUSHBUTTON   "Enter",IDOK,133,4,50,14,WS_GROUP\r
-    PUSHBUTTON      "Cancel",IDCANCEL,133,24,50,14\r
+    EDITTEXT        OPT_QuestionInput,4,36,179,14,ES_AUTOHSCROLL
+    DEFPUSHBUTTON   "OK",IDOK,78,58,50,14,WS_GROUP
+    PUSHBUTTON      "Cancel",IDCANCEL,134,58,50,14
     LTEXT           "Enter a chess engine command or just type something stupid that will completely screw things up.",\r
-                    OPT_QuestionText,27,4,101,33\r
-    ICON            32514,IDC_STATIC,4,13,20,20\r
+                    OPT_QuestionText,30,2,153,28
+    ICON            32514,IDC_STATIC,4,4,21,20
 END\r
 \r
 DLG_Startup DIALOG DISCARDABLE  0, 0, 276, 127\r
@@ -337,14 +345,14 @@ BEGIN
     EDITTEXT        OPT_IndexNumber,54,0,155,13,ES_AUTOHSCROLL\r
 END\r
 \r
-DLG_TypeInMove DIALOG DISCARDABLE  0, 0, 186, 46\r
+DLG_TypeInMove DIALOG DISCARDABLE  0, 0, 206, 23
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
 CAPTION "Type in a move"\r
 FONT 8, "MS Sans Serif"\r
 BEGIN\r
-    DEFPUSHBUTTON   "OK",IDOK,129,7,50,14\r
-    PUSHBUTTON      "Cancel",IDCANCEL,129,24,50,14\r
-    EDITTEXT        OPT_Move,7,16,109,13,ES_AUTOHSCROLL\r
+    DEFPUSHBUTTON   "OK",IDOK,96,4,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,152,4,50,14
+    EDITTEXT        OPT_Move,4,4,86,13,ES_AUTOHSCROLL
 END\r
 \r
 DLG_Sound DIALOG DISCARDABLE  0, 0, 257, 95\r
@@ -373,7 +381,7 @@ BEGIN
     LTEXT           "Event:",IDC_STATIC,19,9,26,9\r
 END\r
 \r
-DLG_GeneralOptions DIALOG DISCARDABLE  0, 0, 271, 150
+DLG_GeneralOptions DIALOG DISCARDABLE  0, 0, 271, 162
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
 CAPTION "General Options"\r
 FONT 8, "MS Sans Serif"\r
@@ -381,25 +389,25 @@ BEGIN
     DEFPUSHBUTTON   "OK",IDOK,207,7,50,14\r
     PUSHBUTTON      "Cancel",IDCANCEL,208,25,50,14\r
     CONTROL         "Always on &Top",OPT_AlwaysOnTop,"Button",\r
-                    BS_AUTOCHECKBOX | WS_TABSTOP,16,6,79,10\r
+                    BS_AUTOCHECKBOX | WS_TABSTOP,4,6,79,10
     CONTROL         "Always &Queen",OPT_AlwaysQueen,"Button",BS_AUTOCHECKBOX | \r
-                    WS_TABSTOP,16,20,79,10\r
+                    WS_TABSTOP,4,20,79,10
     CONTROL         "Animate &Dragging",OPT_AnimateDragging,"Button",\r
-                    BS_AUTOCHECKBOX | WS_TABSTOP,16,34,79,10\r
+                    BS_AUTOCHECKBOX | WS_TABSTOP,4,34,79,10
     CONTROL         "&Animate Moving",OPT_AnimateMoving,"Button",\r
-                    BS_AUTOCHECKBOX | WS_TABSTOP,16,48,79,10\r
+                    BS_AUTOCHECKBOX | WS_TABSTOP,4,48,79,10
     CONTROL         "Auto &Flag",OPT_AutoFlag,"Button",BS_AUTOCHECKBOX | \r
-                    WS_TABSTOP,16,62,79,10\r
+                    WS_TABSTOP,4,62,79,10
     CONTROL         "Auto Flip &View",OPT_AutoFlipView,"Button",\r
-                    BS_AUTOCHECKBOX | WS_TABSTOP,16,76,79,10\r
+                    BS_AUTOCHECKBOX | WS_TABSTOP,4,76,79,10
     CONTROL         "Auto &Raise Board",OPT_AutoRaiseBoard,"Button",\r
-                    BS_AUTOCHECKBOX | WS_TABSTOP,16,90,71,10\r
+                    BS_AUTOCHECKBOX | WS_TABSTOP,4,90,71,10
     CONTROL         "&Blindfold",OPT_Blindfold,"Button",BS_AUTOCHECKBOX | \r
-                    WS_TABSTOP,16,104,79,10\r
+                    WS_TABSTOP,4,104,79,10
     CONTROL         "&Highlight Dragging",OPT_HighlightDragging,"Button",\r
-                    BS_AUTOCHECKBOX | WS_TABSTOP,16,118,79,10\r
+                    BS_AUTOCHECKBOX | WS_TABSTOP,4,118,79,10
     CONTROL         "Extended PGN Info",OPT_SaveExtPGN,"Button",
-                    BS_AUTOCHECKBOX | WS_TABSTOP,16,132,79,10
+                    BS_AUTOCHECKBOX | WS_TABSTOP,4,132,79,10
     CONTROL         "Highlight Last &Move",OPT_HighlightLastMove,"Button",\r
                     BS_AUTOCHECKBOX | WS_TABSTOP,109,6,79,10\r
     CONTROL         "Periodic &Updates",OPT_PeriodicUpdates,"Button",\r
@@ -418,165 +426,168 @@ BEGIN
                     BS_AUTOCHECKBOX | WS_TABSTOP,109,104,79,10\r
     CONTROL         "Test &Legality",OPT_TestLegality,"Button",\r
                     BS_AUTOCHECKBOX | WS_TABSTOP,109,118,79,10\r
-    CONTROL         "Hide Thinking From Human",OPT_HideThinkFromHuman,"Button",
-                    BS_AUTOCHECKBOX | WS_TABSTOP,109,132,102,10
+    CONTROL         "Hide Thinking from Human",OPT_HideThinkFromHuman,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,109,132,100,10
+    CONTROL         "Extra Info in Move History",OPT_ExtraInfoInMoveHistory,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,4,146,97,10
+    CONTROL         "Highlight Move with Arrow",OPT_HighlightMoveArrow,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,109,145,111,9
 END\r
 \r
-DLG_IcsOptions DIALOGEX 0, 0, 318, 271\r
+DLG_IcsOptions DIALOGEX 0, 0, 302, 265
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
 CAPTION "ICS Options"\r
-FONT 8, "MS Sans Serif", 0, 0, 0x1
+FONT 8, "MS Sans Serif"
 BEGIN\r
-    DEFPUSHBUTTON   "OK",IDOK,88,250,60,15\r
-    PUSHBUTTON      "Cancel",IDCANCEL,168,250,60,15\r
+    DEFPUSHBUTTON   "OK",IDOK,194,246,50,15
+    PUSHBUTTON      "Cancel",IDCANCEL,248,246,50,15
     CONTROL         "&Auto Comment",OPT_AutoComment,"Button",BS_AUTOCHECKBOX | \r
-                    WS_TABSTOP,20,12,63,8\r
+                    WS_TABSTOP,10,12,63,8
     CONTROL         "Auto &Observe",OPT_AutoObserve,"Button",BS_AUTOCHECKBOX | \r
-                    WS_TABSTOP,20,27,63,8\r
+                    WS_TABSTOP,10,25,63,8
     CONTROL         "&Get Move List",OPT_GetMoveList,"Button",\r
-                    BS_AUTOCHECKBOX | WS_TABSTOP,20,42,63,8\r
+                    BS_AUTOCHECKBOX | WS_TABSTOP,10,38,63,8
     CONTROL         "&Local Line Editing",OPT_LocalLineEditing,"Button",\r
-                    BS_AUTOCHECKBOX | WS_TABSTOP,20,57,84,8\r
+                    BS_AUTOCHECKBOX | WS_TABSTOP,10,51,84,8
     CONTROL         "&Quiet Play",OPT_QuietPlay,"Button",BS_AUTOCHECKBOX | \r
-                    WS_TABSTOP,20,72,63,8\r
+                    WS_TABSTOP,10,64,63,8
     CONTROL         "&Premove",OPT_Premove,"Button",BS_AUTOCHECKBOX | \r
-                    WS_TABSTOP,171,12,46,10\r
+                    WS_TABSTOP,160,12,46,10
     CONTROL         "&White first move",OPT_PremoveWhite,"Button",\r
-                    BS_AUTOCHECKBOX | WS_TABSTOP,171,27,67,10\r
-    EDITTEXT        OPT_PremoveWhiteText,252,23,25,14,ES_AUTOHSCROLL\r
+                    BS_AUTOCHECKBOX | WS_TABSTOP,160,26,67,10
+    EDITTEXT        OPT_PremoveWhiteText,236,22,25,14,ES_AUTOHSCROLL
     CONTROL         "&Black first move",OPT_PremoveBlack,"Button",\r
-                    BS_AUTOCHECKBOX | WS_TABSTOP,171,42,66,10\r
-    EDITTEXT        OPT_PremoveBlackText,252,39,25,14,ES_AUTOHSCROLL\r
+                    BS_AUTOCHECKBOX | WS_TABSTOP,160,40,66,10
+    EDITTEXT        OPT_PremoveBlackText,236,38,25,14,ES_AUTOHSCROLL
     CONTROL         "&Sound alarm at",OPT_IcsAlarm,"Button",BS_AUTOCHECKBOX | \r
-                    WS_TABSTOP,171,72,63,10\r
-    EDITTEXT        OPT_IcsAlarmTime,243,70,20,13,ES_AUTOHSCROLL\r
-    LTEXT           "seconds",IDC_STATIC,270,72,28,8\r
-    PUSHBUTTON      "Choose...",OPT_ChooseShoutColor,104,101,45,15\r
-    PUSHBUTTON      "Choose...",OPT_ChooseSShoutColor,104,121,45,15\r
-    PUSHBUTTON      "Choose...",OPT_ChooseChannel1Color,104,141,45,15\r
-    PUSHBUTTON      "Choose...",OPT_ChooseChannelColor,104,161,45,15\r
-    PUSHBUTTON      "Choose...",OPT_ChooseKibitzColor,104,181,45,15\r
-    PUSHBUTTON      "Choose...",OPT_ChooseTellColor,252,101,45,15\r
-    PUSHBUTTON      "Choose...",OPT_ChooseChallengeColor,252,121,45,15\r
-    PUSHBUTTON      "Choose...",OPT_ChooseRequestColor,252,141,45,15\r
-    PUSHBUTTON      "Choose...",OPT_ChooseSeekColor,252,161,45,15\r
-    PUSHBUTTON      "Choose...",OPT_ChooseNormalColor,252,181,45,15\r
+                    WS_TABSTOP,160,72,63,10
+    EDITTEXT        OPT_IcsAlarmTime,236,68,26,14,ES_AUTOHSCROLL
+    LTEXT           "seconds",IDC_STATIC,264,72,28,8
+    PUSHBUTTON      "Choose...",OPT_ChooseShoutColor,98,101,45,15
+    PUSHBUTTON      "Choose...",OPT_ChooseSShoutColor,98,121,45,15
+    PUSHBUTTON      "Choose...",OPT_ChooseChannel1Color,98,141,45,15
+    PUSHBUTTON      "Choose...",OPT_ChooseChannelColor,98,161,45,15
+    PUSHBUTTON      "Choose...",OPT_ChooseKibitzColor,98,181,45,15
+    PUSHBUTTON      "Choose...",OPT_ChooseTellColor,246,101,45,15
+    PUSHBUTTON      "Choose...",OPT_ChooseChallengeColor,246,121,45,15
+    PUSHBUTTON      "Choose...",OPT_ChooseRequestColor,246,141,45,15
+    PUSHBUTTON      "Choose...",OPT_ChooseSeekColor,246,161,45,15
+    PUSHBUTTON      "Choose...",OPT_ChooseNormalColor,246,181,45,15
     PUSHBUTTON      "&Choose Background Color...",OPT_ChooseBackgroundColor,\r
-                    17,204,132,16\r
-    PUSHBUTTON      "&Default ICS Colors",OPT_DefaultColors,165,204,132,16\r
+                    11,204,132,16
+    PUSHBUTTON      "&Default ICS Colors",OPT_DefaultColors,159,204,132,16
     CONTROL         "Do &not colorize messages",OPT_DontColorize,"Button",\r
-                    BS_AUTOCHECKBOX | WS_TABSTOP,110,225,97,10\r
+                    BS_AUTOCHECKBOX | WS_TABSTOP,104,225,97,10
     CONTROL         "",OPT_SampleShout,"RICHEDIT",ES_CENTER | ES_MULTILINE | \r
-                    ES_READONLY | WS_DISABLED | WS_GROUP,17,101,75,15,\r
+                    ES_READONLY | WS_DISABLED | WS_GROUP,11,101,75,15,
                     WS_EX_CLIENTEDGE\r
     CONTROL         "",OPT_SampleSShout,"RICHEDIT",ES_CENTER | ES_MULTILINE | \r
-                    ES_READONLY | WS_DISABLED | WS_GROUP,17,121,75,15,\r
+                    ES_READONLY | WS_DISABLED | WS_GROUP,11,121,75,15,
                     WS_EX_CLIENTEDGE\r
     CONTROL         "",OPT_SampleChannel1,"RICHEDIT",ES_CENTER | \r
-                    ES_MULTILINE | ES_READONLY | WS_DISABLED | WS_GROUP,17,\r
+                    ES_MULTILINE | ES_READONLY | WS_DISABLED | WS_GROUP,11,
                     141,75,15,WS_EX_CLIENTEDGE\r
     CONTROL         "",OPT_SampleChannel,"RICHEDIT",ES_CENTER | ES_MULTILINE | \r
-                    ES_READONLY | WS_DISABLED | WS_GROUP,17,161,75,15,\r
+                    ES_READONLY | WS_DISABLED | WS_GROUP,11,161,75,15,
                     WS_EX_CLIENTEDGE\r
     CONTROL         "",OPT_SampleKibitz,"RICHEDIT",ES_CENTER | ES_MULTILINE | \r
-                    ES_READONLY | WS_GROUP,17,181,75,15,WS_EX_CLIENTEDGE\r
+                    ES_READONLY | WS_GROUP,11,181,75,15,WS_EX_CLIENTEDGE
     CONTROL         "",OPT_SampleTell,"RICHEDIT",ES_CENTER | ES_MULTILINE | \r
-                    ES_READONLY | WS_DISABLED | WS_GROUP,165,101,75,15,\r
+                    ES_READONLY | WS_DISABLED | WS_GROUP,159,101,75,15,
                     WS_EX_CLIENTEDGE\r
     CONTROL         "",OPT_SampleChallenge,"RICHEDIT",ES_CENTER | \r
-                    ES_MULTILINE | ES_READONLY | WS_DISABLED | WS_GROUP,165,\r
+                    ES_MULTILINE | ES_READONLY | WS_DISABLED | WS_GROUP,159,
                     121,75,15,WS_EX_CLIENTEDGE\r
     CONTROL         "",OPT_SampleRequest,"RICHEDIT",ES_CENTER | ES_MULTILINE | \r
-                    ES_READONLY | WS_DISABLED | WS_GROUP,165,141,75,15,\r
+                    ES_READONLY | WS_DISABLED | WS_GROUP,159,141,75,15,
                     WS_EX_CLIENTEDGE\r
     CONTROL         "",OPT_SampleSeek,"RICHEDIT",ES_CENTER | ES_MULTILINE | \r
-                    ES_READONLY | WS_DISABLED | WS_GROUP,165,161,75,15,\r
+                    ES_READONLY | WS_DISABLED | WS_GROUP,159,161,75,15,
                     WS_EX_CLIENTEDGE\r
     CONTROL         "",OPT_SampleNormal,"RICHEDIT",ES_CENTER | ES_MULTILINE | \r
-                    ES_READONLY | WS_DISABLED | WS_GROUP,165,181,75,15,\r
+                    ES_READONLY | WS_DISABLED | WS_GROUP,159,181,75,15,
                     WS_EX_CLIENTEDGE\r
-    GROUPBOX        "Interaction Colors",IDC_STATIC,10,90,295,150\r
-    GROUPBOX        "Premove",IDC_STATIC,159,0,146,59\r
-    GROUPBOX        "",IDC_STATIC,10,0,146,89\r
-    GROUPBOX        "Alarm",IDC_STATIC,159,59,146,30\r
+    GROUPBOX        "Interaction Colors",IDC_STATIC,4,90,294,150
+    GROUPBOX        "Premove",IDC_STATIC,154,0,144,56
+    GROUPBOX        "",IDC_STATIC,4,0,146,88
+    GROUPBOX        "Alarm",IDC_STATIC,154,58,144,30
 END\r
 \r
-DLG_BoardOptions DIALOG DISCARDABLE  0, 0, 262, 250\r
+DLG_BoardOptions DIALOG DISCARDABLE  0, 0, 194, 250
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU\r
 CAPTION "Board Options"\r
 FONT 8, "MS Sans Serif"\r
 BEGIN\r
-    DEFPUSHBUTTON   "OK",IDOK,205,10,50,14\r
-    PUSHBUTTON      "Cancel",IDCANCEL,205,35,50,14\r
+    DEFPUSHBUTTON   "OK",IDOK,84,232,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,140,232,50,14
     CONTROL         "&Tiny",OPT_SizeTiny,"Button",BS_AUTORADIOBUTTON | \r
-                    WS_GROUP | WS_TABSTOP,15,15,50,10\r
-    CONTROL         "T&eeny",OPT_SizeTeeny,"Button",BS_AUTORADIOBUTTON,15,25,\r
-                    50,10\r
-    CONTROL         "&Dinky",OPT_SizeDinky,"Button",BS_AUTORADIOBUTTON,15,35,\r
+                    WS_GROUP | WS_TABSTOP,9,14,50,10
+    CONTROL         "T&eeny",OPT_SizeTeeny,"Button",BS_AUTORADIOBUTTON,9,25,
                     50,10\r
-    CONTROL         "&Petite",OPT_SizePetite,"Button",BS_AUTORADIOBUTTON,15,\r
-                    45,50,10\r
-    CONTROL         "Sl&im",OPT_SizeSlim,"Button",BS_AUTORADIOBUTTON,15,55,\r
+    CONTROL         "&Dinky",OPT_SizeDinky,"Button",BS_AUTORADIOBUTTON,9,34,
                     50,10\r
-    CONTROL         "&Small",OPT_SizeSmall,"Button",BS_AUTORADIOBUTTON,15,65,\r
+    CONTROL         "&Petite",OPT_SizePetite,"Button",BS_AUTORADIOBUTTON,9,
+                    44,50,10
+    CONTROL         "Sl&im",OPT_SizeSlim,"Button",BS_AUTORADIOBUTTON,9,54,50,
+                    10
+    CONTROL         "&Small",OPT_SizeSmall,"Button",BS_AUTORADIOBUTTON,9,65,
                     50,10\r
     CONTROL         "Medi&ocre",OPT_SizeMediocre,"Button",BS_AUTORADIOBUTTON,\r
-                    76,15,50,10\r
+                    70,14,50,10
     CONTROL         "&Middling",OPT_SizeMiddling,"Button",BS_AUTORADIOBUTTON,\r
-                    76,25,50,10\r
+                    70,25,50,10
     CONTROL         "&Average",OPT_SizeAverage,"Button",BS_AUTORADIOBUTTON,\r
-                    76,35,50,10\r
+                    70,34,50,10
     CONTROL         "Mode&rate",OPT_SizeModerate,"Button",BS_AUTORADIOBUTTON,\r
-                    76,45,50,10\r
-    CONTROL         "Medi&um",OPT_SizeMedium,"Button",BS_AUTORADIOBUTTON,76,\r
-                    55,50,10\r
-    CONTROL         "Bul&ky",OPT_SizeBulky,"Button",BS_AUTORADIOBUTTON,76,65,\r
+                    70,44,50,10
+    CONTROL         "Medi&um",OPT_SizeMedium,"Button",BS_AUTORADIOBUTTON,70,
+                    54,50,10
+    CONTROL         "Bul&ky",OPT_SizeBulky,"Button",BS_AUTORADIOBUTTON,70,65,
                     50,10\r
-    CONTROL         "&Large",OPT_SizeLarge,"Button",BS_AUTORADIOBUTTON,140,\r
-                    15,50,10\r
-    CONTROL         "&Big",OPT_SizeBig,"Button",BS_AUTORADIOBUTTON,140,25,50,\r
+    CONTROL         "&Large",OPT_SizeLarge,"Button",BS_AUTORADIOBUTTON,134,
+                    14,50,10
+    CONTROL         "&Big",OPT_SizeBig,"Button",BS_AUTORADIOBUTTON,134,25,50,
                     10\r
-    CONTROL         "&Huge",OPT_SizeHuge,"Button",BS_AUTORADIOBUTTON,140,35,\r
+    CONTROL         "&Huge",OPT_SizeHuge,"Button",BS_AUTORADIOBUTTON,134,34,
                     50,10\r
-    CONTROL         "&Giant",OPT_SizeGiant,"Button",BS_AUTORADIOBUTTON,140,\r
-                    45,50,10\r
+    CONTROL         "&Giant",OPT_SizeGiant,"Button",BS_AUTORADIOBUTTON,134,
+                    44,50,10
     CONTROL         "&Colossal",OPT_SizeColossal,"Button",BS_AUTORADIOBUTTON,\r
-                    140,55,50,10\r
+                    134,54,50,10
     CONTROL         "Tita&nic",OPT_SizeTitanic,"Button",BS_AUTORADIOBUTTON,\r
-                    140,65,50,10\r
-    PUSHBUTTON      "Choose...",OPT_ChooseLightSquareColor,144,100,40,15\r
-    PUSHBUTTON      "Choose...",OPT_ChooseDarkSquareColor,144,120,40,15\r
-    PUSHBUTTON      "Choose...",OPT_ChooseWhitePieceColor,144,140,40,15\r
-    PUSHBUTTON      "Choose...",OPT_ChooseBlackPieceColor,144,160,40,15\r
-    PUSHBUTTON      "Choose...",OPT_ChooseHighlightSquareColor,144,180,40,15\r
-    PUSHBUTTON      "Choose...",OPT_ChoosePremoveHighlightColor,144,200,40,\r
-                    15\r
-    PUSHBUTTON      "Defaults",OPT_DefaultBoardColors,118,225,65,15\r
-    EDITTEXT        OPT_DarkSquareColor,104,120,25,15,ES_READONLY | \r
+                    134,65,50,10
+    PUSHBUTTON      "...",OPT_ChooseLightSquareColor,110,94,20,15
+    PUSHBUTTON      "...",OPT_ChooseDarkSquareColor,110,112,20,15
+    PUSHBUTTON      "...",OPT_ChooseWhitePieceColor,110,130,20,15
+    PUSHBUTTON      "...",OPT_ChooseBlackPieceColor,110,148,20,15
+    PUSHBUTTON      "...",OPT_ChooseHighlightSquareColor,110,166,20,15
+    PUSHBUTTON      "...",OPT_ChoosePremoveHighlightColor,110,184,20,15
+    PUSHBUTTON      "Defaults",OPT_DefaultBoardColors,80,206,50,15
+    EDITTEXT        OPT_DarkSquareColor,80,112,25,15,ES_READONLY |
                     WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP\r
-    EDITTEXT        OPT_LightSquareColor,104,100,25,15,ES_READONLY | \r
+    EDITTEXT        OPT_LightSquareColor,80,94,25,15,ES_READONLY |
                     WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP\r
-    EDITTEXT        OPT_WhitePieceColor,104,140,25,15,ES_READONLY | \r
+    EDITTEXT        OPT_WhitePieceColor,80,130,25,15,ES_READONLY |
                     WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP\r
-    EDITTEXT        OPT_BlackPieceColor,104,160,25,15,ES_READONLY | \r
+    EDITTEXT        OPT_BlackPieceColor,80,148,25,15,ES_READONLY |
                     WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP\r
-    EDITTEXT        OPT_HighlightSquareColor,104,180,25,15,ES_READONLY | \r
+    EDITTEXT        OPT_HighlightSquareColor,80,166,25,15,ES_READONLY |
                     WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP\r
-    GROUPBOX        "Colors",IDC_STATIC,10,90,185,155\r
-    EDITTEXT        OPT_PremoveHighlightColor,104,200,25,15,ES_READONLY | \r
+    GROUPBOX        "Colors",IDC_STATIC,4,84,185,142
+    EDITTEXT        OPT_PremoveHighlightColor,80,184,25,15,ES_READONLY |
                     WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP\r
-    LTEXT           "Light Squares",IDC_STATIC,25,105,60,10\r
-    LTEXT           "Dark Squares",IDC_STATIC,25,124,60,10\r
-    LTEXT           "White Pieces",IDC_STATIC,25,145,60,10\r
-    LTEXT           "Black Pieces",IDC_STATIC,25,165,60,10\r
-    LTEXT           "Square Highlights",IDC_STATIC,25,185,60,10\r
-    LTEXT           "Premove Highlights",IDC_STATIC,25,205,70,10\r
-    GROUPBOX        "Size",IDC_STATIC,10,5,185,75\r
+    LTEXT           "Light Squares",IDC_STATIC,10,98,60,10
+    LTEXT           "Dark Squares",IDC_STATIC,10,116,60,10
+    LTEXT           "White Pieces",IDC_STATIC,10,134,60,10
+    LTEXT           "Black Pieces",IDC_STATIC,10,152,60,10
+    LTEXT           "Square Highlights",IDC_STATIC,10,170,60,10
+    LTEXT           "Premove Highlights",IDC_STATIC,10,188,70,10
+    GROUPBOX        "Size",IDC_STATIC,4,4,185,75
     CONTROL         "Monochrome",OPT_Monochrome,"Button",BS_AUTOCHECKBOX | \r
-                    WS_TABSTOP,25,225,75,10\r
-    EDITTEXT        OPT_SampleLightSquare,205,110,39,36,ES_READONLY | \r
+                    WS_TABSTOP,10,210,64,10
+    EDITTEXT        OPT_SampleLightSquare,144,96,39,36,ES_READONLY |
                     WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP\r
-    EDITTEXT        OPT_SampleDarkSquare,205,165,39,36,ES_READONLY | \r
+    EDITTEXT        OPT_SampleDarkSquare,144,138,39,36,ES_READONLY |
                     WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP\r
 END\r
 \r
@@ -585,15 +596,15 @@ STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "Fonts"\r
 FONT 8, "MS Sans Serif"\r
 BEGIN\r
-    DEFPUSHBUTTON   "OK",IDOK,155,210,50,14\r
-    PUSHBUTTON      "Cancel",IDCANCEL,215,210,50,14\r
+    DEFPUSHBUTTON   "OK",IDOK,168,212,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,224,212,50,14
     PUSHBUTTON      "Choose...",OPT_ChooseClockFont,221,17,45,15\r
     PUSHBUTTON      "Choose...",OPT_ChooseMessageFont,221,47,45,15\r
     PUSHBUTTON      "Choose...",OPT_ChooseCoordFont,221,77,45,15\r
     PUSHBUTTON      "Choose...",OPT_ChooseTagFont,221,120,45,15\r
     PUSHBUTTON      "Choose...",OPT_ChooseCommentsFont,221,150,45,15\r
     PUSHBUTTON      "Choose...",OPT_ChooseConsoleFont,221,180,45,15\r
-    PUSHBUTTON      "&Revert to Defaults",OPT_DefaultFonts,15,210,80,15\r
+    PUSHBUTTON      "&Revert to Defaults",OPT_DefaultFonts,6,210,80,15
     CONTROL         "",OPT_SampleCoordFont,"RICHEDIT",ES_READONLY | \r
                     WS_DISABLED | WS_BORDER,70,72,140,20\r
     CONTROL         "",OPT_SampleTagFont,"RICHEDIT",ES_READONLY | \r
@@ -625,8 +636,89 @@ BEGIN
     LTEXT           "&Start Position Number:",IDC_NFG_Label,4,7,71,8
     EDITTEXT        IDC_NFG_Edit,76,4,42,14,ES_AUTOHSCROLL
     PUSHBUTTON      "Random",IDC_NFG_Random,122,4,50,14
-    DEFPUSHBUTTON   "OK",IDOK,34,28,50,14
-    PUSHBUTTON      "Cancel",IDCANCEL,92,28,50,14
+    DEFPUSHBUTTON   "OK",IDOK,64,28,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,122,28,50,14
+END
+
+DLG_GameListOptions DIALOG DISCARDABLE  0, 0, 206, 140
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Game List Options"
+FONT 8, "MS Sans Serif"
+BEGIN
+    LTEXT           "PGN &Tags:",IDC_GLT,2,2,36,8
+    LISTBOX         IDC_GameListTags,2,12,158,102,LBS_USETABSTOPS |
+                    LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+    PUSHBUTTON      "&Up",IDC_GLT_Up,164,12,38,14
+    PUSHBUTTON      "&Down",IDC_GLT_Down,164,30,38,14
+    PUSHBUTTON      "Default",IDC_GLT_Restore,164,82,38,14
+    PUSHBUTTON      "Factory",IDC_GLT_Default,164,100,38,14
+    DEFPUSHBUTTON   "OK",IDOK,96,122,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,152,122,50,14
+    LTEXT           "Restore to:",IDC_GLT_RestoreTo,164,70,36,8
+END
+
+DLG_MoveHistory DIALOGEX 0, 0, 225, 130
+STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
+EXSTYLE WS_EX_TOOLWINDOW
+CAPTION "Move History"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+    CONTROL         "",IDC_MoveHistory,"RICHEDIT",ES_MULTILINE | ES_READONLY |
+                    WS_BORDER | WS_VSCROLL | WS_TABSTOP,2,2,222,128
+END
+
+DLG_EvalGraph DIALOGEX 0, 0, 215, 75
+STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
+EXSTYLE WS_EX_TOOLWINDOW
+CAPTION "Evaluation Graph"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+END
+
+DLG_EngineOutput DIALOGEX 0, 0, 266, 167
+STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
+EXSTYLE WS_EX_TOOLWINDOW
+CAPTION "Engine output"
+FONT 8, "MS Sans Serif"
+BEGIN
+    LTEXT           "Engine #1",IDC_EngineLabel1,2,2,152,8
+    RTEXT           "",IDC_Engine1_NPS,194,2,69,8
+    CONTROL         "",IDC_EngineMemo1,"RICHEDIT",ES_MULTILINE |
+                    ES_AUTOVSCROLL | ES_AUTOHSCROLL | WS_BORDER | WS_VSCROLL |
+                    WS_HSCROLL | WS_TABSTOP,2,10,262,72
+    LTEXT           "Engine #2",IDC_EngineLabel2,2,84,152,8
+    RTEXT           "",IDC_Engine2_NPS,196,84,67,8
+    CONTROL         "",IDC_EngineMemo2,"RICHEDIT",ES_MULTILINE |
+                    ES_AUTOVSCROLL | ES_AUTOHSCROLL | WS_BORDER | WS_VSCROLL |
+                    WS_HSCROLL | WS_TABSTOP,2,92,262,74
+END
+
+DLG_EnginePlayOptions DIALOG DISCARDABLE  0, 0, 208, 129
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Engine Options"
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "Ponder Next Move",IDC_EpPonder,"Button",BS_AUTOCHECKBOX |
+                    WS_TABSTOP,4,4,75,10
+    CONTROL         "Enable and Show Thinking (recommended)",
+                    IDC_EpShowThinking,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    4,16,152,10
+    CONTROL         "Hide Thinking when Playing against Human",
+                    IDC_EpHideThinkingHuman,"Button",BS_AUTOCHECKBOX |
+                    WS_TABSTOP,4,28,153,10
+    CONTROL         "Periodic Updates (for Analysis Mode)",
+                    IDC_EpPeriodicUpdates,"Button",BS_AUTOCHECKBOX |
+                    WS_TABSTOP,4,40,131,10
+    GROUPBOX        "Engine-engine matches",IDC_STATIC,4,56,200,50
+    LTEXT           "Adjudicate draw after:",IDC_STATIC,10,72,70,8
+    EDITTEXT        IDC_EpDrawMoveCount,116,68,40,14,ES_AUTOHSCROLL
+    LTEXT           "moves",IDC_STATIC,158,72,22,8
+    LTEXT           "Win/loss adjudication threshold:",IDC_STATIC,10,90,102,
+                    8
+    EDITTEXT        IDC_EpAdjudicationThreshold,116,86,40,14,ES_AUTOHSCROLL
+    LTEXT           "centipawns",IDC_STATIC,158,90,37,8
+    DEFPUSHBUTTON   "OK",IDOK,98,112,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,154,112,50,14
 END
 
 \r
@@ -643,26 +735,31 @@ BEGIN
         RIGHTMARGIN, 258
     END
 
+    DLG_Colorize, DIALOG
+    BEGIN
+        BOTTOMMARGIN, 32
+    END
+
     DLG_GeneralOptions, DIALOG\r
     BEGIN\r
         LEFTMARGIN, 7\r
         RIGHTMARGIN, 264\r
         TOPMARGIN, 7\r
-        BOTTOMMARGIN, 143
+        BOTTOMMARGIN, 155
     END\r
 \r
     DLG_IcsOptions, DIALOG\r
     BEGIN\r
         LEFTMARGIN, 7\r
-        RIGHTMARGIN, 311\r
+        RIGHTMARGIN, 295
         TOPMARGIN, 7\r
-        BOTTOMMARGIN, 264\r
+        BOTTOMMARGIN, 258
     END\r
 \r
     DLG_BoardOptions, DIALOG\r
     BEGIN\r
         LEFTMARGIN, 7\r
-        RIGHTMARGIN, 255\r
+        RIGHTMARGIN, 187
         TOPMARGIN, 7\r
         BOTTOMMARGIN, 243\r
     END\r
@@ -682,6 +779,46 @@ BEGIN
         TOPMARGIN, 7
         BOTTOMMARGIN, 40
     END
+
+    DLG_GameListOptions, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 199
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 133
+    END
+
+    DLG_MoveHistory, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 218
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 123
+    END
+
+    DLG_EvalGraph, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 208
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 68
+    END
+
+    DLG_EngineOutput, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 259
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 160
+    END
+
+    DLG_EnginePlayOptions, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 201
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 122
+    END
 END\r
 #endif    // APSTUDIO_INVOKED\r
 \r
@@ -718,6 +855,8 @@ BEGIN
         MENUITEM "&Copy Game To Clipboard\tAlt+C", IDM_CopyGame\r
         MENUITEM "Paste Game &From Clipboard\tAlt+V", IDM_PasteGame\r
         MENUITEM SEPARATOR\r
+        MENUITEM "Copy Game List to Clipboard", IDM_CopyGameListToClipboard
+        MENUITEM SEPARATOR
         MENUITEM "L&oad Position...",           IDM_LoadPosition\r
         MENUITEM "Load N&ext Position\tAlt+Shift+PgDn", IDM_LoadNextPosition\r
         MENUITEM "Load Pre&vious Position\tAlt+Shift+PgUp", \r
@@ -744,7 +883,11 @@ BEGIN
         MENUITEM "&Edit Position",              IDM_EditPosition\r
         MENUITEM "Trai&ning",                   IDM_Training, GRAYED\r
         MENUITEM SEPARATOR\r
-        MENUITEM "Show Game &List...",          IDM_ShowGameList\r
+        MENUITEM "Show Engine Output",          IDM_ShowEngineOutput
+        MENUITEM "Show Evaluation Graph",       IDM_ShowEvalGraph
+        MENUITEM "Show Game &List",             IDM_ShowGameList
+        MENUITEM "Show Move History",           IDM_ShowMoveHistory
+        MENUITEM SEPARATOR
         MENUITEM "Edit &Tags...",               IDM_EditTags\r
         MENUITEM "Edit &Comment...",            IDM_EditComment\r
         MENUITEM "&Pause\tPause",               IDM_Pause\r
@@ -788,9 +931,11 @@ BEGIN
         MENUITEM SEPARATOR\r
         MENUITEM "&General...",                 IDM_GeneralOptions\r
         MENUITEM "&Board...",                   IDM_BoardOptions\r
+        MENUITEM "Engines...",                  IDM_EnginePlayOptions
         MENUITEM "&ICS...",                     IDM_IcsOptions, GRAYED\r
         MENUITEM "&Fonts...",                   IDM_Fonts\r
         MENUITEM "Soun&ds...",                  IDM_Sounds\r
+        MENUITEM "Game List...",                IDM_GameListOptions
         MENUITEM SEPARATOR\r
         MENUITEM "Comm&unications...",          IDM_CommPort\r
         MENUITEM "&Load Game...",               IDM_LoadOptions\r
@@ -926,11 +1071,13 @@ BEGIN
     "2",            IDM_DirectCommand2,     VIRTKEY, ALT, NOINVERT\r
     "C",            IDM_CopyGame,           VIRTKEY, ALT, NOINVERT\r
     "C",            IDM_CopyPosition,       VIRTKEY, SHIFT, ALT, NOINVERT
+    "M",            IDM_TypeInMove,         VIRTKEY, ALT, NOINVERT
     "N",            IDM_NewGame,            VIRTKEY, CONTROL, NOINVERT
     "O",            IDM_LoadGame,           VIRTKEY, CONTROL, NOINVERT
     "S",            IDM_SaveGame,           VIRTKEY, CONTROL, NOINVERT
-    "V",            IDM_Paste,              VIRTKEY, CONTROL, NOINVERT
     "V",            IDM_PasteGame,          VIRTKEY, ALT, NOINVERT\r
+    "V",            IDM_PasteAny,           VIRTKEY, SHIFT, CONTROL,
+                                                    NOINVERT
     "V",            IDM_PastePosition,      VIRTKEY, SHIFT, ALT, NOINVERT
     VK_DELETE,      IDM_RetractMove,        VIRTKEY, ALT, NOINVERT\r
     VK_DOWN,        IDM_ToEnd,              VIRTKEY, ALT, NOINVERT\r
@@ -978,6 +1125,12 @@ BEGIN
     VK_UP,          IDM_ToStart,            VIRTKEY, NOINVERT\r
 END\r
 \r
+NO_ICS ACCELERATORS DISCARDABLE
+BEGIN
+    "A",            IDM_AnalysisMode,       VIRTKEY, CONTROL, NOINVERT
+    "V",            IDM_PasteAny,           VIRTKEY, CONTROL, NOINVERT
+END
+
 \r
 /////////////////////////////////////////////////////////////////////////////\r
 //\r
diff --git a/winboard/wlayout.c b/winboard/wlayout.c
new file mode 100644 (file)
index 0000000..169c7ea
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Layout management
+ *
+ * Author: Alessandro Scotti
+ *
+ * ------------------------------------------------------------------------
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ------------------------------------------------------------------------
+ */
+#include "config.h"
+
+#include <windows.h> /* required for all Windows applications */
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <commdlg.h>
+#include <dlgs.h>
+
+#include "common.h"
+#include "winboard.h"
+
+VOID RestoreWindowPlacement( HWND hWnd, WindowPlacement * wp )
+{
+    if( wp->x != CW_USEDEFAULT ||
+        wp->y != CW_USEDEFAULT ||
+        wp->width != CW_USEDEFAULT ||
+        wp->height != CW_USEDEFAULT )
+    {
+       WINDOWPLACEMENT stWP;
+
+        ZeroMemory( &stWP, sizeof(stWP) );
+
+       EnsureOnScreen( &wp->x, &wp->y);
+
+       stWP.length = sizeof(stWP);
+       stWP.flags = 0;
+       stWP.showCmd = SW_SHOW;
+       stWP.ptMaxPosition.x = 0;
+        stWP.ptMaxPosition.y = 0;
+       stWP.rcNormalPosition.left = wp->x;
+       stWP.rcNormalPosition.right = wp->x + wp->width;
+       stWP.rcNormalPosition.top = wp->y;
+       stWP.rcNormalPosition.bottom = wp->y + wp->height;
+
+       SetWindowPlacement(hWnd, &stWP);
+    }
+}
+
+VOID InitWindowPlacement( WindowPlacement * wp )
+{
+    wp->visible = TRUE;
+    wp->x = CW_USEDEFAULT;
+    wp->y = CW_USEDEFAULT;
+    wp->width = CW_USEDEFAULT;
+    wp->height = CW_USEDEFAULT;
+}
+
+static BOOL IsAttachDistance( int a, int b )
+{
+    BOOL result = FALSE;
+
+    if( a == b ) {
+        result = TRUE;
+    }
+
+    return result;
+}
+
+static BOOL IsDefaultPlacement( WindowPlacement * wp )
+{
+    BOOL result = FALSE;
+
+    if( wp->x == CW_USEDEFAULT || wp->y == CW_USEDEFAULT || wp->width == CW_USEDEFAULT || wp->height == CW_USEDEFAULT ) {
+        result = TRUE;
+    }
+
+    return result;
+}
+
+static BOOL GetActualPlacement( HWND hWnd, WindowPlacement * wp )
+{
+    BOOL result = FALSE;
+
+    if( hWnd != NULL ) {
+        WINDOWPLACEMENT stWP;
+
+        ZeroMemory( &stWP, sizeof(stWP) );
+
+        stWP.length = sizeof(stWP);
+
+        GetWindowPlacement( hWnd, &stWP );
+
+        wp->x = stWP.rcNormalPosition.left;
+        wp->y = stWP.rcNormalPosition.top;
+        wp->width = stWP.rcNormalPosition.right - stWP.rcNormalPosition.left;
+        wp->height = stWP.rcNormalPosition.bottom - stWP.rcNormalPosition.top;
+
+        result = TRUE;
+    }
+
+    return result;
+}
+
+static BOOL IsAttachedByWindowPlacement( LPRECT lprcMain, WindowPlacement * wp )
+{
+    BOOL result = FALSE;
+
+    if( ! IsDefaultPlacement(wp) ) {
+        if( IsAttachDistance( lprcMain->right, wp->x ) ||
+            IsAttachDistance( lprcMain->bottom, wp->y ) ||
+            IsAttachDistance( lprcMain->left, (wp->x + wp->width) ) ||
+            IsAttachDistance( lprcMain->top, (wp->y + wp->height) ) )
+        {
+            result = TRUE;
+        }
+    }
+
+    return result;
+}
+
+VOID ReattachAfterMove( LPRECT lprcOldPos, int new_x, int new_y, HWND hWndChild, WindowPlacement * pwpChild )
+{
+    if( ! IsDefaultPlacement( pwpChild ) ) {
+        GetActualPlacement( hWndChild, pwpChild );
+
+        if( IsAttachedByWindowPlacement( lprcOldPos, pwpChild ) ) {
+            /* Get position delta */
+            int delta_x = pwpChild->x - lprcOldPos->left;
+            int delta_y = pwpChild->y - lprcOldPos->top;
+
+            /* Adjust placement */
+            pwpChild->x = new_x + delta_x;
+            pwpChild->y = new_y + delta_y;
+
+            /* Move window */
+            if( hWndChild != NULL ) {
+                SetWindowPos( hWndChild, HWND_TOP,
+                    pwpChild->x, pwpChild->y,
+                    0, 0,
+                    SWP_NOZORDER | SWP_NOSIZE );
+            }
+        }
+    }
+}
index d568cee..41df86c 100644 (file)
@@ -169,6 +169,8 @@ GeneralOptionsDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
     CHECK_BOX(OPT_TestLegality, appData.testLegality);\r
     CHECK_BOX(OPT_HideThinkFromHuman, appData.hideThinkingFromHuman);
     CHECK_BOX(OPT_SaveExtPGN, appData.saveExtendedInfoInPGN);
+    CHECK_BOX(OPT_ExtraInfoInMoveHistory, appData.showEvalInMoveHistory);
+    CHECK_BOX(OPT_HighlightMoveArrow, appData.highlightMoveWithArrow);
 \r
 #undef CHECK_BOX\r
 \r
@@ -212,6 +214,8 @@ GeneralOptionsDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
       appData.testLegality         = IS_CHECKED(OPT_TestLegality);\r
       appData.hideThinkingFromHuman= IS_CHECKED(OPT_HideThinkFromHuman);
       appData.saveExtendedInfoInPGN= IS_CHECKED(OPT_SaveExtPGN);
+      appData.showEvalInMoveHistory= IS_CHECKED(OPT_ExtraInfoInMoveHistory);
+      appData.highlightMoveWithArrow=IS_CHECKED(OPT_HighlightMoveArrow);
 \r
 #undef IS_CHECKED\r
 \r
@@ -2237,6 +2241,7 @@ SaveOptionsDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
     } else {\r
       CheckRadioButton(hDlg, OPT_PGN, OPT_Old, OPT_PGN);\r
     }\r
+    CheckDlgButton( hDlg, OPT_OutOfBookInfo, appData.saveOutOfBookInfo );
     SetSaveOptionEnables(hDlg);\r
     return TRUE;\r
 \r
@@ -2276,6 +2281,7 @@ SaveOptionsDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
        appData.saveGameFile = "";\r
       }\r
       appData.oldSaveStyle = IsDlgButtonChecked(hDlg, OPT_Old);\r
+      appData.saveOutOfBookInfo = IsDlgButtonChecked( hDlg, OPT_OutOfBookInfo );
       EndDialog(hDlg, TRUE);\r
       return TRUE;\r
 \r
@@ -2433,4 +2439,80 @@ TimeControlOptionsPopup(HWND hwnd)
   }\r
 }\r
 \r
-\r
+/*---------------------------------------------------------------------------*\
+ *
+ * Engine Options Dialog functions
+ *
+\*---------------------------------------------------------------------------*/
+#define CHECK_BOX(x,y) CheckDlgButton(hDlg, (x), (BOOL)(y))
+#define IS_CHECKED(x) (Boolean)IsDlgButtonChecked(hDlg, (x))
+
+#define INT_ABS( n )    ((n) >= 0 ? (n) : -(n))
+
+LRESULT CALLBACK EnginePlayOptionsDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  switch (message) {
+  case WM_INITDIALOG: /* message: initialize dialog box */
+
+    /* Center the dialog over the application window */
+    CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+
+    /* Initialize the dialog items */
+    CHECK_BOX(IDC_EpPeriodicUpdates, appData.periodicUpdates);
+    CHECK_BOX(IDC_EpPonder, appData.ponderNextMove);
+    CHECK_BOX(IDC_EpShowThinking, appData.showThinking);
+    CHECK_BOX(IDC_EpHideThinkingHuman, appData.hideThinkingFromHuman);
+
+    SetDlgItemInt( hDlg, IDC_EpDrawMoveCount, appData.adjudicateDrawMoves, TRUE );
+    SendDlgItemMessage( hDlg, IDC_EpDrawMoveCount, EM_SETSEL, 0, -1 );
+
+    SetDlgItemInt( hDlg, IDC_EpAdjudicationThreshold, INT_ABS(appData.adjudicateLossThreshold), TRUE );
+    SendDlgItemMessage( hDlg, IDC_EpAdjudicationThreshold, EM_SETSEL, 0, -1 );
+
+    return TRUE;
+
+  case WM_COMMAND: /* message: received a command */
+    switch (LOWORD(wParam)) {
+    case IDOK:
+      /* Read changed options from the dialog box */
+      PeriodicUpdatesEvent(          IS_CHECKED(IDC_EpPeriodicUpdates));
+      PonderNextMoveEvent(           IS_CHECKED(IDC_EpPonder));
+      ShowThinkingEvent(             IS_CHECKED(IDC_EpShowThinking));
+      appData.hideThinkingFromHuman= IS_CHECKED(IDC_EpHideThinkingHuman);
+
+      appData.adjudicateDrawMoves = GetDlgItemInt(hDlg, IDC_EpDrawMoveCount, NULL, FALSE );
+      appData.adjudicateLossThreshold = - (int) GetDlgItemInt(hDlg, IDC_EpAdjudicationThreshold, NULL, FALSE );
+
+      EndDialog(hDlg, TRUE);
+      return TRUE;
+
+    case IDCANCEL:
+      EndDialog(hDlg, FALSE);
+      return TRUE;
+
+    case IDC_EpDrawMoveCount:
+    case IDC_EpAdjudicationThreshold:
+        if( HIWORD(wParam) == EN_CHANGE ) {
+            int n1_ok;
+            int n2_ok;
+
+            GetDlgItemInt(hDlg, IDC_EpDrawMoveCount, &n1_ok, FALSE );
+            GetDlgItemInt(hDlg, IDC_EpAdjudicationThreshold, &n2_ok, FALSE );
+
+            EnableWindow( GetDlgItem(hDlg, IDOK), n1_ok && n2_ok ? TRUE : FALSE );
+        }
+        return TRUE;
+    }
+    break;
+  }
+  return FALSE;
+}
+
+VOID EnginePlayOptionsPopup(HWND hwnd)
+{
+  FARPROC lpProc;
+\r
+  lpProc = MakeProcInstance((FARPROC)EnginePlayOptionsDialog, hInst);
+  DialogBox(hInst, MAKEINTRESOURCE(DLG_EnginePlayOptions), hwnd, (DLGPROC) lpProc);
+  FreeProcInstance(lpProc);
+}
index a35d6a6..a26d8e4 100644 (file)
@@ -32,3 +32,4 @@ VOID CommPortOptionsPopup(HWND hwnd);
 VOID LoadOptionsPopup(HWND hwnd);\r
 VOID SaveOptionsPopup(HWND hwnd);\r
 VOID TimeControlOptionsPopup(HWND hwnd);\r
+VOID EnginePlayOptionsPopup(HWND hwnd);
diff --git a/winboard/wsnap.c b/winboard/wsnap.c
new file mode 100644 (file)
index 0000000..cba18c0
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * wsnap.c -- Smart "snapping" for window moving and sizing
+ *
+ * Author: Alessandro Scotti
+ *
+ * ------------------------------------------------------------------------
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ------------------------------------------------------------------------
+ */
+#include "wsnap.h"
+
+/* Imports from winboard.c */
+extern HINSTANCE hInst;
+
+extern HWND hwndMain;
+extern HWND moveHistoryDialog;
+extern HWND evalGraphDialog;
+extern HWND gameListDialog;
+
+static BOOL SnappingEnabled = TRUE;
+
+static void AddSnapPoint( int * grid, int * grid_len, int value )
+{
+    int len = *grid_len;
+
+    if( len < MAX_SNAP_POINTS ) {
+        int i;
+
+        for( i=0; i<len; i++ ) {
+            if( grid[i] == value ) {
+                return;
+            }
+        }
+
+        grid[ len++ ] = value;
+
+        *grid_len = len;
+    }
+}
+
+static void AddSnapRectangle( SnapData * sd, RECT * rc )
+{
+    AddSnapPoint( sd->x_grid, &sd->x_grid_len, rc->left );
+    AddSnapPoint( sd->x_grid, &sd->x_grid_len, rc->right );
+
+    AddSnapPoint( sd->y_grid, &sd->y_grid_len, rc->top );
+    AddSnapPoint( sd->y_grid, &sd->y_grid_len, rc->bottom );
+}
+
+static void AddSnapWindow( HWND hWndCaller, SnapData * sd, HWND hWndSnapWindow )
+{
+    if( hWndSnapWindow != NULL && hWndCaller != hWndSnapWindow && IsWindowVisible(hWndSnapWindow) ) {
+        RECT rc;
+
+        GetWindowRect( hWndSnapWindow, &rc );
+
+        AddSnapRectangle( sd, &rc );
+    }
+}
+
+static BOOL AdjustToSnapPoint( int * grid, int grid_len, int value, int * snap_size, int * delta )
+{
+    BOOL result = FALSE;
+    int i;
+
+    for( i=0; i<grid_len; i++ ) {
+        int distance = value - grid[i];
+
+        if( distance < 0 ) distance = -distance;
+
+        if( distance < *snap_size ) {
+            result = TRUE;
+            *snap_size = distance;
+            *delta = grid[i] - value;
+        }
+    }
+
+    return result;
+}
+
+LRESULT OnEnterSizeMove( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam )
+{
+    RECT rc;
+
+    snapData->x_grid_len = 0;
+    snapData->y_grid_len = 0;
+
+    /* Add desktop area */
+    if( SystemParametersInfo( SPI_GETWORKAREA, 0, &rc, 0 ) ) {
+        AddSnapRectangle( snapData, &rc );
+    }
+
+    if( hWnd != hwndMain ) {
+        /* Add other windows */
+        AddSnapWindow( hWnd, snapData, hwndMain );
+        AddSnapWindow( hWnd, snapData, moveHistoryDialog );
+        AddSnapWindow( hWnd, snapData, evalGraphDialog );
+        AddSnapWindow( hWnd, snapData, gameListDialog );
+    }
+
+    return 0;
+}
+
+LRESULT OnMoving( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam )
+{
+    LPRECT lprc = (LPRECT) lParam;
+    int delta_x = 0;
+    int delta_y = 0;
+    int snap_size_x = SNAP_DISTANCE;
+    int snap_size_y = SNAP_DISTANCE;
+
+    if( ! SnappingEnabled ) {
+        return FALSE;
+    }
+
+    AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->left, &snap_size_x, &delta_x );
+    AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->right, &snap_size_x, &delta_x );
+
+    AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->top, &snap_size_y, &delta_y );
+    AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->bottom, &snap_size_y, &delta_y );
+
+    OffsetRect( lprc, delta_x, delta_y );
+
+    return TRUE;
+}
+
+LRESULT OnSizing( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam )
+{
+    LPRECT lprc = (LPRECT) lParam;
+    int delta_x = 0;
+    int delta_y = 0;
+    int snap_size_x = SNAP_DISTANCE;
+    int snap_size_y = SNAP_DISTANCE;
+
+    if( ! SnappingEnabled ) {
+        return FALSE;
+    }
+
+    switch( wParam ) {
+    case WMSZ_BOTTOM:
+        AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->bottom, &snap_size_y, &delta_y );
+        lprc->bottom += delta_y;
+        break;
+    case WMSZ_BOTTOMLEFT:
+        AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->bottom, &snap_size_y, &delta_y );
+        lprc->bottom += delta_y;
+        AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->left, &snap_size_x, &delta_x );
+        lprc->left += delta_x;
+        break;
+    case WMSZ_BOTTOMRIGHT:
+        AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->bottom, &snap_size_y, &delta_y );
+        lprc->bottom += delta_y;
+        AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->right, &snap_size_x, &delta_x );
+        lprc->right += delta_x;
+        break;
+    case WMSZ_LEFT:
+        AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->left, &snap_size_x, &delta_x );
+        lprc->left += delta_x;
+        break;
+    case WMSZ_RIGHT:
+        AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->right, &snap_size_x, &delta_x );
+        lprc->right += delta_x;
+        break;
+    case WMSZ_TOP:
+        AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->top, &snap_size_y, &delta_y );
+        lprc->top += delta_y;
+        break;
+    case WMSZ_TOPLEFT:
+        AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->top, &snap_size_y, &delta_y );
+        lprc->top += delta_y;
+        AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->left, &snap_size_x, &delta_x );
+        lprc->left += delta_x;
+        break;
+    case WMSZ_TOPRIGHT:
+        AdjustToSnapPoint( snapData->y_grid, snapData->y_grid_len, lprc->top, &snap_size_y, &delta_y );
+        lprc->top += delta_y;
+        AdjustToSnapPoint( snapData->x_grid, snapData->x_grid_len, lprc->right, &snap_size_x, &delta_x );
+        lprc->right += delta_x;
+        break;
+    default:
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+LRESULT OnExitSizeMove( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam )
+{
+    return 0;
+}
diff --git a/winboard/wsnap.h b/winboard/wsnap.h
new file mode 100644 (file)
index 0000000..5fb226e
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * wsnap.h -- Smart "snapping" for window moving and sizing
+ *
+ * Author: Alessandro Scotti
+ *
+ * ------------------------------------------------------------------------
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ------------------------------------------------------------------------
+ */
+#ifndef WSNAP_H_
+#define WSNAP_H_
+
+#include <windows.h>
+
+#define MAX_SNAP_POINTS     12
+
+#define SNAP_DISTANCE       4
+
+typedef struct {
+    int x_grid[ MAX_SNAP_POINTS ];
+    int x_grid_len;
+    int y_grid[ MAX_SNAP_POINTS ];
+    int y_grid_len;
+} SnapData;
+
+LRESULT OnEnterSizeMove( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam );
+LRESULT OnMoving( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam );
+LRESULT OnSizing( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam );
+LRESULT OnExitSizeMove( SnapData * snapData, HWND hWnd, WPARAM wParam, LPARAM lParam );
+
+#endif // WSNAP_H_