Adapt WinBoard front-end to Mighty Lion
[xboard.git] / winboard / winboard.c
index f263124..1539f42 100644 (file)
@@ -5,7 +5,7 @@
  * Massachusetts. \r
  *\r
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
- * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.\r
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013 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
                   /*char*/int promoChar));\r
 void DisplayMove P((int moveNumber));\r
-Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
 void ChatPopUp P((char *s));\r
 typedef struct {\r
   ChessSquare piece;  \r
@@ -162,7 +163,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
@@ -185,6 +186,7 @@ Boolean alwaysOnTop = FALSE;
 RECT boardRect;\r
 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
+COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
 HPALETTE hPal;\r
 ColorClass currentColorClass;\r
 \r
@@ -195,7 +197,7 @@ static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred
 static HBRUSH lightSquareBrush, darkSquareBrush,\r
   blackSquareBrush, /* [HGM] for band between board and holdings */\r
   explodeBrush,     /* [HGM] atomic */\r
-  markerBrush,      /* [HGM] markers */\r
+  markerBrush[8],   /* [HGM] markers */\r
   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
@@ -220,6 +222,7 @@ static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOA
 #if __GNUC__ && !defined(_winmajor)\r
 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
 #else\r
+\r
 #if defined(_winmajor)\r
 #define oldDialog (_winmajor < 4)\r
 #else\r
@@ -294,7 +297,7 @@ int dialogItems[][42] = {
   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
-  GPB_General, GPB_Alarm }, \r
+  GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
@@ -1004,8 +1007,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
@@ -1022,6 +1031,7 @@ InitTextures()
   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
   \r
   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
+      if(liteBackTexture) DeleteObject(liteBackTexture);\r
       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
       liteBackTextureMode = appData.liteBackTextureMode;\r
 \r
@@ -1031,6 +1041,7 @@ InitTextures()
   }\r
   \r
   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
+      if(darkBackTexture) DeleteObject(darkBackTexture);\r
       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
       darkBackTextureMode = appData.darkBackTextureMode;\r
 \r
@@ -1053,6 +1064,7 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
 \r
   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
     *filepart = NULLCHAR;\r
+    SetCurrentDirectory(installDir);\r
   } else {\r
     GetCurrentDirectory(MSG_SIZ, installDir);\r
   }\r
@@ -1106,6 +1118,7 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
   InitDrawingColors();\r
   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
+  InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
     /* Compute window size for each board size, and use the largest\r
        size that fits on this screen as the default. */\r
@@ -1269,6 +1282,7 @@ ParseFontName(char *name, MyFontParams *mfp)
     p = q + 1;\r
   } else {\r
     q = mfp->faceName;\r
+\r
     while (*p && !isdigit(*p)) {\r
       *q++ = *p++;\r
       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
@@ -2105,9 +2119,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
@@ -2144,6 +2164,7 @@ InsertInPalette(COLORREF color)
 VOID\r
 InitDrawingColors()\r
 {\r
+  int i;\r
   if (pLogPal == NULL) {\r
     /* Allocate enough memory for a logical palette with\r
      * PALETTESIZE entries and set the size and version fields\r
@@ -2175,8 +2196,9 @@ InitDrawingColors()
   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
-  markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
-  /* [AS] Force rendering of the font-based pieces */\r
+    for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
+\r
+   /* [AS] Force rendering of the font-based pieces */\r
   if( fontBitmapSquareSize > 0 ) {\r
     fontBitmapSquareSize = 0;\r
   }\r
@@ -2234,12 +2256,29 @@ 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
+  if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\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 || v == VariantLion ) {\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 == 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
@@ -2251,6 +2290,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
@@ -2275,8 +2315,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
@@ -2348,7 +2388,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
@@ -2448,20 +2487,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
@@ -2589,6 +2628,9 @@ InitDrawingSizes(BoardSize boardSize, int flags)
     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
+    pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
+    pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
+    pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
 \r
     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
@@ -2714,11 +2756,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
@@ -2745,15 +2787,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
@@ -2790,14 +2840,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
@@ -2875,7 +2925,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
@@ -2888,7 +2940,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
@@ -2900,6 +2952,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
@@ -3188,6 +3252,9 @@ BOOL HasHighlightInfo()
     }\r
 \r
     return result;\r
+\r
+\r
+\r
 }\r
 \r
 BOOL IsDrawArrowEnabled()\r
@@ -3287,6 +3354,40 @@ 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, mode;\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
+  mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
+  StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
+                  boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
+  SetStretchBltMode(hdc, mode);\r
+  SelectObject(tmphdc, hbm);\r
+  DeleteDC(tmphdc);\r
+}\r
+\r
 VOID\r
 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
 {\r
@@ -3345,6 +3446,7 @@ DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
       else if( column == BOARD_RGHT) /* right align */\r
             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
+      else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
       else\r
       if (appData.monoMode) {\r
         if (piece == EmptySquare) {\r
@@ -3527,7 +3629,7 @@ void DrawSeekDot(int x, int y, int color)
 {\r
        int square = color & 0x80;\r
        HBRUSH oldBrush = SelectObject(hdcSeek, \r
-                       color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
+                       color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
        color &= 0x7F;\r
        if(square)\r
            Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
@@ -3538,6 +3640,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
@@ -3797,6 +3907,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
@@ -3811,8 +3922,7 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
   for (row = 0; row < BOARD_HEIGHT; row++) {\r
     for (column = 0; column < BOARD_WIDTH; column++) {\r
        if (marker[row][column]) { // marker changes only occur with full repaint!\r
-           HBRUSH oldBrush = SelectObject(hdcmem, \r
-                       marker[row][column] == 2 ? markerBrush : explodeBrush);\r
+           HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
            SquareToPos(row, column, &x, &y);\r
            Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
                          x + 3*squareSize/4, y + 3*squareSize/4);\r
@@ -4050,11 +4160,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
@@ -4159,6 +4269,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   }\r
 \r
   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
+  controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
 \r
   switch (message) {\r
   case WM_LBUTTONDOWN:\r
@@ -4167,8 +4278,10 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
        ClockClick(!flipClock); break;\r
       }\r
+    if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
       dragInfo.start.x = dragInfo.start.y = -1;\r
       dragInfo.from = dragInfo.start;\r
+    }\r
     if(fromX == -1 && frozen) { // not sure where this is for\r
                fromX = fromY = -1; \r
       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
@@ -4188,7 +4301,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
     if ((appData.animateDragging || appData.highlightDragging)\r
-       && (wParam & MK_LBUTTON)\r
+       && (wParam & MK_LBUTTON || dragging == 2)\r
        && dragInfo.from.x >= 0) \r
     {\r
       BOOL full_repaint = FALSE;\r
@@ -4197,7 +4310,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        dragInfo.pos = pt;\r
       }\r
       if (appData.highlightDragging) {\r
-       SetHighlights(fromX, fromY, x, y);\r
+       HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
             full_repaint = TRUE;\r
         }\r
@@ -4443,14 +4556,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
@@ -4589,7 +4694,6 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   FILE *f;\r
   UINT number;\r
   char fileTitle[MSG_SIZ];\r
-  char buf[MSG_SIZ];\r
   static SnapData sd;\r
   static int peek=0;\r
 \r
@@ -4664,6 +4768,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       nnew = RealizePalette(hdc);\r
       if (nnew > 0) {\r
        paletteChanged = TRUE;\r
+\r
         InvalidateRect(hwnd, &boardRect, FALSE);\r
       }\r
       ReleaseDC(hwnd, hdc);\r
@@ -4780,6 +4885,10 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       }\r
       break;\r
 \r
+    case IDM_CreateBook:\r
+      CreateBookEvent();\r
+      break;\r
+\r
     case IDM_CopyGame:\r
       CopyGameToClipboard();\r
       break;\r
@@ -4922,52 +5031,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
@@ -5041,6 +5111,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       break;\r
 \r
     case IDM_Rematch:\r
+\r
       RematchEvent();\r
       break;\r
 \r
@@ -5157,6 +5228,10 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       BoardOptionsPopup(hwnd);\r
       break;\r
 \r
+    case IDM_ThemeOptions:\r
+      ThemeOptionsPopup(hwnd);\r
+      break;\r
+\r
     case IDM_EnginePlayOptions:\r
       EnginePlayOptionsPopup(hwnd);\r
       break;\r
@@ -6194,6 +6269,7 @@ StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
        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
+\r
        appData.noChessProgram = FALSE;\r
        appData.icsActive = FALSE;\r
       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
@@ -6783,6 +6859,7 @@ SaveInHistory(char *cmd)
   histIn = (histIn + 1) % HISTORY_SIZE;\r
   if (history[histIn] != NULL) {\r
     free(history[histIn]);\r
+\r
     history[histIn] = NULL;\r
   }\r
   histP = histIn;\r
@@ -8649,6 +8726,13 @@ EditCommentPopUp(int index, char *title, char *str)
 }\r
 \r
 \r
+int\r
+Roar()\r
+{\r
+  MyPlaySound(&sounds[(int)SoundRoar]);\r
+  return 1;\r
+}\r
+\r
 VOID\r
 RingBell()\r
 {\r
@@ -8806,6 +8890,7 @@ DisplayBlackClock(long timeRemaining, int highlight)
   HDC hdc;\r
   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
 \r
+\r
   if(appData.noGUI) return;\r
   hdc = GetDC(hwndMain);\r
   if (!IsIconic(hwndMain)) {\r
@@ -9712,7 +9797,7 @@ CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
 /* see wedittags.c for Edit Tags functions */\r
 \r
 \r
-VOID\r
+int\r
 ICSInitScript()\r
 {\r
   FILE *f;\r
@@ -9724,8 +9809,10 @@ ICSInitScript()
     if (f != NULL) {\r
       ProcessICSInitScript(f);\r
       fclose(f);\r
+      return TRUE;\r
     }\r
   }\r
+  return FALSE;\r
 }\r
 \r
 \r
@@ -9822,16 +9909,23 @@ AnimateMove(board, fromX, fromY, toX, toY)
      int toY;\r
 {\r
   ChessSquare piece;\r
+  int x = toX, y = toY;\r
   POINT start, finish, mid;\r
   POINT frames[kFactor * 2 + 1];\r
   int nFrames, n;\r
 \r
+  if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
+\r
   if (!appData.animate) return;\r
   if (doingSizing) return;\r
   if (fromY < 0 || fromX < 0) return;\r
   piece = board[fromY][fromX];\r
   if (piece >= EmptySquare) return;\r
 \r
+  if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
+\r
+again:\r
+\r
   ScreenSquare(fromX, fromY, &start);\r
   ScreenSquare(toX, toY, &finish);\r
 \r
@@ -9870,6 +9964,9 @@ AnimateMove(board, fromX, fromY, toX, toY)
   }\r
   animInfo.pos = finish;\r
   DrawPosition(FALSE, NULL);\r
+\r
+  if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
+\r
   animInfo.piece = EmptySquare;\r
   Explode(board, fromX, fromY, toX, toY);\r
 }\r
@@ -9881,11 +9978,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
@@ -9953,3 +10050,24 @@ int flock(int fid, int code)
     }\r
     return 0;\r
 }\r
+\r
+char *\r
+Col2Text (int n)\r
+{\r
+    static int i=0;\r
+    static char col[8][20];\r
+    COLORREF color = *(COLORREF *) colorVariable[n];\r
+    i = i+1 & 7;\r
+    snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
+    return col[i];\r
+}\r
+\r
+void\r
+ActivateTheme (int new)\r
+{   // Redo initialization of features depending on options that can occur in themes\r
+   InitTextures();\r
+   if(new) InitDrawingColors();\r
+   fontBitmapSquareSize = 0; // request creation of new font pieces\r
+   InitDrawingSizes(boardSize, 0);\r
+   InvalidateRect(hwndMain, NULL, TRUE);\r
+}\r