Allow external piece bitmaps and board border (WB)
[xboard.git] / winboard / winboard.c
index 7638faf..e6dec36 100644 (file)
@@ -5,7 +5,7 @@
  * Massachusetts. \r
  *\r
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
- * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.\r
+ * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.\r
  *\r
  * Enhancements Copyright 2005 Alessandro Scotti\r
  *\r
 extern int whiteFlag, blackFlag;\r
 Boolean flipClock = FALSE;\r
 extern HANDLE chatHandle[];\r
-extern int ics_type;\r
+extern enum ICS_TYPE ics_type;\r
 \r
+int  MySearchPath P((char *installDir, char *name, char *fullname));\r
+int  MyGetFullPathName P((char *name, char *fullname));\r
 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
 VOID NewVariantPopup(HWND hwnd);\r
 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
@@ -162,7 +164,7 @@ BoardSize boardSize;
 Boolean chessProgram;\r
 //static int boardX, boardY;\r
 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
-int squareSize, lineGap, minorSize;\r
+int squareSize, lineGap, minorSize, border;\r
 static int winW, winH;\r
 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
 static int logoHeight = 0;\r
@@ -249,7 +251,7 @@ Boolean barbaric; // flag indicating if translation is needed
 #define ABOUTBOX -1  /* not sure why these are needed */\r
 #define ABOUTBOX2 -1\r
 \r
-int dialogItems[][41   ] = {\r
+int dialogItems[][42] = {\r
 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
@@ -302,7 +304,7 @@ int dialogItems[][41        ] = {
   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
-  IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont }, \r
+  IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
@@ -435,7 +437,7 @@ TranslateOneMenu(int i, HMENU subMenu)
             info.dwTypeData = buf;\r
             info.cch = sizeof(buf);\r
             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
-            if(i < 10) {
+            if(i < 10) {\r
                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
                 else menuText[i][j] = strdup(buf); // remember original on first change\r
             }\r
@@ -492,6 +494,30 @@ TranslateMenus(int addLanguage)
 \r
 #endif\r
 \r
+#define IDM_RecentEngines 3000\r
+\r
+void\r
+RecentEngineMenu (char *s)\r
+{\r
+    if(appData.icsActive) return;\r
+    if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
+       HMENU mainMenu = GetMenu(hwndMain);\r
+       HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
+       int i=IDM_RecentEngines;\r
+       recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
+       AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
+       while(*s) {\r
+         char *p = strchr(s, '\n');\r
+         if(p == NULL) return; // malformed!\r
+         *p = NULLCHAR;\r
+         AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
+         *p = '\n';\r
+         s = p+1;\r
+       }\r
+    }\r
+}\r
+\r
+\r
 typedef struct {\r
   char *name;\r
   int squareSize;\r
@@ -737,7 +763,8 @@ void ThawUI()
 #define JAWS_INIT\r
 #define JAWS_ARGS\r
 #define JAWS_ALT_INTERCEPT\r
-#define JAWS_KB_NAVIGATION\r
+#define JAWS_KBUP_NAVIGATION\r
+#define JAWS_KBDOWN_NAVIGATION\r
 #define JAWS_MENU_ITEMS\r
 #define JAWS_SILENCE\r
 #define JAWS_REPLAY\r
@@ -979,8 +1006,14 @@ LoadLogo(ChessProgramState *cps, int n, Boolean ics)
       }\r
   } else if(appData.autoLogo) {\r
       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
-       sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
-       cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
+       char *opponent = "";\r
+       if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
+       if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
+       sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
+       if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
+           sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
+           cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
+       }\r
       } else\r
       if(appData.directory[n] && appData.directory[n][0]) {\r
         SetCurrentDirectory(appData.directory[n]);\r
@@ -1094,6 +1127,7 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
   }\r
 \r
   InitDrawingSizes(boardSize, 0);\r
+  RecentEngineMenu(appData.recentEngineList);\r
   InitMenuChecks();\r
   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
 \r
@@ -1138,15 +1172,6 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
 #endif\r
     ShowWindow(hwndConsole, nCmdShow);\r
-    if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
-      char buf[MSG_SIZ], *p = buf, *q;\r
-       safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
-      do {\r
-       q = strchr(p, ';');\r
-       if(q) *q++ = 0;\r
-       if(*p) ChatPopUp(p);\r
-      } while(p=q);\r
-    }\r
     SetActiveWindow(hwndConsole);\r
   }\r
   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
@@ -1177,6 +1202,7 @@ InitMenuChecks()
 \r
 #define OPTCHAR "/"\r
 #define SEPCHAR "="\r
+#define TOPLEVEL 0\r
 \r
 #include "args.h"\r
 \r
@@ -2087,9 +2113,15 @@ void CreatePiecesFromFont()
 HBITMAP\r
 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
 {\r
-  char name[128];\r
+  char name[128], buf[MSG_SIZ];\r
 \r
     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
+  if(appData.pieceDirectory[0]) {\r
+    HBITMAP res;\r
+    snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
+    res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
+    if(res) return res;\r
+  }\r
   if (gameInfo.event &&\r
       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
       strcmp(name, "k80s") == 0) {\r
@@ -2216,12 +2248,28 @@ InitDrawingSizes(BoardSize boardSize, int flags)
   RECT crect, wrect, oldRect;\r
   int offby;\r
   LOGBRUSH logbrush;\r
+  VariantClass v = gameInfo.variant;\r
 \r
   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
 \r
   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
+  oldBoardSize = boardSize;\r
+\r
+  if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
+  { // correct board size to one where built-in pieces exist\r
+    if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
+       && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
+      || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
+      ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
+      ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
+      if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
+      if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
+                                   boardSize = SizeMiddling;\r
+    }\r
+  }\r
+  if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
 \r
   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
   oldRect.top = wpMain.y;\r
@@ -2233,6 +2281,7 @@ InitDrawingSizes(BoardSize boardSize, int flags)
   squareSize = sizeInfo[boardSize].squareSize;\r
   lineGap = sizeInfo[boardSize].lineGap;\r
   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
+  border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
 \r
   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
       lineGap = appData.overrideLineGap;\r
@@ -2257,8 +2306,8 @@ InitDrawingSizes(BoardSize boardSize, int flags)
     DrawMenuBar(hwndMain);\r
   }\r
 \r
-  boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
-  boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
+  boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
+  boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
 \r
   /* Get text area sizes */\r
   hdc = GetDC(hwndMain);\r
@@ -2330,7 +2379,6 @@ InitDrawingSizes(BoardSize boardSize, int flags)
 \r
   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
-  oldBoardSize = boardSize;\r
   oldTinyLayout = tinyLayout;\r
   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
@@ -2430,20 +2478,20 @@ InitDrawingSizes(BoardSize boardSize, int flags)
 \r
     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
-      gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
+      gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
-       boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
+       boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
-        BOARD_WIDTH * (squareSize + lineGap);\r
+        BOARD_WIDTH * (squareSize + lineGap) + border;\r
       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
     }\r
     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
-      gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
+      gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
-       lineGap / 2 + (i * (squareSize + lineGap));\r
+       lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
-        boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
+        boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
     }\r
   }\r
@@ -2696,11 +2744,11 @@ VOID
 SquareToPos(int row, int column, int * x, int * y)\r
 {\r
   if (flipView) {\r
-    *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
-    *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
+    *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
+    *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
   } else {\r
-    *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
-    *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
+    *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
+    *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
   }\r
 }\r
 \r
@@ -2727,15 +2775,23 @@ DrawCoordsOnDC(HDC hdc)
   y = boardRect.top + lineGap;\r
   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
 \r
+  if(border) {\r
+    SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
+    x += border - lineGap - 4; y += squareSize - 6;\r
+  } else\r
   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
   for (i = 0; i < BOARD_HEIGHT; i++) {\r
     str[0] = files[start + i];\r
-    ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
+    ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
     y += squareSize + lineGap;\r
   }\r
 \r
   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
 \r
+  if(border) {\r
+    SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
+    x += -border + 4; y += border - squareSize + 6;\r
+  } else\r
   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
     str[0] = ranks[start + i];\r
@@ -2772,14 +2828,14 @@ DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
   if (lineGap == 0) return;\r
   if (flipView) {\r
     x1 = boardRect.left +\r
-      lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
+      lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
     y1 = boardRect.top +\r
-      lineGap/2 + y * (squareSize + lineGap);\r
+      lineGap/2 + y * (squareSize + lineGap) + border;\r
   } else {\r
     x1 = boardRect.left +\r
-      lineGap/2 + x * (squareSize + lineGap);\r
+      lineGap/2 + x * (squareSize + lineGap) + border;\r
     y1 = boardRect.top +\r
-      lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
+      lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
   }\r
   hPen = pen ? premovePen : highlightPen;\r
   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
@@ -2857,7 +2913,9 @@ DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y,
     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
           sqcolor ? SRCCOPY : NOTSRCCOPY);\r
   } else {\r
+    HBRUSH xBrush = whitePieceBrush;\r
     tmpSize = squareSize;\r
+    if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
     if(minorSize &&\r
         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
@@ -2870,7 +2928,7 @@ DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y,
     if (color || appData.allWhite ) {\r
       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
       if( color )\r
-              oldBrush = SelectObject(hdc, whitePieceBrush);\r
+              oldBrush = SelectObject(hdc, xBrush);\r
       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
       if(appData.upsideDown && color==flipView)\r
         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
@@ -2882,6 +2940,18 @@ DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y,
         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
       else\r
         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
+    } else if(appData.pieceDirectory[0]) {\r
+      oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
+      oldBrush = SelectObject(hdc, xBrush);\r
+      if(appData.upsideDown && color==flipView)\r
+        StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
+      else\r
+        BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
+      SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
+      if(appData.upsideDown && color==flipView)\r
+        StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
+      else\r
+        BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
     } else {\r
       /* Use square color for details of black pieces */\r
       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
@@ -3269,6 +3339,38 @@ BOOL DrawPositionNeedsFullRepaint()
     return result;\r
 }\r
 \r
+static HBITMAP borderBitmap;\r
+\r
+VOID\r
+DrawBackgroundOnDC(HDC hdc)\r
+{\r
+  \r
+  BITMAP bi;\r
+  HDC tmphdc;\r
+  HBITMAP hbm;\r
+  static char oldBorder[MSG_SIZ];\r
+  int w = 600, h = 600;\r
+\r
+  if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
+    strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
+    borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );        \r
+  }\r
+  if(borderBitmap == NULL) { // loading failed, use white\r
+    FillRect( hdc, &boardRect, whitePieceBrush );\r
+    return;\r
+  }\r
+  tmphdc = CreateCompatibleDC(hdc);\r
+  hbm = SelectObject(tmphdc, borderBitmap);\r
+  if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
+            w = bi.bmWidth;\r
+            h = bi.bmHeight;\r
+  }\r
+  StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
+                  boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
+  SelectObject(tmphdc, hbm);\r
+  DeleteDC(tmphdc);\r
+}\r
+\r
 VOID\r
 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
 {\r
@@ -3520,6 +3622,14 @@ void DrawSeekDot(int x, int y, int color)
            SelectObject(hdcSeek, oldBrush);\r
 }\r
 \r
+void DrawSeekOpen()\r
+{\r
+}\r
+\r
+void DrawSeekClose()\r
+{\r
+}\r
+\r
 VOID\r
 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
 {\r
@@ -3779,6 +3889,7 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
        Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
        SelectObject(hdcmem, oldBrush);\r
   } else {\r
+    if(border) DrawBackgroundOnDC(hdcmem);\r
     DrawGridOnDC(hdcmem);\r
     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
        DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
@@ -4032,11 +4143,11 @@ PaintProc(HWND hwnd)
 int EventToSquare(x, limit)\r
      int x, limit;\r
 {\r
-  if (x <= 0)\r
+  if (x <= border)\r
     return -2;\r
-  if (x < lineGap)\r
+  if (x < lineGap + border)\r
     return -1;\r
-  x -= lineGap;\r
+  x -= lineGap + border;\r
   if ((x % (squareSize + lineGap)) >= squareSize)\r
     return -1;\r
   x /= (squareSize + lineGap);\r
@@ -4145,9 +4256,9 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   switch (message) {\r
   case WM_LBUTTONDOWN:\r
       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
-        ClockClick(flipClock);\r
+        ClockClick(flipClock); break;\r
       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
-       ClockClick(!flipClock);\r
+       ClockClick(!flipClock); break;\r
       }\r
       dragInfo.start.x = dragInfo.start.y = -1;\r
       dragInfo.from = dragInfo.start;\r
@@ -4255,7 +4366,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       }\r
       break;\r
     case 2:\r
-      SetCapture(hwndMain);
+      SetCapture(hwndMain);\r
       break;\r
     case 1:\r
       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
@@ -4425,14 +4536,6 @@ PromotionPopUp()
   PromotionPopup(hwndMain);\r
 }\r
 \r
-/* Toggle ShowThinking */\r
-VOID\r
-ToggleShowThinking()\r
-{\r
-  appData.showThinking = !appData.showThinking;\r
-  ShowThinkingEvent();\r
-}\r
-\r
 VOID\r
 LoadGameDialog(HWND hwnd, char* title)\r
 {\r
@@ -4573,6 +4676,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   char fileTitle[MSG_SIZ];\r
   char buf[MSG_SIZ];\r
   static SnapData sd;\r
+  static int peek=0;\r
 \r
   switch (message) {\r
 \r
@@ -4600,7 +4704,23 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     MouseEvent(hwnd, message, wParam, lParam);\r
     break;\r
 \r
-  JAWS_KB_NAVIGATION\r
+  case WM_KEYUP:\r
+    if((char)wParam == '\b') {\r
+      ForwardEvent(); peek = 0;\r
+    }\r
+\r
+    JAWS_KBUP_NAVIGATION\r
+\r
+    break;\r
+\r
+  case WM_KEYDOWN:\r
+    if((char)wParam == '\b') {\r
+      if(!peek) BackwardEvent(), peek = 1;\r
+    }\r
+\r
+    JAWS_KBDOWN_NAVIGATION\r
+\r
+    break;\r
 \r
   case WM_CHAR:\r
     \r
@@ -4887,52 +5007,13 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       break;\r
 \r
     case IDM_AnalysisMode:\r
-      if (!first.analysisSupport) {\r
-        snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
-        DisplayError(buf, 0);\r
-      } else {\r
+      if(AnalyzeModeEvent()) {\r
        SAY("analyzing current position");\r
-        /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
-        if (appData.icsActive) {\r
-               if (gameMode != IcsObserving) {\r
-                snprintf(buf, MSG_SIZ, "You are not observing a game");\r
-                       DisplayError(buf, 0);\r
-                       /* secure check */\r
-                       if (appData.icsEngineAnalyze) {\r
-                               if (appData.debugMode) \r
-                                       fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
-                               ExitAnalyzeMode();\r
-                               ModeHighlight();\r
-                               break;\r
-                       }\r
-                       break;\r
-               } else {\r
-                       /* if enable, user want disable icsEngineAnalyze */\r
-                       if (appData.icsEngineAnalyze) {\r
-                               ExitAnalyzeMode();\r
-                               ModeHighlight();\r
-                               break;\r
-                       }\r
-                       appData.icsEngineAnalyze = TRUE;\r
-                       if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
-               }\r
-        } \r
-       if (!appData.showThinking) ToggleShowThinking();\r
-       AnalyzeModeEvent();\r
       }\r
       break;\r
 \r
     case IDM_AnalyzeFile:\r
-      if (!first.analysisSupport) {\r
-        char buf[MSG_SIZ];\r
-         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
-        DisplayError(buf, 0);\r
-      } else {\r
-       if (!appData.showThinking) ToggleShowThinking();\r
-       AnalyzeFileEvent();\r
-//     LoadGameDialog(hwnd, _("Analyze Game from File"));\r
-       AnalysisPeriodicEvent(1);\r
-      }\r
+      AnalyzeFileEvent();\r
       break;\r
 \r
     case IDM_IcsClient:\r
@@ -4963,9 +5044,12 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
       break;\r
 \r
-    case IDM_EditProgs2:\r
-     LoadEnginePopUp(hwndMain);\r
-//      EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);\r
+    case IDM_LoadProg1:\r
+     LoadEnginePopUp(hwndMain, 0);\r
+      break;\r
+\r
+    case IDM_LoadProg2:\r
+     LoadEnginePopUp(hwndMain, 1);\r
       break;\r
 \r
     case IDM_EditServers:\r
@@ -5434,6 +5518,9 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       break;\r
 \r
     default:\r
+      if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
+          RecentEngineEvent(wmId - IDM_RecentEngines);\r
+      else\r
       if(wmId > IDM_English && wmId < IDM_English+20) {\r
           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
           TranslateMenus(0);\r
@@ -6145,10 +6232,11 @@ StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
        GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
         p = buf;\r
+       comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
        ParseArgs(StringGet, &p);\r
        safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
        GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
-        p = buf;
+        p = buf;\r
        SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
        ParseArgs(StringGet, &p);\r
        SwapEngines(singleList); // ... and then make it 'second'\r
@@ -6458,10 +6546,10 @@ TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
 \r
   case WM_COMMAND:\r
     switch (LOWORD(wParam)) {\r
-    case IDOK:
+    case IDOK:\r
 \r
       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
-      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
+      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
       TypeInDoneEvent(move);\r
       EndDialog(hDlg, TRUE);\r
       return TRUE;\r
@@ -7424,7 +7512,7 @@ DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
   HFONT oldFont;\r
   RECT rect;\r
 \r
-  if(copyNumber > 1)
+  if(copyNumber > 1)\r
     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
 \r
   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
@@ -7456,6 +7544,7 @@ DisplayAClock(HDC hdc, int timeRemaining, int highlight,
   COLORREF oldFg, oldBg;\r
   HFONT oldFont;\r
 \r
+  if (twoBoards && partnerUp) return;\r
   if (appData.clockMode) {\r
     if (tinyLayout)\r
       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
@@ -7820,7 +7909,8 @@ Enables icsEnables[] = {
   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
-  { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },\r
+  { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
+  { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
@@ -8174,8 +8264,8 @@ DisplayMessage(char *str1, char *str2)
     if (len > remain) len = remain;\r
     strncat(messageText, str2, len);\r
   }\r
-  messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
-  safeStrCpy(lastMsg, messageText, MSG_SIZ);
+  messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
+  safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
 \r
   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
 \r
@@ -8535,7 +8625,19 @@ DisplayIcsInteractionTitle(char *str)
   char consoleTitle[MSG_SIZ];\r
 \r
     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
-  SetWindowText(hwndConsole, consoleTitle);\r
+    SetWindowText(hwndConsole, consoleTitle);\r
+\r
+    if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
+      char buf[MSG_SIZ], *p = buf, *q;\r
+       safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
+      do {\r
+       q = strchr(p, ';');\r
+       if(q) *q++ = 0;\r
+       if(*p) ChatPopUp(p);\r
+      } while(p=q);\r
+    }\r
+\r
+    SetActiveWindow(hwndMain);\r
 }\r
 \r
 void\r
@@ -9153,6 +9255,7 @@ OpenTCP(char *host, char *port, ProcRef *pr)
   ChildProc *cp;\r
   int err;\r
   SOCKET s;\r
+\r
   struct sockaddr_in sa, mysa;\r
   struct hostent FAR *hp;\r
   unsigned short uport;\r
@@ -9824,11 +9927,11 @@ ScreenSquare(column, row, pt)
      int column; int row; POINT * pt;\r
 {\r
   if (flipView) {\r
-    pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
-    pt->y = lineGap + row * (squareSize + lineGap);\r
+    pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
+    pt->y = lineGap + row * (squareSize + lineGap) + border;\r
   } else {\r
-    pt->x = lineGap + column * (squareSize + lineGap);\r
-    pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
+    pt->x = lineGap + column * (squareSize + lineGap) + border;\r
+    pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
   }\r
 }\r
 \r