Expand ~~/ to bundle path (OSX)
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 #define SLASH '/'\r
96 #define DATADIR "~~"\r
97 \r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
99 \r
100   int myrandom(void);\r
101   void mysrandom(unsigned int seed);\r
102 \r
103 extern int whiteFlag, blackFlag;\r
104 Boolean flipClock = FALSE;\r
105 extern HANDLE chatHandle[];\r
106 extern enum ICS_TYPE ics_type;\r
107 \r
108 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
109 int  MyGetFullPathName P((char *name, char *fullname));\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
111 VOID NewVariantPopup(HWND hwnd);\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
113                    /*char*/int promoChar));\r
114 void DisplayMove P((int moveNumber));\r
115 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
116 void ChatPopUp P((char *s));\r
117 typedef struct {\r
118   ChessSquare piece;  \r
119   POINT pos;      /* window coordinates of current pos */\r
120   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
121   POINT from;     /* board coordinates of the piece's orig pos */\r
122   POINT to;       /* board coordinates of the piece's new pos */\r
123 } AnimInfo;\r
124 \r
125 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
126 \r
127 typedef struct {\r
128   POINT start;    /* window coordinates of start pos */\r
129   POINT pos;      /* window coordinates of current pos */\r
130   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
131   POINT from;     /* board coordinates of the piece's orig pos */\r
132   ChessSquare piece;\r
133 } DragInfo;\r
134 \r
135 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
136 \r
137 typedef struct {\r
138   POINT sq[2];    /* board coordinates of from, to squares */\r
139 } HighlightInfo;\r
140 \r
141 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
143 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
144 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
145 \r
146 typedef struct { // [HGM] atomic\r
147   int fromX, fromY, toX, toY, radius;\r
148 } ExplodeInfo;\r
149 \r
150 static ExplodeInfo explodeInfo;\r
151 \r
152 /* Window class names */\r
153 char szAppName[] = "WinBoard";\r
154 char szConsoleName[] = "WBConsole";\r
155 \r
156 /* Title bar text */\r
157 char szTitle[] = "WinBoard";\r
158 char szConsoleTitle[] = "I C S Interaction";\r
159 \r
160 char *programName;\r
161 char *settingsFileName;\r
162 Boolean saveSettingsOnExit;\r
163 char installDir[MSG_SIZ];\r
164 int errorExitStatus;\r
165 \r
166 BoardSize boardSize;\r
167 Boolean chessProgram;\r
168 //static int boardX, boardY;\r
169 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
170 int squareSize, lineGap, minorSize, border;\r
171 static int winW, winH;\r
172 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
173 static int logoHeight = 0;\r
174 static char messageText[MESSAGE_TEXT_MAX];\r
175 static int clockTimerEvent = 0;\r
176 static int loadGameTimerEvent = 0;\r
177 static int analysisTimerEvent = 0;\r
178 static DelayedEventCallback delayedTimerCallback;\r
179 static int delayedTimerEvent = 0;\r
180 static int buttonCount = 2;\r
181 char *icsTextMenuString;\r
182 char *icsNames;\r
183 char *firstChessProgramNames;\r
184 char *secondChessProgramNames;\r
185 \r
186 #define PALETTESIZE 256\r
187 \r
188 HINSTANCE hInst;          /* current instance */\r
189 Boolean alwaysOnTop = FALSE;\r
190 RECT boardRect;\r
191 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
192   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
193 HPALETTE hPal;\r
194 ColorClass currentColorClass;\r
195 \r
196 static HWND savedHwnd;\r
197 HWND hCommPort = NULL;    /* currently open comm port */\r
198 static HWND hwndPause;    /* pause button */\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,\r
201   blackSquareBrush, /* [HGM] for band between board and holdings */\r
202   explodeBrush,     /* [HGM] atomic */\r
203   markerBrush,      /* [HGM] markers */\r
204   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
207 static HPEN gridPen = NULL;\r
208 static HPEN highlightPen = NULL;\r
209 static HPEN premovePen = NULL;\r
210 static NPLOGPALETTE pLogPal;\r
211 static BOOL paletteChanged = FALSE;\r
212 static HICON iconWhite, iconBlack, iconCurrent;\r
213 static int doingSizing = FALSE;\r
214 static int lastSizing = 0;\r
215 static int prevStderrPort;\r
216 static HBITMAP userLogo;\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 \r
229 \r
230 \r
231 #if defined(_winmajor)\r
232 #define oldDialog (_winmajor < 4)\r
233 #else\r
234 #define oldDialog 0\r
235 #endif\r
236 #endif\r
237 \r
238 #define INTERNATIONAL\r
239 \r
240 #ifdef INTERNATIONAL\r
241 #  define _(s) T_(s)\r
242 #  define N_(s) s\r
243 #else\r
244 #  define _(s) s\r
245 #  define N_(s) s\r
246 #  define T_(s) s\r
247 #  define Translate(x, y)\r
248 #  define LoadLanguageFile(s)\r
249 #endif\r
250 \r
251 #ifdef INTERNATIONAL\r
252 \r
253 Boolean barbaric; // flag indicating if translation is needed\r
254 \r
255 // list of item numbers used in each dialog (used to alter language at run time)\r
256 \r
257 #define ABOUTBOX -1  /* not sure why these are needed */\r
258 #define ABOUTBOX2 -1\r
259 \r
260 int dialogItems[][42] = {\r
261 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
262 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
263   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
264 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
265   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
266 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
267   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
268 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
269 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
270   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
271 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
272 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
273   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
274 { ABOUTBOX2, IDC_ChessBoard }, \r
275 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
276   OPT_GameListClose, IDC_GameListDoFilter }, \r
277 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
278 { DLG_Error, IDOK }, \r
279 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
280   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
281 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
282 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
283   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
284   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
285 { DLG_IndexNumber, IDC_Index }, \r
286 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
287 { DLG_TypeInName, IDOK, IDCANCEL }, \r
288 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
289   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
290 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
291   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
292   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
293   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
294   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
295   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
296   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
297 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
298   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
299   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
300   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
301   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
302   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
303   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
304   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
305   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
306 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
307   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
308   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
309   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
310   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
311   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
312   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
313   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
314 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
315   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
316   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
317   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
318   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
319   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
320   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
321   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
322   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
323 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
324   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
325   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
326   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
327   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
328 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
329 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
330   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
331 { DLG_MoveHistory }, \r
332 { DLG_EvalGraph }, \r
333 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
334 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
335 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
336   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
337   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
338   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
339 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
340   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
341   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
342 { 0 }\r
343 };\r
344 \r
345 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
346 static int lastChecked;\r
347 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
348 extern int tinyLayout;\r
349 extern char * menuBarText[][10];\r
350 \r
351 void\r
352 LoadLanguageFile(char *name)\r
353 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
354     FILE *f;\r
355     int i=0, j=0, n=0, k;\r
356     char buf[MSG_SIZ];\r
357 \r
358     if(!name || name[0] == NULLCHAR) return;\r
359       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
360     appData.language = oldLanguage;\r
361     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
362     if((f = fopen(buf, "r")) == NULL) return;\r
363     while((k = fgetc(f)) != EOF) {\r
364         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
365         languageBuf[i] = k;\r
366         if(k == '\n') {\r
367             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
368                 char *p;\r
369                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
370                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
371                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
372                         english[j] = languageBuf + n + 1; *p = 0;\r
373                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
374 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
375                     }\r
376                 }\r
377             }\r
378             n = i + 1;\r
379         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
380             switch(k) {\r
381               case 'n': k = '\n'; break;\r
382               case 'r': k = '\r'; break;\r
383               case 't': k = '\t'; break;\r
384             }\r
385             languageBuf[--i] = k;\r
386         }\r
387         i++;\r
388     }\r
389     fclose(f);\r
390     barbaric = (j != 0);\r
391     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
392 }\r
393 \r
394 char *\r
395 T_(char *s)\r
396 {   // return the translation of the given string\r
397     // efficiency can be improved a lot...\r
398     int i=0;\r
399     static char buf[MSG_SIZ];\r
400 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
401     if(!barbaric) return s;\r
402     if(!s) return ""; // sanity\r
403     while(english[i]) {\r
404         if(!strcmp(s, english[i])) return foreign[i];\r
405         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
406             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
407             return buf;\r
408         }\r
409         i++;\r
410     }\r
411     return s;\r
412 }\r
413 \r
414 void\r
415 Translate(HWND hDlg, int dialogID)\r
416 {   // translate all text items in the given dialog\r
417     int i=0, j, k;\r
418     char buf[MSG_SIZ], *s;\r
419     if(!barbaric) return;\r
420     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
421     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
422     GetWindowText( hDlg, buf, MSG_SIZ );\r
423     s = T_(buf);\r
424     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
425     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
426         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
427         if(strlen(buf) == 0) continue;\r
428         s = T_(buf);\r
429         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
430     }\r
431 }\r
432 \r
433 HMENU\r
434 TranslateOneMenu(int i, HMENU subMenu)\r
435 {\r
436     int j;\r
437     static MENUITEMINFO info;\r
438 \r
439     info.cbSize = sizeof(MENUITEMINFO);\r
440     info.fMask = MIIM_STATE | MIIM_TYPE;\r
441           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
442             char buf[MSG_SIZ];\r
443             info.dwTypeData = buf;\r
444             info.cch = sizeof(buf);\r
445             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
446             if(i < 10) {\r
447                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
448                 else menuText[i][j] = strdup(buf); // remember original on first change\r
449             }\r
450             if(buf[0] == NULLCHAR) continue;\r
451             info.dwTypeData = T_(buf);\r
452             info.cch = strlen(buf)+1;\r
453             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
454           }\r
455     return subMenu;\r
456 }\r
457 \r
458 void\r
459 TranslateMenus(int addLanguage)\r
460 {\r
461     int i;\r
462     WIN32_FIND_DATA fileData;\r
463     HANDLE hFind;\r
464 #define IDM_English 1970\r
465     if(1) {\r
466         HMENU mainMenu = GetMenu(hwndMain);\r
467         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
468           HMENU subMenu = GetSubMenu(mainMenu, i);\r
469           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
470                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
471           TranslateOneMenu(i, subMenu);\r
472         }\r
473         DrawMenuBar(hwndMain);\r
474     }\r
475 \r
476     if(!addLanguage) return;\r
477     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
478         HMENU mainMenu = GetMenu(hwndMain);\r
479         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
480         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
481         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
482         i = 0; lastChecked = IDM_English;\r
483         do {\r
484             char *p, *q = fileData.cFileName;\r
485             int checkFlag = MF_UNCHECKED;\r
486             languageFile[i] = strdup(q);\r
487             if(barbaric && !strcmp(oldLanguage, q)) {\r
488                 checkFlag = MF_CHECKED;\r
489                 lastChecked = IDM_English + i + 1;\r
490                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
491             }\r
492             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
493             p = strstr(fileData.cFileName, ".lng");\r
494             if(p) *p = 0;\r
495             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
496         } while(FindNextFile(hFind, &fileData));\r
497         FindClose(hFind);\r
498     }\r
499 }\r
500 \r
501 #endif\r
502 \r
503 #define IDM_RecentEngines 3000\r
504 \r
505 void\r
506 RecentEngineMenu (char *s)\r
507 {\r
508     if(appData.icsActive) return;\r
509     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
510         HMENU mainMenu = GetMenu(hwndMain);\r
511         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
512         int i=IDM_RecentEngines;\r
513         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
514         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
515         while(*s) {\r
516           char *p = strchr(s, '\n');\r
517           if(p == NULL) return; // malformed!\r
518           *p = NULLCHAR;\r
519           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
520           *p = '\n';\r
521           s = p+1;\r
522         }\r
523     }\r
524 }\r
525 \r
526 \r
527 typedef struct {\r
528   char *name;\r
529   int squareSize;\r
530   int lineGap;\r
531   int smallLayout;\r
532   int tinyLayout;\r
533   int cliWidth, cliHeight;\r
534 } SizeInfo;\r
535 \r
536 SizeInfo sizeInfo[] = \r
537 {\r
538   { "tiny",     21, 0, 1, 1, 0, 0 },\r
539   { "teeny",    25, 1, 1, 1, 0, 0 },\r
540   { "dinky",    29, 1, 1, 1, 0, 0 },\r
541   { "petite",   33, 1, 1, 1, 0, 0 },\r
542   { "slim",     37, 2, 1, 0, 0, 0 },\r
543   { "small",    40, 2, 1, 0, 0, 0 },\r
544   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
545   { "middling", 49, 2, 0, 0, 0, 0 },\r
546   { "average",  54, 2, 0, 0, 0, 0 },\r
547   { "moderate", 58, 3, 0, 0, 0, 0 },\r
548   { "medium",   64, 3, 0, 0, 0, 0 },\r
549   { "bulky",    72, 3, 0, 0, 0, 0 },\r
550   { "large",    80, 3, 0, 0, 0, 0 },\r
551   { "big",      87, 3, 0, 0, 0, 0 },\r
552   { "huge",     95, 3, 0, 0, 0, 0 },\r
553   { "giant",    108, 3, 0, 0, 0, 0 },\r
554   { "colossal", 116, 4, 0, 0, 0, 0 },\r
555   { "titanic",  129, 4, 0, 0, 0, 0 },\r
556   { NULL, 0, 0, 0, 0, 0, 0 }\r
557 };\r
558 \r
559 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
560 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
561 {\r
562   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
563   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
564   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
565   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
566   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
567   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
568   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
569   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
570   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
571   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
572   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
573   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
574   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
575   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
576   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
577   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
578   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
579   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
580 };\r
581 \r
582 MyFont *font[NUM_SIZES][NUM_FONTS];\r
583 \r
584 typedef struct {\r
585   char *label;\r
586   int id;\r
587   HWND hwnd;\r
588   WNDPROC wndproc;\r
589 } MyButtonDesc;\r
590 \r
591 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
592 #define N_BUTTONS 5\r
593 \r
594 MyButtonDesc buttonDesc[N_BUTTONS] =\r
595 {\r
596   {"<<", IDM_ToStart, NULL, NULL},\r
597   {"<", IDM_Backward, NULL, NULL},\r
598   {"P", IDM_Pause, NULL, NULL},\r
599   {">", IDM_Forward, NULL, NULL},\r
600   {">>", IDM_ToEnd, NULL, NULL},\r
601 };\r
602 \r
603 int tinyLayout = 0, smallLayout = 0;\r
604 #define MENU_BAR_ITEMS 9\r
605 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
606   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
607   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
608 };\r
609 \r
610 \r
611 MySound sounds[(int)NSoundClasses];\r
612 MyTextAttribs textAttribs[(int)NColorClasses];\r
613 \r
614 MyColorizeAttribs colorizeAttribs[] = {\r
615   { (COLORREF)0, 0, N_("Shout Text") },\r
616   { (COLORREF)0, 0, N_("SShout/CShout") },\r
617   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
618   { (COLORREF)0, 0, N_("Channel Text") },\r
619   { (COLORREF)0, 0, N_("Kibitz Text") },\r
620   { (COLORREF)0, 0, N_("Tell Text") },\r
621   { (COLORREF)0, 0, N_("Challenge Text") },\r
622   { (COLORREF)0, 0, N_("Request Text") },\r
623   { (COLORREF)0, 0, N_("Seek Text") },\r
624   { (COLORREF)0, 0, N_("Normal Text") },\r
625   { (COLORREF)0, 0, N_("None") }\r
626 };\r
627 \r
628 \r
629 \r
630 static char *commentTitle;\r
631 static char *commentText;\r
632 static int commentIndex;\r
633 static Boolean editComment = FALSE;\r
634 \r
635 \r
636 char errorTitle[MSG_SIZ];\r
637 char errorMessage[2*MSG_SIZ];\r
638 HWND errorDialog = NULL;\r
639 BOOLEAN moveErrorMessageUp = FALSE;\r
640 BOOLEAN consoleEcho = TRUE;\r
641 CHARFORMAT consoleCF;\r
642 COLORREF consoleBackgroundColor;\r
643 \r
644 char *programVersion;\r
645 \r
646 #define CPReal 1\r
647 #define CPComm 2\r
648 #define CPSock 3\r
649 #define CPRcmd 4\r
650 typedef int CPKind;\r
651 \r
652 typedef struct {\r
653   CPKind kind;\r
654   HANDLE hProcess;\r
655   DWORD pid;\r
656   HANDLE hTo;\r
657   HANDLE hFrom;\r
658   SOCKET sock;\r
659   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
660 } ChildProc;\r
661 \r
662 #define INPUT_SOURCE_BUF_SIZE 4096\r
663 \r
664 typedef struct _InputSource {\r
665   CPKind kind;\r
666   HANDLE hFile;\r
667   SOCKET sock;\r
668   int lineByLine;\r
669   HANDLE hThread;\r
670   DWORD id;\r
671   char buf[INPUT_SOURCE_BUF_SIZE];\r
672   char *next;\r
673   DWORD count;\r
674   int error;\r
675   InputCallback func;\r
676   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
677   VOIDSTAR closure;\r
678 } InputSource;\r
679 \r
680 InputSource *consoleInputSource;\r
681 \r
682 DCB dcb;\r
683 \r
684 /* forward */\r
685 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
686 VOID ConsoleCreate();\r
687 LRESULT CALLBACK\r
688   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
689 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
690 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
691 VOID ParseCommSettings(char *arg, DCB *dcb);\r
692 LRESULT CALLBACK\r
693   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
694 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
695 void ParseIcsTextMenu(char *icsTextMenuString);\r
696 VOID PopUpNameDialog(char firstchar);\r
697 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
698 \r
699 /* [AS] */\r
700 int NewGameFRC();\r
701 int GameListOptions();\r
702 \r
703 int dummy; // [HGM] for obsolete args\r
704 \r
705 HWND hwndMain = NULL;        /* root window*/\r
706 HWND hwndConsole = NULL;\r
707 HWND commentDialog = NULL;\r
708 HWND moveHistoryDialog = NULL;\r
709 HWND evalGraphDialog = NULL;\r
710 HWND engineOutputDialog = NULL;\r
711 HWND gameListDialog = NULL;\r
712 HWND editTagsDialog = NULL;\r
713 \r
714 int commentUp = FALSE;\r
715 \r
716 WindowPlacement wpMain;\r
717 WindowPlacement wpConsole;\r
718 WindowPlacement wpComment;\r
719 WindowPlacement wpMoveHistory;\r
720 WindowPlacement wpEvalGraph;\r
721 WindowPlacement wpEngineOutput;\r
722 WindowPlacement wpGameList;\r
723 WindowPlacement wpTags;\r
724 \r
725 VOID EngineOptionsPopup(); // [HGM] settings\r
726 \r
727 VOID GothicPopUp(char *title, VariantClass variant);\r
728 /*\r
729  * Setting "frozen" should disable all user input other than deleting\r
730  * the window.  We do this while engines are initializing themselves.\r
731  */\r
732 static int frozen = 0;\r
733 static int oldMenuItemState[MENU_BAR_ITEMS];\r
734 void FreezeUI()\r
735 {\r
736   HMENU hmenu;\r
737   int i;\r
738 \r
739   if (frozen) return;\r
740   frozen = 1;\r
741   hmenu = GetMenu(hwndMain);\r
742   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
743     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
744   }\r
745   DrawMenuBar(hwndMain);\r
746 }\r
747 \r
748 /* Undo a FreezeUI */\r
749 void ThawUI()\r
750 {\r
751   HMENU hmenu;\r
752   int i;\r
753 \r
754   if (!frozen) return;\r
755   frozen = 0;\r
756   hmenu = GetMenu(hwndMain);\r
757   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
758     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
759   }\r
760   DrawMenuBar(hwndMain);\r
761 }\r
762 \r
763 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
764 \r
765 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
766 #ifdef JAWS\r
767 #include "jaws.c"\r
768 #else\r
769 #define JAWS_INIT\r
770 #define JAWS_ARGS\r
771 #define JAWS_ALT_INTERCEPT\r
772 #define JAWS_KBUP_NAVIGATION\r
773 #define JAWS_KBDOWN_NAVIGATION\r
774 #define JAWS_MENU_ITEMS\r
775 #define JAWS_SILENCE\r
776 #define JAWS_REPLAY\r
777 #define JAWS_ACCEL\r
778 #define JAWS_COPYRIGHT\r
779 #define JAWS_DELETE(X) X\r
780 #define SAYMACHINEMOVE()\r
781 #define SAY(X)\r
782 #endif\r
783 \r
784 /*---------------------------------------------------------------------------*\\r
785  *\r
786  * WinMain\r
787  *\r
788 \*---------------------------------------------------------------------------*/\r
789 \r
790 int APIENTRY\r
791 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
792         LPSTR lpCmdLine, int nCmdShow)\r
793 {\r
794   MSG msg;\r
795   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
796 //  INITCOMMONCONTROLSEX ex;\r
797 \r
798   debugFP = stderr;\r
799 \r
800   LoadLibrary("RICHED32.DLL");\r
801   consoleCF.cbSize = sizeof(CHARFORMAT);\r
802 \r
803   if (!InitApplication(hInstance)) {\r
804     return (FALSE);\r
805   }\r
806   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
807     return (FALSE);\r
808   }\r
809 \r
810   JAWS_INIT\r
811   TranslateMenus(1);\r
812 \r
813 //  InitCommonControlsEx(&ex);\r
814   InitCommonControls();\r
815 \r
816   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
817   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
818   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
819 \r
820   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
821 \r
822   while (GetMessage(&msg, /* message structure */\r
823                     NULL, /* handle of window receiving the message */\r
824                     0,    /* lowest message to examine */\r
825                     0))   /* highest message to examine */\r
826     {\r
827 \r
828       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
829         // [HGM] navigate: switch between all windows with tab\r
830         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
831         int i, currentElement = 0;\r
832 \r
833         // first determine what element of the chain we come from (if any)\r
834         if(appData.icsActive) {\r
835             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
836             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
837         }\r
838         if(engineOutputDialog && EngineOutputIsUp()) {\r
839             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
840             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
841         }\r
842         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
843             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
844         }\r
845         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
846         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
847         if(msg.hwnd == e1)                 currentElement = 2; else\r
848         if(msg.hwnd == e2)                 currentElement = 3; else\r
849         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
850         if(msg.hwnd == mh)                currentElement = 4; else\r
851         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
852         if(msg.hwnd == hText)  currentElement = 5; else\r
853         if(msg.hwnd == hInput) currentElement = 6; else\r
854         for (i = 0; i < N_BUTTONS; i++) {\r
855             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
856         }\r
857 \r
858         // determine where to go to\r
859         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
860           do {\r
861             currentElement = (currentElement + direction) % 7;\r
862             switch(currentElement) {\r
863                 case 0:\r
864                   h = hwndMain; break; // passing this case always makes the loop exit\r
865                 case 1:\r
866                   h = buttonDesc[0].hwnd; break; // could be NULL\r
867                 case 2:\r
868                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
869                   h = e1; break;\r
870                 case 3:\r
871                   if(!EngineOutputIsUp()) continue;\r
872                   h = e2; break;\r
873                 case 4:\r
874                   if(!MoveHistoryIsUp()) continue;\r
875                   h = mh; break;\r
876 //              case 6: // input to eval graph does not seem to get here!\r
877 //                if(!EvalGraphIsUp()) continue;\r
878 //                h = evalGraphDialog; break;\r
879                 case 5:\r
880                   if(!appData.icsActive) continue;\r
881                   SAY("display");\r
882                   h = hText; break;\r
883                 case 6:\r
884                   if(!appData.icsActive) continue;\r
885                   SAY("input");\r
886                   h = hInput; break;\r
887             }\r
888           } while(h == 0);\r
889 \r
890           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
891           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
892           SetFocus(h);\r
893 \r
894           continue; // this message now has been processed\r
895         }\r
896       }\r
897 \r
898       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
899           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
900           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
901           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
902           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
903           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
904           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
905           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
906           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
907           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
908         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
909         for(i=0; i<MAX_CHAT; i++) \r
910             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
911                 done = 1; break;\r
912         }\r
913         if(done) continue; // [HGM] chat: end patch\r
914         TranslateMessage(&msg); /* Translates virtual key codes */\r
915         DispatchMessage(&msg);  /* Dispatches message to window */\r
916       }\r
917     }\r
918 \r
919 \r
920   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
921 }\r
922 \r
923 /*---------------------------------------------------------------------------*\\r
924  *\r
925  * Initialization functions\r
926  *\r
927 \*---------------------------------------------------------------------------*/\r
928 \r
929 void\r
930 SetUserLogo()\r
931 {   // update user logo if necessary\r
932     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
933 \r
934     if(appData.autoLogo) {\r
935           curName = UserName();\r
936           if(strcmp(curName, oldUserName)) {\r
937                 GetCurrentDirectory(MSG_SIZ, dir);\r
938                 SetCurrentDirectory(installDir);\r
939                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
940                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
941                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
942                 if(userLogo == NULL)\r
943                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
944                 SetCurrentDirectory(dir); /* return to prev directory */\r
945           }\r
946     }\r
947 }\r
948 \r
949 BOOL\r
950 InitApplication(HINSTANCE hInstance)\r
951 {\r
952   WNDCLASS wc;\r
953 \r
954   /* Fill in window class structure with parameters that describe the */\r
955   /* main window. */\r
956 \r
957   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
958   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
959   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
960   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
961   wc.hInstance     = hInstance;         /* Owner of this class */\r
962   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
963   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
964   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
965   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
966   wc.lpszClassName = szAppName;                 /* Name to register as */\r
967 \r
968   /* Register the window class and return success/failure code. */\r
969   if (!RegisterClass(&wc)) return FALSE;\r
970 \r
971   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
972   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
973   wc.cbClsExtra    = 0;\r
974   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
975   wc.hInstance     = hInstance;\r
976   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
977   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
978   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
979   wc.lpszMenuName  = NULL;\r
980   wc.lpszClassName = szConsoleName;\r
981 \r
982   if (!RegisterClass(&wc)) return FALSE;\r
983   return TRUE;\r
984 }\r
985 \r
986 \r
987 /* Set by InitInstance, used by EnsureOnScreen */\r
988 int screenHeight, screenWidth;\r
989 RECT screenGeometry;\r
990 \r
991 void\r
992 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
993 {\r
994 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
995   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
996   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
997   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
998   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
999   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1000 }\r
1001 \r
1002 VOID\r
1003 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1004 {\r
1005   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1006   GetCurrentDirectory(MSG_SIZ, dir);\r
1007   SetCurrentDirectory(installDir);\r
1008   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1009       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1010 \r
1011       if (cps->programLogo == NULL && appData.debugMode) {\r
1012           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1013       }\r
1014   } else if(appData.autoLogo) {\r
1015       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1016         char *opponent = "";\r
1017         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1018         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1019         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1020         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1021             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1022             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1023         }\r
1024       } else\r
1025       if(appData.directory[n] && appData.directory[n][0]) {\r
1026         SetCurrentDirectory(appData.directory[n]);\r
1027         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1028       }\r
1029   }\r
1030   SetCurrentDirectory(dir); /* return to prev directory */\r
1031 }\r
1032 \r
1033 VOID\r
1034 InitTextures()\r
1035 {\r
1036   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1037   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1038   \r
1039   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1040       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1041       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1042       liteBackTextureMode = appData.liteBackTextureMode;\r
1043 \r
1044       if (liteBackTexture == NULL && appData.debugMode) {\r
1045           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1046       }\r
1047   }\r
1048   \r
1049   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1050       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1051       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1052       darkBackTextureMode = appData.darkBackTextureMode;\r
1053 \r
1054       if (darkBackTexture == NULL && appData.debugMode) {\r
1055           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1056       }\r
1057   }\r
1058 }\r
1059 \r
1060 #ifndef SM_CXVIRTUALSCREEN\r
1061 #define SM_CXVIRTUALSCREEN 78\r
1062 #endif\r
1063 #ifndef SM_CYVIRTUALSCREEN\r
1064 #define SM_CYVIRTUALSCREEN 79\r
1065 #endif\r
1066 #ifndef SM_XVIRTUALSCREEN \r
1067 #define SM_XVIRTUALSCREEN 76\r
1068 #endif\r
1069 #ifndef SM_YVIRTUALSCREEN \r
1070 #define SM_YVIRTUALSCREEN 77\r
1071 #endif\r
1072 \r
1073 VOID\r
1074 InitGeometry()\r
1075 {\r
1076   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1077   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1078   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1079   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1080   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1081   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1082   screenGeometry.right = screenGeometry.left + screenWidth;\r
1083   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1084 }\r
1085 \r
1086 BOOL\r
1087 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1088 {\r
1089   HWND hwnd; /* Main window handle. */\r
1090   int ibs;\r
1091   WINDOWPLACEMENT wp;\r
1092   char *filepart;\r
1093 \r
1094   hInst = hInstance;    /* Store instance handle in our global variable */\r
1095   programName = szAppName;\r
1096 \r
1097   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1098     *filepart = NULLCHAR;\r
1099     SetCurrentDirectory(installDir);\r
1100   } else {\r
1101     GetCurrentDirectory(MSG_SIZ, installDir);\r
1102   }\r
1103   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1104   InitGeometry();\r
1105   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1106   /* xboard, and older WinBoards, controlled the move sound with the\r
1107      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1108      always turn the option on (so that the backend will call us),\r
1109      then let the user turn the sound off by setting it to silence if\r
1110      desired.  To accommodate old winboard.ini files saved by old\r
1111      versions of WinBoard, we also turn off the sound if the option\r
1112      was initially set to false. [HGM] taken out of InitAppData */\r
1113   if (!appData.ringBellAfterMoves) {\r
1114     sounds[(int)SoundMove].name = strdup("");\r
1115     appData.ringBellAfterMoves = TRUE;\r
1116   }\r
1117   if (appData.debugMode) {\r
1118     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1119     setbuf(debugFP, NULL);\r
1120   }\r
1121 \r
1122   LoadLanguageFile(appData.language);\r
1123 \r
1124   InitBackEnd1();\r
1125 \r
1126 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1127 //  InitEngineUCI( installDir, &second );\r
1128 \r
1129   /* Create a main window for this application instance. */\r
1130   hwnd = CreateWindow(szAppName, szTitle,\r
1131                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1132                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1133                       NULL, NULL, hInstance, NULL);\r
1134   hwndMain = hwnd;\r
1135 \r
1136   /* If window could not be created, return "failure" */\r
1137   if (!hwnd) {\r
1138     return (FALSE);\r
1139   }\r
1140 \r
1141   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1142   LoadLogo(&first, 0, FALSE);\r
1143   LoadLogo(&second, 1, appData.icsActive);\r
1144 \r
1145   SetUserLogo();\r
1146 \r
1147   iconWhite = LoadIcon(hInstance, "icon_white");\r
1148   iconBlack = LoadIcon(hInstance, "icon_black");\r
1149   iconCurrent = iconWhite;\r
1150   InitDrawingColors();\r
1151 \r
1152   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1153   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1154     /* Compute window size for each board size, and use the largest\r
1155        size that fits on this screen as the default. */\r
1156     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1157     if (boardSize == (BoardSize)-1 &&\r
1158         winH <= screenHeight\r
1159            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1160         && winW <= screenWidth) {\r
1161       boardSize = (BoardSize)ibs;\r
1162     }\r
1163   }\r
1164 \r
1165   InitDrawingSizes(boardSize, 0);\r
1166   RecentEngineMenu(appData.recentEngineList);\r
1167   InitMenuChecks();\r
1168   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1169 \r
1170   /* [AS] Load textures if specified */\r
1171   InitTextures();\r
1172 \r
1173   mysrandom( (unsigned) time(NULL) );\r
1174 \r
1175   /* [AS] Restore layout */\r
1176   if( wpMoveHistory.visible ) {\r
1177       MoveHistoryPopUp();\r
1178   }\r
1179 \r
1180   if( wpEvalGraph.visible ) {\r
1181       EvalGraphPopUp();\r
1182   }\r
1183 \r
1184   if( wpEngineOutput.visible ) {\r
1185       EngineOutputPopUp();\r
1186   }\r
1187 \r
1188   /* Make the window visible; update its client area; and return "success" */\r
1189   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1190   wp.length = sizeof(WINDOWPLACEMENT);\r
1191   wp.flags = 0;\r
1192   wp.showCmd = nCmdShow;\r
1193   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1194   wp.rcNormalPosition.left = wpMain.x;\r
1195   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1196   wp.rcNormalPosition.top = wpMain.y;\r
1197   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1198   SetWindowPlacement(hwndMain, &wp);\r
1199 \r
1200   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1201 \r
1202   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1203                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1204 \r
1205   if (hwndConsole) {\r
1206 #if AOT_CONSOLE\r
1207     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1208                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1209 #endif\r
1210     ShowWindow(hwndConsole, nCmdShow);\r
1211     SetActiveWindow(hwndConsole);\r
1212   }\r
1213   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1214   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1215 \r
1216   return TRUE;\r
1217 \r
1218 }\r
1219 \r
1220 VOID\r
1221 InitMenuChecks()\r
1222 {\r
1223   HMENU hmenu = GetMenu(hwndMain);\r
1224 \r
1225   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1226                         MF_BYCOMMAND|((appData.icsActive &&\r
1227                                        *appData.icsCommPort != NULLCHAR) ?\r
1228                                       MF_ENABLED : MF_GRAYED));\r
1229   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1230                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1231                                      MF_CHECKED : MF_UNCHECKED));\r
1232 }\r
1233 \r
1234 //---------------------------------------------------------------------------------------------------------\r
1235 \r
1236 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1237 #define XBOARD FALSE\r
1238 \r
1239 #define OPTCHAR "/"\r
1240 #define SEPCHAR "="\r
1241 #define TOPLEVEL 0\r
1242 \r
1243 #include "args.h"\r
1244 \r
1245 // front-end part of option handling\r
1246 \r
1247 VOID\r
1248 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1249 {\r
1250   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1251   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1252   DeleteDC(hdc);\r
1253   lf->lfWidth = 0;\r
1254   lf->lfEscapement = 0;\r
1255   lf->lfOrientation = 0;\r
1256   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1257   lf->lfItalic = mfp->italic;\r
1258   lf->lfUnderline = mfp->underline;\r
1259   lf->lfStrikeOut = mfp->strikeout;\r
1260   lf->lfCharSet = mfp->charset;\r
1261   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1262   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1263   lf->lfQuality = DEFAULT_QUALITY;\r
1264   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1265     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1266 }\r
1267 \r
1268 void\r
1269 CreateFontInMF(MyFont *mf)\r
1270\r
1271   LFfromMFP(&mf->lf, &mf->mfp);\r
1272   if (mf->hf) DeleteObject(mf->hf);\r
1273   mf->hf = CreateFontIndirect(&mf->lf);\r
1274 }\r
1275 \r
1276 // [HGM] This platform-dependent table provides the location for storing the color info\r
1277 void *\r
1278 colorVariable[] = {\r
1279   &whitePieceColor, \r
1280   &blackPieceColor, \r
1281   &lightSquareColor,\r
1282   &darkSquareColor, \r
1283   &highlightSquareColor,\r
1284   &premoveHighlightColor,\r
1285   NULL,\r
1286   &consoleBackgroundColor,\r
1287   &appData.fontForeColorWhite,\r
1288   &appData.fontBackColorWhite,\r
1289   &appData.fontForeColorBlack,\r
1290   &appData.fontBackColorBlack,\r
1291   &appData.evalHistColorWhite,\r
1292   &appData.evalHistColorBlack,\r
1293   &appData.highlightArrowColor,\r
1294 };\r
1295 \r
1296 /* Command line font name parser.  NULL name means do nothing.\r
1297    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1298    For backward compatibility, syntax without the colon is also\r
1299    accepted, but font names with digits in them won't work in that case.\r
1300 */\r
1301 VOID\r
1302 ParseFontName(char *name, MyFontParams *mfp)\r
1303 {\r
1304   char *p, *q;\r
1305   if (name == NULL) return;\r
1306   p = name;\r
1307   q = strchr(p, ':');\r
1308   if (q) {\r
1309     if (q - p >= sizeof(mfp->faceName))\r
1310       ExitArgError(_("Font name too long:"), name, TRUE);\r
1311     memcpy(mfp->faceName, p, q - p);\r
1312     mfp->faceName[q - p] = NULLCHAR;\r
1313     p = q + 1;\r
1314   } else {\r
1315     q = mfp->faceName;\r
1316 \r
1317     while (*p && !isdigit(*p)) {\r
1318       *q++ = *p++;\r
1319       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1320         ExitArgError(_("Font name too long:"), name, TRUE);\r
1321     }\r
1322     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1323     *q = NULLCHAR;\r
1324   }\r
1325   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1326   mfp->pointSize = (float) atof(p);\r
1327   mfp->bold = (strchr(p, 'b') != NULL);\r
1328   mfp->italic = (strchr(p, 'i') != NULL);\r
1329   mfp->underline = (strchr(p, 'u') != NULL);\r
1330   mfp->strikeout = (strchr(p, 's') != NULL);\r
1331   mfp->charset = DEFAULT_CHARSET;\r
1332   q = strchr(p, 'c');\r
1333   if (q)\r
1334     mfp->charset = (BYTE) atoi(q+1);\r
1335 }\r
1336 \r
1337 void\r
1338 ParseFont(char *name, int number)\r
1339 { // wrapper to shield back-end from 'font'\r
1340   ParseFontName(name, &font[boardSize][number]->mfp);\r
1341 }\r
1342 \r
1343 void\r
1344 SetFontDefaults()\r
1345 { // in WB  we have a 2D array of fonts; this initializes their description\r
1346   int i, j;\r
1347   /* Point font array elements to structures and\r
1348      parse default font names */\r
1349   for (i=0; i<NUM_FONTS; i++) {\r
1350     for (j=0; j<NUM_SIZES; j++) {\r
1351       font[j][i] = &fontRec[j][i];\r
1352       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1353     }\r
1354   }\r
1355 }\r
1356 \r
1357 void\r
1358 CreateFonts()\r
1359 { // here we create the actual fonts from the selected descriptions\r
1360   int i, j;\r
1361   for (i=0; i<NUM_FONTS; i++) {\r
1362     for (j=0; j<NUM_SIZES; j++) {\r
1363       CreateFontInMF(font[j][i]);\r
1364     }\r
1365   }\r
1366 }\r
1367 /* Color name parser.\r
1368    X version accepts X color names, but this one\r
1369    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1370 COLORREF\r
1371 ParseColorName(char *name)\r
1372 {\r
1373   int red, green, blue, count;\r
1374   char buf[MSG_SIZ];\r
1375 \r
1376   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1377   if (count != 3) {\r
1378     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1379       &red, &green, &blue);\r
1380   }\r
1381   if (count != 3) {\r
1382     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1383     DisplayError(buf, 0);\r
1384     return RGB(0, 0, 0);\r
1385   }\r
1386   return PALETTERGB(red, green, blue);\r
1387 }\r
1388 \r
1389 void\r
1390 ParseColor(int n, char *name)\r
1391 { // for WinBoard the color is an int, which needs to be derived from the string\r
1392   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1393 }\r
1394 \r
1395 void\r
1396 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1397 {\r
1398   char *e = argValue;\r
1399   int eff = 0;\r
1400 \r
1401   while (*e) {\r
1402     if (*e == 'b')      eff |= CFE_BOLD;\r
1403     else if (*e == 'i') eff |= CFE_ITALIC;\r
1404     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1405     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1406     else if (*e == '#' || isdigit(*e)) break;\r
1407     e++;\r
1408   }\r
1409   *effects = eff;\r
1410   *color   = ParseColorName(e);\r
1411 }\r
1412 \r
1413 void\r
1414 ParseTextAttribs(ColorClass cc, char *s)\r
1415 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1416     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1417     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1418 }\r
1419 \r
1420 void\r
1421 ParseBoardSize(void *addr, char *name)\r
1422 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1423   BoardSize bs = SizeTiny;\r
1424   while (sizeInfo[bs].name != NULL) {\r
1425     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1426         *(BoardSize *)addr = bs;\r
1427         return;\r
1428     }\r
1429     bs++;\r
1430   }\r
1431   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1432 }\r
1433 \r
1434 void\r
1435 LoadAllSounds()\r
1436 { // [HGM] import name from appData first\r
1437   ColorClass cc;\r
1438   SoundClass sc;\r
1439   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1440     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1441     textAttribs[cc].sound.data = NULL;\r
1442     MyLoadSound(&textAttribs[cc].sound);\r
1443   }\r
1444   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1445     textAttribs[cc].sound.name = strdup("");\r
1446     textAttribs[cc].sound.data = NULL;\r
1447   }\r
1448   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1449     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1450     sounds[sc].data = NULL;\r
1451     MyLoadSound(&sounds[sc]);\r
1452   }\r
1453 }\r
1454 \r
1455 void\r
1456 SetCommPortDefaults()\r
1457 {\r
1458    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1459   dcb.DCBlength = sizeof(DCB);\r
1460   dcb.BaudRate = 9600;\r
1461   dcb.fBinary = TRUE;\r
1462   dcb.fParity = FALSE;\r
1463   dcb.fOutxCtsFlow = FALSE;\r
1464   dcb.fOutxDsrFlow = FALSE;\r
1465   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1466   dcb.fDsrSensitivity = FALSE;\r
1467   dcb.fTXContinueOnXoff = TRUE;\r
1468   dcb.fOutX = FALSE;\r
1469   dcb.fInX = FALSE;\r
1470   dcb.fNull = FALSE;\r
1471   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1472   dcb.fAbortOnError = FALSE;\r
1473   dcb.ByteSize = 7;\r
1474   dcb.Parity = SPACEPARITY;\r
1475   dcb.StopBits = ONESTOPBIT;\r
1476 }\r
1477 \r
1478 // [HGM] args: these three cases taken out to stay in front-end\r
1479 void\r
1480 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1481 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1482         // while the curent board size determines the element. This system should be ported to XBoard.\r
1483         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1484         int bs;\r
1485         for (bs=0; bs<NUM_SIZES; bs++) {\r
1486           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1487           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1488           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1489             ad->argName, mfp->faceName, mfp->pointSize,\r
1490             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1491             mfp->bold ? "b" : "",\r
1492             mfp->italic ? "i" : "",\r
1493             mfp->underline ? "u" : "",\r
1494             mfp->strikeout ? "s" : "",\r
1495             (int)mfp->charset);\r
1496         }\r
1497       }\r
1498 \r
1499 void\r
1500 ExportSounds()\r
1501 { // [HGM] copy the names from the internal WB variables to appData\r
1502   ColorClass cc;\r
1503   SoundClass sc;\r
1504   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1505     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1506   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1507     (&appData.soundMove)[sc] = sounds[sc].name;\r
1508 }\r
1509 \r
1510 void\r
1511 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1512 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1513         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1514         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1515           (ta->effects & CFE_BOLD) ? "b" : "",\r
1516           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1517           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1518           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1519           (ta->effects) ? " " : "",\r
1520           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1521       }\r
1522 \r
1523 void\r
1524 SaveColor(FILE *f, ArgDescriptor *ad)\r
1525 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1526         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1527         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1528           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1529 }\r
1530 \r
1531 void\r
1532 SaveBoardSize(FILE *f, char *name, void *addr)\r
1533 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1534   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1535 }\r
1536 \r
1537 void\r
1538 ParseCommPortSettings(char *s)\r
1539 { // wrapper to keep dcb from back-end\r
1540   ParseCommSettings(s, &dcb);\r
1541 }\r
1542 \r
1543 void\r
1544 GetWindowCoords()\r
1545 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1546   GetActualPlacement(hwndMain, &wpMain);\r
1547   GetActualPlacement(hwndConsole, &wpConsole);\r
1548   GetActualPlacement(commentDialog, &wpComment);\r
1549   GetActualPlacement(editTagsDialog, &wpTags);\r
1550   GetActualPlacement(gameListDialog, &wpGameList);\r
1551   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1552   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1553   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1554 }\r
1555 \r
1556 void\r
1557 PrintCommPortSettings(FILE *f, char *name)\r
1558 { // wrapper to shield back-end from DCB\r
1559       PrintCommSettings(f, name, &dcb);\r
1560 }\r
1561 \r
1562 int\r
1563 MySearchPath(char *installDir, char *name, char *fullname)\r
1564 {\r
1565   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1566   if(name[0]== '%') {\r
1567     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1568     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1569       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1570       *strchr(buf, '%') = 0;\r
1571       strcat(fullname, getenv(buf));\r
1572       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1573     }\r
1574     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1575     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1576     return (int) strlen(fullname);\r
1577   }\r
1578   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1579 }\r
1580 \r
1581 int\r
1582 MyGetFullPathName(char *name, char *fullname)\r
1583 {\r
1584   char *dummy;\r
1585   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1586 }\r
1587 \r
1588 int\r
1589 MainWindowUp()\r
1590 { // [HGM] args: allows testing if main window is realized from back-end\r
1591   return hwndMain != NULL;\r
1592 }\r
1593 \r
1594 void\r
1595 PopUpStartupDialog()\r
1596 {\r
1597     FARPROC lpProc;\r
1598     \r
1599     LoadLanguageFile(appData.language);\r
1600     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1601     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1602     FreeProcInstance(lpProc);\r
1603 }\r
1604 \r
1605 /*---------------------------------------------------------------------------*\\r
1606  *\r
1607  * GDI board drawing routines\r
1608  *\r
1609 \*---------------------------------------------------------------------------*/\r
1610 \r
1611 /* [AS] Draw square using background texture */\r
1612 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1613 {\r
1614     XFORM   x;\r
1615 \r
1616     if( mode == 0 ) {\r
1617         return; /* Should never happen! */\r
1618     }\r
1619 \r
1620     SetGraphicsMode( dst, GM_ADVANCED );\r
1621 \r
1622     switch( mode ) {\r
1623     case 1:\r
1624         /* Identity */\r
1625         break;\r
1626     case 2:\r
1627         /* X reflection */\r
1628         x.eM11 = -1.0;\r
1629         x.eM12 = 0;\r
1630         x.eM21 = 0;\r
1631         x.eM22 = 1.0;\r
1632         x.eDx = (FLOAT) dw + dx - 1;\r
1633         x.eDy = 0;\r
1634         dx = 0;\r
1635         SetWorldTransform( dst, &x );\r
1636         break;\r
1637     case 3:\r
1638         /* Y reflection */\r
1639         x.eM11 = 1.0;\r
1640         x.eM12 = 0;\r
1641         x.eM21 = 0;\r
1642         x.eM22 = -1.0;\r
1643         x.eDx = 0;\r
1644         x.eDy = (FLOAT) dh + dy - 1;\r
1645         dy = 0;\r
1646         SetWorldTransform( dst, &x );\r
1647         break;\r
1648     case 4:\r
1649         /* X/Y flip */\r
1650         x.eM11 = 0;\r
1651         x.eM12 = 1.0;\r
1652         x.eM21 = 1.0;\r
1653         x.eM22 = 0;\r
1654         x.eDx = (FLOAT) dx;\r
1655         x.eDy = (FLOAT) dy;\r
1656         dx = 0;\r
1657         dy = 0;\r
1658         SetWorldTransform( dst, &x );\r
1659         break;\r
1660     }\r
1661 \r
1662     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1663 \r
1664     x.eM11 = 1.0;\r
1665     x.eM12 = 0;\r
1666     x.eM21 = 0;\r
1667     x.eM22 = 1.0;\r
1668     x.eDx = 0;\r
1669     x.eDy = 0;\r
1670     SetWorldTransform( dst, &x );\r
1671 \r
1672     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1673 }\r
1674 \r
1675 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1676 enum {\r
1677     PM_WP = (int) WhitePawn, \r
1678     PM_WN = (int) WhiteKnight, \r
1679     PM_WB = (int) WhiteBishop, \r
1680     PM_WR = (int) WhiteRook, \r
1681     PM_WQ = (int) WhiteQueen, \r
1682     PM_WF = (int) WhiteFerz, \r
1683     PM_WW = (int) WhiteWazir, \r
1684     PM_WE = (int) WhiteAlfil, \r
1685     PM_WM = (int) WhiteMan, \r
1686     PM_WO = (int) WhiteCannon, \r
1687     PM_WU = (int) WhiteUnicorn, \r
1688     PM_WH = (int) WhiteNightrider, \r
1689     PM_WA = (int) WhiteAngel, \r
1690     PM_WC = (int) WhiteMarshall, \r
1691     PM_WAB = (int) WhiteCardinal, \r
1692     PM_WD = (int) WhiteDragon, \r
1693     PM_WL = (int) WhiteLance, \r
1694     PM_WS = (int) WhiteCobra, \r
1695     PM_WV = (int) WhiteFalcon, \r
1696     PM_WSG = (int) WhiteSilver, \r
1697     PM_WG = (int) WhiteGrasshopper, \r
1698     PM_WK = (int) WhiteKing,\r
1699     PM_BP = (int) BlackPawn, \r
1700     PM_BN = (int) BlackKnight, \r
1701     PM_BB = (int) BlackBishop, \r
1702     PM_BR = (int) BlackRook, \r
1703     PM_BQ = (int) BlackQueen, \r
1704     PM_BF = (int) BlackFerz, \r
1705     PM_BW = (int) BlackWazir, \r
1706     PM_BE = (int) BlackAlfil, \r
1707     PM_BM = (int) BlackMan,\r
1708     PM_BO = (int) BlackCannon, \r
1709     PM_BU = (int) BlackUnicorn, \r
1710     PM_BH = (int) BlackNightrider, \r
1711     PM_BA = (int) BlackAngel, \r
1712     PM_BC = (int) BlackMarshall, \r
1713     PM_BG = (int) BlackGrasshopper, \r
1714     PM_BAB = (int) BlackCardinal,\r
1715     PM_BD = (int) BlackDragon,\r
1716     PM_BL = (int) BlackLance,\r
1717     PM_BS = (int) BlackCobra,\r
1718     PM_BV = (int) BlackFalcon,\r
1719     PM_BSG = (int) BlackSilver,\r
1720     PM_BK = (int) BlackKing\r
1721 };\r
1722 \r
1723 static HFONT hPieceFont = NULL;\r
1724 static HBITMAP hPieceMask[(int) EmptySquare];\r
1725 static HBITMAP hPieceFace[(int) EmptySquare];\r
1726 static int fontBitmapSquareSize = 0;\r
1727 static char pieceToFontChar[(int) EmptySquare] =\r
1728                               { 'p', 'n', 'b', 'r', 'q', \r
1729                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1730                       'k', 'o', 'm', 'v', 't', 'w', \r
1731                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1732                                                               'l' };\r
1733 \r
1734 extern BOOL SetCharTable( char *table, const char * map );\r
1735 /* [HGM] moved to backend.c */\r
1736 \r
1737 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1738 {\r
1739     HBRUSH hbrush;\r
1740     BYTE r1 = GetRValue( color );\r
1741     BYTE g1 = GetGValue( color );\r
1742     BYTE b1 = GetBValue( color );\r
1743     BYTE r2 = r1 / 2;\r
1744     BYTE g2 = g1 / 2;\r
1745     BYTE b2 = b1 / 2;\r
1746     RECT rc;\r
1747 \r
1748     /* Create a uniform background first */\r
1749     hbrush = CreateSolidBrush( color );\r
1750     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1751     FillRect( hdc, &rc, hbrush );\r
1752     DeleteObject( hbrush );\r
1753     \r
1754     if( mode == 1 ) {\r
1755         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1756         int steps = squareSize / 2;\r
1757         int i;\r
1758 \r
1759         for( i=0; i<steps; i++ ) {\r
1760             BYTE r = r1 - (r1-r2) * i / steps;\r
1761             BYTE g = g1 - (g1-g2) * i / steps;\r
1762             BYTE b = b1 - (b1-b2) * i / steps;\r
1763 \r
1764             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1765             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1766             FillRect( hdc, &rc, hbrush );\r
1767             DeleteObject(hbrush);\r
1768         }\r
1769     }\r
1770     else if( mode == 2 ) {\r
1771         /* Diagonal gradient, good more or less for every piece */\r
1772         POINT triangle[3];\r
1773         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1774         HBRUSH hbrush_old;\r
1775         int steps = squareSize;\r
1776         int i;\r
1777 \r
1778         triangle[0].x = squareSize - steps;\r
1779         triangle[0].y = squareSize;\r
1780         triangle[1].x = squareSize;\r
1781         triangle[1].y = squareSize;\r
1782         triangle[2].x = squareSize;\r
1783         triangle[2].y = squareSize - steps;\r
1784 \r
1785         for( i=0; i<steps; i++ ) {\r
1786             BYTE r = r1 - (r1-r2) * i / steps;\r
1787             BYTE g = g1 - (g1-g2) * i / steps;\r
1788             BYTE b = b1 - (b1-b2) * i / steps;\r
1789 \r
1790             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1791             hbrush_old = SelectObject( hdc, hbrush );\r
1792             Polygon( hdc, triangle, 3 );\r
1793             SelectObject( hdc, hbrush_old );\r
1794             DeleteObject(hbrush);\r
1795             triangle[0].x++;\r
1796             triangle[2].y++;\r
1797         }\r
1798 \r
1799         SelectObject( hdc, hpen );\r
1800     }\r
1801 }\r
1802 \r
1803 /*\r
1804     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1805     seems to work ok. The main problem here is to find the "inside" of a chess\r
1806     piece: follow the steps as explained below.\r
1807 */\r
1808 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1809 {\r
1810     HBITMAP hbm;\r
1811     HBITMAP hbm_old;\r
1812     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1813     RECT rc;\r
1814     SIZE sz;\r
1815     POINT pt;\r
1816     int backColor = whitePieceColor; \r
1817     int foreColor = blackPieceColor;\r
1818     \r
1819     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1820         backColor = appData.fontBackColorWhite;\r
1821         foreColor = appData.fontForeColorWhite;\r
1822     }\r
1823     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1824         backColor = appData.fontBackColorBlack;\r
1825         foreColor = appData.fontForeColorBlack;\r
1826     }\r
1827 \r
1828     /* Mask */\r
1829     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1830 \r
1831     hbm_old = SelectObject( hdc, hbm );\r
1832 \r
1833     rc.left = 0;\r
1834     rc.top = 0;\r
1835     rc.right = squareSize;\r
1836     rc.bottom = squareSize;\r
1837 \r
1838     /* Step 1: background is now black */\r
1839     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1840 \r
1841     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1842 \r
1843     pt.x = (squareSize - sz.cx) / 2;\r
1844     pt.y = (squareSize - sz.cy) / 2;\r
1845 \r
1846     SetBkMode( hdc, TRANSPARENT );\r
1847     SetTextColor( hdc, chroma );\r
1848     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1849     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1850 \r
1851     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1852     /* Step 3: the area outside the piece is filled with white */\r
1853 //    FloodFill( hdc, 0, 0, chroma );\r
1854     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1855     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1856     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1857     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1858     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1859     /* \r
1860         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1861         but if the start point is not inside the piece we're lost!\r
1862         There should be a better way to do this... if we could create a region or path\r
1863         from the fill operation we would be fine for example.\r
1864     */\r
1865 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1866     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1867 \r
1868     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1869         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1870         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1871 \r
1872         SelectObject( dc2, bm2 );\r
1873         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1874         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1875         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1876         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1877         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1878 \r
1879         DeleteDC( dc2 );\r
1880         DeleteObject( bm2 );\r
1881     }\r
1882 \r
1883     SetTextColor( hdc, 0 );\r
1884     /* \r
1885         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1886         draw the piece again in black for safety.\r
1887     */\r
1888     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1889 \r
1890     SelectObject( hdc, hbm_old );\r
1891 \r
1892     if( hPieceMask[index] != NULL ) {\r
1893         DeleteObject( hPieceMask[index] );\r
1894     }\r
1895 \r
1896     hPieceMask[index] = hbm;\r
1897 \r
1898     /* Face */\r
1899     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1900 \r
1901     SelectObject( hdc, hbm );\r
1902 \r
1903     {\r
1904         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1905         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1906         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1907 \r
1908         SelectObject( dc1, hPieceMask[index] );\r
1909         SelectObject( dc2, bm2 );\r
1910         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1911         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1912         \r
1913         /* \r
1914             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1915             the piece background and deletes (makes transparent) the rest.\r
1916             Thanks to that mask, we are free to paint the background with the greates\r
1917             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1918             We use this, to make gradients and give the pieces a "roundish" look.\r
1919         */\r
1920         SetPieceBackground( hdc, backColor, 2 );\r
1921         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1922 \r
1923         DeleteDC( dc2 );\r
1924         DeleteDC( dc1 );\r
1925         DeleteObject( bm2 );\r
1926     }\r
1927 \r
1928     SetTextColor( hdc, foreColor );\r
1929     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1930 \r
1931     SelectObject( hdc, hbm_old );\r
1932 \r
1933     if( hPieceFace[index] != NULL ) {\r
1934         DeleteObject( hPieceFace[index] );\r
1935     }\r
1936 \r
1937     hPieceFace[index] = hbm;\r
1938 }\r
1939 \r
1940 static int TranslatePieceToFontPiece( int piece )\r
1941 {\r
1942     switch( piece ) {\r
1943     case BlackPawn:\r
1944         return PM_BP;\r
1945     case BlackKnight:\r
1946         return PM_BN;\r
1947     case BlackBishop:\r
1948         return PM_BB;\r
1949     case BlackRook:\r
1950         return PM_BR;\r
1951     case BlackQueen:\r
1952         return PM_BQ;\r
1953     case BlackKing:\r
1954         return PM_BK;\r
1955     case WhitePawn:\r
1956         return PM_WP;\r
1957     case WhiteKnight:\r
1958         return PM_WN;\r
1959     case WhiteBishop:\r
1960         return PM_WB;\r
1961     case WhiteRook:\r
1962         return PM_WR;\r
1963     case WhiteQueen:\r
1964         return PM_WQ;\r
1965     case WhiteKing:\r
1966         return PM_WK;\r
1967 \r
1968     case BlackAngel:\r
1969         return PM_BA;\r
1970     case BlackMarshall:\r
1971         return PM_BC;\r
1972     case BlackFerz:\r
1973         return PM_BF;\r
1974     case BlackNightrider:\r
1975         return PM_BH;\r
1976     case BlackAlfil:\r
1977         return PM_BE;\r
1978     case BlackWazir:\r
1979         return PM_BW;\r
1980     case BlackUnicorn:\r
1981         return PM_BU;\r
1982     case BlackCannon:\r
1983         return PM_BO;\r
1984     case BlackGrasshopper:\r
1985         return PM_BG;\r
1986     case BlackMan:\r
1987         return PM_BM;\r
1988     case BlackSilver:\r
1989         return PM_BSG;\r
1990     case BlackLance:\r
1991         return PM_BL;\r
1992     case BlackFalcon:\r
1993         return PM_BV;\r
1994     case BlackCobra:\r
1995         return PM_BS;\r
1996     case BlackCardinal:\r
1997         return PM_BAB;\r
1998     case BlackDragon:\r
1999         return PM_BD;\r
2000 \r
2001     case WhiteAngel:\r
2002         return PM_WA;\r
2003     case WhiteMarshall:\r
2004         return PM_WC;\r
2005     case WhiteFerz:\r
2006         return PM_WF;\r
2007     case WhiteNightrider:\r
2008         return PM_WH;\r
2009     case WhiteAlfil:\r
2010         return PM_WE;\r
2011     case WhiteWazir:\r
2012         return PM_WW;\r
2013     case WhiteUnicorn:\r
2014         return PM_WU;\r
2015     case WhiteCannon:\r
2016         return PM_WO;\r
2017     case WhiteGrasshopper:\r
2018         return PM_WG;\r
2019     case WhiteMan:\r
2020         return PM_WM;\r
2021     case WhiteSilver:\r
2022         return PM_WSG;\r
2023     case WhiteLance:\r
2024         return PM_WL;\r
2025     case WhiteFalcon:\r
2026         return PM_WV;\r
2027     case WhiteCobra:\r
2028         return PM_WS;\r
2029     case WhiteCardinal:\r
2030         return PM_WAB;\r
2031     case WhiteDragon:\r
2032         return PM_WD;\r
2033     }\r
2034 \r
2035     return 0;\r
2036 }\r
2037 \r
2038 void CreatePiecesFromFont()\r
2039 {\r
2040     LOGFONT lf;\r
2041     HDC hdc_window = NULL;\r
2042     HDC hdc = NULL;\r
2043     HFONT hfont_old;\r
2044     int fontHeight;\r
2045     int i;\r
2046 \r
2047     if( fontBitmapSquareSize < 0 ) {\r
2048         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2049         return;\r
2050     }\r
2051 \r
2052     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2053             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2054         fontBitmapSquareSize = -1;\r
2055         return;\r
2056     }\r
2057 \r
2058     if( fontBitmapSquareSize != squareSize ) {\r
2059         hdc_window = GetDC( hwndMain );\r
2060         hdc = CreateCompatibleDC( hdc_window );\r
2061 \r
2062         if( hPieceFont != NULL ) {\r
2063             DeleteObject( hPieceFont );\r
2064         }\r
2065         else {\r
2066             for( i=0; i<=(int)BlackKing; i++ ) {\r
2067                 hPieceMask[i] = NULL;\r
2068                 hPieceFace[i] = NULL;\r
2069             }\r
2070         }\r
2071 \r
2072         fontHeight = 75;\r
2073 \r
2074         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2075             fontHeight = appData.fontPieceSize;\r
2076         }\r
2077 \r
2078         fontHeight = (fontHeight * squareSize) / 100;\r
2079 \r
2080         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2081         lf.lfWidth = 0;\r
2082         lf.lfEscapement = 0;\r
2083         lf.lfOrientation = 0;\r
2084         lf.lfWeight = FW_NORMAL;\r
2085         lf.lfItalic = 0;\r
2086         lf.lfUnderline = 0;\r
2087         lf.lfStrikeOut = 0;\r
2088         lf.lfCharSet = DEFAULT_CHARSET;\r
2089         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2090         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2091         lf.lfQuality = PROOF_QUALITY;\r
2092         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2093         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2094         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2095 \r
2096         hPieceFont = CreateFontIndirect( &lf );\r
2097 \r
2098         if( hPieceFont == NULL ) {\r
2099             fontBitmapSquareSize = -2;\r
2100         }\r
2101         else {\r
2102             /* Setup font-to-piece character table */\r
2103             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2104                 /* No (or wrong) global settings, try to detect the font */\r
2105                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2106                     /* Alpha */\r
2107                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2108                 }\r
2109                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2110                     /* DiagramTT* family */\r
2111                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2112                 }\r
2113                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2114                     /* Fairy symbols */\r
2115                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2116                 }\r
2117                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2118                     /* Good Companion (Some characters get warped as literal :-( */\r
2119                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2120                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2121                     SetCharTable(pieceToFontChar, s);\r
2122                 }\r
2123                 else {\r
2124                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2125                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2126                 }\r
2127             }\r
2128 \r
2129             /* Create bitmaps */\r
2130             hfont_old = SelectObject( hdc, hPieceFont );\r
2131             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2132                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2133                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2134 \r
2135             SelectObject( hdc, hfont_old );\r
2136 \r
2137             fontBitmapSquareSize = squareSize;\r
2138         }\r
2139     }\r
2140 \r
2141     if( hdc != NULL ) {\r
2142         DeleteDC( hdc );\r
2143     }\r
2144 \r
2145     if( hdc_window != NULL ) {\r
2146         ReleaseDC( hwndMain, hdc_window );\r
2147     }\r
2148 }\r
2149 \r
2150 HBITMAP\r
2151 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2152 {\r
2153   char name[128], buf[MSG_SIZ];\r
2154 \r
2155     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2156   if(appData.pieceDirectory[0]) {\r
2157     HBITMAP res;\r
2158     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2159     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2160     if(res) return res;\r
2161   }\r
2162   if (gameInfo.event &&\r
2163       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2164       strcmp(name, "k80s") == 0) {\r
2165     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2166   }\r
2167   return LoadBitmap(hinst, name);\r
2168 }\r
2169 \r
2170 \r
2171 /* Insert a color into the program's logical palette\r
2172    structure.  This code assumes the given color is\r
2173    the result of the RGB or PALETTERGB macro, and it\r
2174    knows how those macros work (which is documented).\r
2175 */\r
2176 VOID\r
2177 InsertInPalette(COLORREF color)\r
2178 {\r
2179   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2180 \r
2181   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2182     DisplayFatalError(_("Too many colors"), 0, 1);\r
2183     pLogPal->palNumEntries--;\r
2184     return;\r
2185   }\r
2186 \r
2187   pe->peFlags = (char) 0;\r
2188   pe->peRed = (char) (0xFF & color);\r
2189   pe->peGreen = (char) (0xFF & (color >> 8));\r
2190   pe->peBlue = (char) (0xFF & (color >> 16));\r
2191   return;\r
2192 }\r
2193 \r
2194 \r
2195 VOID\r
2196 InitDrawingColors()\r
2197 {\r
2198   if (pLogPal == NULL) {\r
2199     /* Allocate enough memory for a logical palette with\r
2200      * PALETTESIZE entries and set the size and version fields\r
2201      * of the logical palette structure.\r
2202      */\r
2203     pLogPal = (NPLOGPALETTE)\r
2204       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2205                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2206     pLogPal->palVersion    = 0x300;\r
2207   }\r
2208   pLogPal->palNumEntries = 0;\r
2209 \r
2210   InsertInPalette(lightSquareColor);\r
2211   InsertInPalette(darkSquareColor);\r
2212   InsertInPalette(whitePieceColor);\r
2213   InsertInPalette(blackPieceColor);\r
2214   InsertInPalette(highlightSquareColor);\r
2215   InsertInPalette(premoveHighlightColor);\r
2216 \r
2217   /*  create a logical color palette according the information\r
2218    *  in the LOGPALETTE structure.\r
2219    */\r
2220   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2221 \r
2222   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2223   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2224   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2225   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2226   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2227   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2228   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2229   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2230   /* [AS] Force rendering of the font-based pieces */\r
2231   if( fontBitmapSquareSize > 0 ) {\r
2232     fontBitmapSquareSize = 0;\r
2233   }\r
2234 }\r
2235 \r
2236 \r
2237 int\r
2238 BoardWidth(int boardSize, int n)\r
2239 { /* [HGM] argument n added to allow different width and height */\r
2240   int lineGap = sizeInfo[boardSize].lineGap;\r
2241 \r
2242   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2243       lineGap = appData.overrideLineGap;\r
2244   }\r
2245 \r
2246   return (n + 1) * lineGap +\r
2247           n * sizeInfo[boardSize].squareSize;\r
2248 }\r
2249 \r
2250 /* Respond to board resize by dragging edge */\r
2251 VOID\r
2252 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2253 {\r
2254   BoardSize newSize = NUM_SIZES - 1;\r
2255   static int recurse = 0;\r
2256   if (IsIconic(hwndMain)) return;\r
2257   if (recurse > 0) return;\r
2258   recurse++;\r
2259   while (newSize > 0) {\r
2260         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2261         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2262            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2263     newSize--;\r
2264   } \r
2265   boardSize = newSize;\r
2266   InitDrawingSizes(boardSize, flags);\r
2267   recurse--;\r
2268 }\r
2269 \r
2270 \r
2271 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2272 \r
2273 VOID\r
2274 InitDrawingSizes(BoardSize boardSize, int flags)\r
2275 {\r
2276   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2277   ChessSquare piece;\r
2278   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2279   HDC hdc;\r
2280   SIZE clockSize, messageSize;\r
2281   HFONT oldFont;\r
2282   char buf[MSG_SIZ];\r
2283   char *str;\r
2284   HMENU hmenu = GetMenu(hwndMain);\r
2285   RECT crect, wrect, oldRect;\r
2286   int offby;\r
2287   LOGBRUSH logbrush;\r
2288   VariantClass v = gameInfo.variant;\r
2289 \r
2290   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2291   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2292 \r
2293   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2294   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2295   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2296   oldBoardSize = boardSize;\r
2297 \r
2298   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2299   { // correct board size to one where built-in pieces exist\r
2300     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2301        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2302       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2303       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2304       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2305       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2306       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2307                                    boardSize = SizeMiddling;\r
2308     }\r
2309   }\r
2310   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2311 \r
2312   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2313   oldRect.top = wpMain.y;\r
2314   oldRect.right = wpMain.x + wpMain.width;\r
2315   oldRect.bottom = wpMain.y + wpMain.height;\r
2316 \r
2317   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2318   smallLayout = sizeInfo[boardSize].smallLayout;\r
2319   squareSize = sizeInfo[boardSize].squareSize;\r
2320   lineGap = sizeInfo[boardSize].lineGap;\r
2321   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2322   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2323 \r
2324   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2325       lineGap = appData.overrideLineGap;\r
2326   }\r
2327 \r
2328   if (tinyLayout != oldTinyLayout) {\r
2329     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2330     if (tinyLayout) {\r
2331       style &= ~WS_SYSMENU;\r
2332       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2333                  "&Minimize\tCtrl+F4");\r
2334     } else {\r
2335       style |= WS_SYSMENU;\r
2336       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2337     }\r
2338     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2339 \r
2340     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2341       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2342         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2343     }\r
2344     DrawMenuBar(hwndMain);\r
2345   }\r
2346 \r
2347   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2348   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2349 \r
2350   /* Get text area sizes */\r
2351   hdc = GetDC(hwndMain);\r
2352   if (appData.clockMode) {\r
2353     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2354   } else {\r
2355     snprintf(buf, MSG_SIZ, _("White"));\r
2356   }\r
2357   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2358   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2359   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2360   str = _("We only care about the height here");\r
2361   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2362   SelectObject(hdc, oldFont);\r
2363   ReleaseDC(hwndMain, hdc);\r
2364 \r
2365   /* Compute where everything goes */\r
2366   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2367         /* [HGM] logo: if either logo is on, reserve space for it */\r
2368         logoHeight =  2*clockSize.cy;\r
2369         leftLogoRect.left   = OUTER_MARGIN;\r
2370         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2371         leftLogoRect.top    = OUTER_MARGIN;\r
2372         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2373 \r
2374         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2375         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2376         rightLogoRect.top    = OUTER_MARGIN;\r
2377         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2378 \r
2379 \r
2380     whiteRect.left = leftLogoRect.right;\r
2381     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2382     whiteRect.top = OUTER_MARGIN;\r
2383     whiteRect.bottom = whiteRect.top + logoHeight;\r
2384 \r
2385     blackRect.right = rightLogoRect.left;\r
2386     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2387     blackRect.top = whiteRect.top;\r
2388     blackRect.bottom = whiteRect.bottom;\r
2389   } else {\r
2390     whiteRect.left = OUTER_MARGIN;\r
2391     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2392     whiteRect.top = OUTER_MARGIN;\r
2393     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2394 \r
2395     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2396     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2397     blackRect.top = whiteRect.top;\r
2398     blackRect.bottom = whiteRect.bottom;\r
2399 \r
2400     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2401   }\r
2402 \r
2403   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2404   if (appData.showButtonBar) {\r
2405     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2406       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2407   } else {\r
2408     messageRect.right = OUTER_MARGIN + boardWidth;\r
2409   }\r
2410   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2411   messageRect.bottom = messageRect.top + messageSize.cy;\r
2412 \r
2413   boardRect.left = OUTER_MARGIN;\r
2414   boardRect.right = boardRect.left + boardWidth;\r
2415   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2416   boardRect.bottom = boardRect.top + boardHeight;\r
2417 \r
2418   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2419   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2420   oldTinyLayout = tinyLayout;\r
2421   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2422   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2423     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2424   winW *= 1 + twoBoards;\r
2425   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2426   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2427   wpMain.height = winH; //       without disturbing window attachments\r
2428   GetWindowRect(hwndMain, &wrect);\r
2429   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2430                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2431 \r
2432   // [HGM] placement: let attached windows follow size change.\r
2433   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2434   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2435   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2436   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2437   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2438 \r
2439   /* compensate if menu bar wrapped */\r
2440   GetClientRect(hwndMain, &crect);\r
2441   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2442   wpMain.height += offby;\r
2443   switch (flags) {\r
2444   case WMSZ_TOPLEFT:\r
2445     SetWindowPos(hwndMain, NULL, \r
2446                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2447                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2448     break;\r
2449 \r
2450   case WMSZ_TOPRIGHT:\r
2451   case WMSZ_TOP:\r
2452     SetWindowPos(hwndMain, NULL, \r
2453                  wrect.left, wrect.bottom - wpMain.height, \r
2454                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2455     break;\r
2456 \r
2457   case WMSZ_BOTTOMLEFT:\r
2458   case WMSZ_LEFT:\r
2459     SetWindowPos(hwndMain, NULL, \r
2460                  wrect.right - wpMain.width, wrect.top, \r
2461                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2462     break;\r
2463 \r
2464   case WMSZ_BOTTOMRIGHT:\r
2465   case WMSZ_BOTTOM:\r
2466   case WMSZ_RIGHT:\r
2467   default:\r
2468     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2469                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2470     break;\r
2471   }\r
2472 \r
2473   hwndPause = NULL;\r
2474   for (i = 0; i < N_BUTTONS; i++) {\r
2475     if (buttonDesc[i].hwnd != NULL) {\r
2476       DestroyWindow(buttonDesc[i].hwnd);\r
2477       buttonDesc[i].hwnd = NULL;\r
2478     }\r
2479     if (appData.showButtonBar) {\r
2480       buttonDesc[i].hwnd =\r
2481         CreateWindow("BUTTON", buttonDesc[i].label,\r
2482                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2483                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2484                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2485                      (HMENU) buttonDesc[i].id,\r
2486                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2487       if (tinyLayout) {\r
2488         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2489                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2490                     MAKELPARAM(FALSE, 0));\r
2491       }\r
2492       if (buttonDesc[i].id == IDM_Pause)\r
2493         hwndPause = buttonDesc[i].hwnd;\r
2494       buttonDesc[i].wndproc = (WNDPROC)\r
2495         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2496     }\r
2497   }\r
2498   if (gridPen != NULL) DeleteObject(gridPen);\r
2499   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2500   if (premovePen != NULL) DeleteObject(premovePen);\r
2501   if (lineGap != 0) {\r
2502     logbrush.lbStyle = BS_SOLID;\r
2503     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2504     gridPen =\r
2505       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2506                    lineGap, &logbrush, 0, NULL);\r
2507     logbrush.lbColor = highlightSquareColor;\r
2508     highlightPen =\r
2509       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2510                    lineGap, &logbrush, 0, NULL);\r
2511 \r
2512     logbrush.lbColor = premoveHighlightColor; \r
2513     premovePen =\r
2514       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2515                    lineGap, &logbrush, 0, NULL);\r
2516 \r
2517     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2518     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2519       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2520       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2521         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2522       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2523         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2524       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2525     }\r
2526     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2527       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2528       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2529         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2530         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2531       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2532         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2533       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2534     }\r
2535   }\r
2536 \r
2537   /* [HGM] Licensing requirement */\r
2538 #ifdef GOTHIC\r
2539   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2540 #endif\r
2541 #ifdef FALCON\r
2542   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2543 #endif\r
2544   GothicPopUp( "", VariantNormal);\r
2545 \r
2546 \r
2547 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2548 \r
2549   /* Load piece bitmaps for this board size */\r
2550   for (i=0; i<=2; i++) {\r
2551     for (piece = WhitePawn;\r
2552          (int) piece < (int) BlackPawn;\r
2553          piece = (ChessSquare) ((int) piece + 1)) {\r
2554       if (pieceBitmap[i][piece] != NULL)\r
2555         DeleteObject(pieceBitmap[i][piece]);\r
2556     }\r
2557   }\r
2558 \r
2559   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2560   // Orthodox Chess pieces\r
2561   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2562   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2563   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2564   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2565   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2566   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2567   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2568   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2569   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2570   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2571   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2572   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2573   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2574   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2575   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2576   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2577     // in Shogi, Hijack the unused Queen for Lance\r
2578     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2579     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2580     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2581   } else {\r
2582     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2583     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2584     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2585   }\r
2586 \r
2587   if(squareSize <= 72 && squareSize >= 33) { \r
2588     /* A & C are available in most sizes now */\r
2589     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2590       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2591       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2592       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2593       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2594       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2595       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2596       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2597       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2598       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2599       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2600       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2601       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2602     } else { // Smirf-like\r
2603       if(gameInfo.variant == VariantSChess) {\r
2604         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2605         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2606         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2607       } else {\r
2608         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2609         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2610         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2611       }\r
2612     }\r
2613     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2614       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2615       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2616       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2617     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2618       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2619       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2620       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2621     } else { // WinBoard standard\r
2622       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2623       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2624       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2625     }\r
2626   }\r
2627 \r
2628 \r
2629   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2630     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2631     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2632     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2633     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2634     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2635     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2636     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2637     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2638     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2639     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2640     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2641     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2642     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2643     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2644     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2645     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2646     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2647     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2648     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2649     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2650     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2651     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2652     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2653     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2654     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2655     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2656     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2657     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2658     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2659     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2660 \r
2661     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2662       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2663       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2664       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2665       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2666       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2667       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2668       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2669       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2670       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2671       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2672       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2673       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2674     } else {\r
2675       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2676       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2677       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2678       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2679       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2680       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2681       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2682       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2683       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2684       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2685       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2686       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2687     }\r
2688 \r
2689   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2690     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2691     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2692     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2693     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2694     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2695     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2696     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2697     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2698     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2699     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2700     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2701     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2702     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2703     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2704   }\r
2705 \r
2706 \r
2707   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2708   /* special Shogi support in this size */\r
2709   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2710       for (piece = WhitePawn;\r
2711            (int) piece < (int) BlackPawn;\r
2712            piece = (ChessSquare) ((int) piece + 1)) {\r
2713         if (pieceBitmap[i][piece] != NULL)\r
2714           DeleteObject(pieceBitmap[i][piece]);\r
2715       }\r
2716     }\r
2717   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2718   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2719   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2720   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2721   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2722   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2723   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2724   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2725   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2726   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2727   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2728   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2729   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2730   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2731   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2732   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2733   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2734   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2735   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2736   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2737   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2738   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2739   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2740   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2741   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2742   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2743   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2744   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2745   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2746   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2747   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2748   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2749   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2750   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2751   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2752   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2753   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2754   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2755   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2756   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2757   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2758   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2759   minorSize = 0;\r
2760   }\r
2761 }\r
2762 \r
2763 HBITMAP\r
2764 PieceBitmap(ChessSquare p, int kind)\r
2765 {\r
2766   if ((int) p >= (int) BlackPawn)\r
2767     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2768 \r
2769   return pieceBitmap[kind][(int) p];\r
2770 }\r
2771 \r
2772 /***************************************************************/\r
2773 \r
2774 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2775 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2776 /*\r
2777 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2778 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2779 */\r
2780 \r
2781 VOID\r
2782 SquareToPos(int row, int column, int * x, int * y)\r
2783 {\r
2784   if (flipView) {\r
2785     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2786     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2787   } else {\r
2788     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2789     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2790   }\r
2791 }\r
2792 \r
2793 VOID\r
2794 DrawCoordsOnDC(HDC hdc)\r
2795 {\r
2796   static char files[] = "0123456789012345678901221098765432109876543210";\r
2797   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2798   char str[2] = { NULLCHAR, NULLCHAR };\r
2799   int oldMode, oldAlign, x, y, start, i;\r
2800   HFONT oldFont;\r
2801   HBRUSH oldBrush;\r
2802 \r
2803   if (!appData.showCoords)\r
2804     return;\r
2805 \r
2806   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2807 \r
2808   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2809   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2810   oldAlign = GetTextAlign(hdc);\r
2811   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2812 \r
2813   y = boardRect.top + lineGap;\r
2814   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2815 \r
2816   if(border) {\r
2817     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2818     x += border - lineGap - 4; y += squareSize - 6;\r
2819   } else\r
2820   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2821   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2822     str[0] = files[start + i];\r
2823     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2824     y += squareSize + lineGap;\r
2825   }\r
2826 \r
2827   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2828 \r
2829   if(border) {\r
2830     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2831     x += -border + 4; y += border - squareSize + 6;\r
2832   } else\r
2833   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2834   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2835     str[0] = ranks[start + i];\r
2836     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2837     x += squareSize + lineGap;\r
2838   }    \r
2839 \r
2840   SelectObject(hdc, oldBrush);\r
2841   SetBkMode(hdc, oldMode);\r
2842   SetTextAlign(hdc, oldAlign);\r
2843   SelectObject(hdc, oldFont);\r
2844 }\r
2845 \r
2846 VOID\r
2847 DrawGridOnDC(HDC hdc)\r
2848 {\r
2849   HPEN oldPen;\r
2850  \r
2851   if (lineGap != 0) {\r
2852     oldPen = SelectObject(hdc, gridPen);\r
2853     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2854     SelectObject(hdc, oldPen);\r
2855   }\r
2856 }\r
2857 \r
2858 #define HIGHLIGHT_PEN 0\r
2859 #define PREMOVE_PEN   1\r
2860 \r
2861 VOID\r
2862 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2863 {\r
2864   int x1, y1;\r
2865   HPEN oldPen, hPen;\r
2866   if (lineGap == 0) return;\r
2867   if (flipView) {\r
2868     x1 = boardRect.left +\r
2869       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2870     y1 = boardRect.top +\r
2871       lineGap/2 + y * (squareSize + lineGap) + border;\r
2872   } else {\r
2873     x1 = boardRect.left +\r
2874       lineGap/2 + x * (squareSize + lineGap) + border;\r
2875     y1 = boardRect.top +\r
2876       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2877   }\r
2878   hPen = pen ? premovePen : highlightPen;\r
2879   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2880   MoveToEx(hdc, x1, y1, NULL);\r
2881   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2882   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2883   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2884   LineTo(hdc, x1, y1);\r
2885   SelectObject(hdc, oldPen);\r
2886 }\r
2887 \r
2888 VOID\r
2889 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2890 {\r
2891   int i;\r
2892   for (i=0; i<2; i++) {\r
2893     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2894       DrawHighlightOnDC(hdc, TRUE,\r
2895                         h->sq[i].x, h->sq[i].y,\r
2896                         pen);\r
2897   }\r
2898 }\r
2899 \r
2900 /* Note: sqcolor is used only in monoMode */\r
2901 /* Note that this code is largely duplicated in woptions.c,\r
2902    function DrawSampleSquare, so that needs to be updated too */\r
2903 VOID\r
2904 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2905 {\r
2906   HBITMAP oldBitmap;\r
2907   HBRUSH oldBrush;\r
2908   int tmpSize;\r
2909 \r
2910   if (appData.blindfold) return;\r
2911 \r
2912   /* [AS] Use font-based pieces if needed */\r
2913   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2914     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2915     CreatePiecesFromFont();\r
2916 \r
2917     if( fontBitmapSquareSize == squareSize ) {\r
2918         int index = TranslatePieceToFontPiece(piece);\r
2919 \r
2920         SelectObject( tmphdc, hPieceMask[ index ] );\r
2921 \r
2922       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2923         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2924       else\r
2925         BitBlt( hdc,\r
2926             x, y,\r
2927             squareSize, squareSize,\r
2928             tmphdc,\r
2929             0, 0,\r
2930             SRCAND );\r
2931 \r
2932         SelectObject( tmphdc, hPieceFace[ index ] );\r
2933 \r
2934       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2935         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2936       else\r
2937         BitBlt( hdc,\r
2938             x, y,\r
2939             squareSize, squareSize,\r
2940             tmphdc,\r
2941             0, 0,\r
2942             SRCPAINT );\r
2943 \r
2944         return;\r
2945     }\r
2946   }\r
2947 \r
2948   if (appData.monoMode) {\r
2949     SelectObject(tmphdc, PieceBitmap(piece, \r
2950       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2951     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2952            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2953   } else {\r
2954     HBRUSH xBrush = whitePieceBrush;\r
2955     tmpSize = squareSize;\r
2956     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2957     if(minorSize &&\r
2958         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2959          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2960       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2961       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2962       x += (squareSize - minorSize)>>1;\r
2963       y += squareSize - minorSize - 2;\r
2964       tmpSize = minorSize;\r
2965     }\r
2966     if (color || appData.allWhite ) {\r
2967       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2968       if( color )\r
2969               oldBrush = SelectObject(hdc, xBrush);\r
2970       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2971       if(appData.upsideDown && color==flipView)\r
2972         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2973       else\r
2974         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2975       /* Use black for outline of white pieces */\r
2976       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2977       if(appData.upsideDown && color==flipView)\r
2978         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2979       else\r
2980         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2981     } else if(appData.pieceDirectory[0]) {\r
2982       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2983       oldBrush = SelectObject(hdc, xBrush);\r
2984       if(appData.upsideDown && color==flipView)\r
2985         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2986       else\r
2987         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2988       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2989       if(appData.upsideDown && color==flipView)\r
2990         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2991       else\r
2992         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2993     } else {\r
2994       /* Use square color for details of black pieces */\r
2995       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2996       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2997       if(appData.upsideDown && !flipView)\r
2998         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2999       else\r
3000         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3001     }\r
3002     SelectObject(hdc, oldBrush);\r
3003     SelectObject(tmphdc, oldBitmap);\r
3004   }\r
3005 }\r
3006 \r
3007 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3008 int GetBackTextureMode( int algo )\r
3009 {\r
3010     int result = BACK_TEXTURE_MODE_DISABLED;\r
3011 \r
3012     switch( algo ) \r
3013     {\r
3014         case BACK_TEXTURE_MODE_PLAIN:\r
3015             result = 1; /* Always use identity map */\r
3016             break;\r
3017         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3018             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3019             break;\r
3020     }\r
3021 \r
3022     return result;\r
3023 }\r
3024 \r
3025 /* \r
3026     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3027     to handle redraws cleanly (as random numbers would always be different).\r
3028 */\r
3029 VOID RebuildTextureSquareInfo()\r
3030 {\r
3031     BITMAP bi;\r
3032     int lite_w = 0;\r
3033     int lite_h = 0;\r
3034     int dark_w = 0;\r
3035     int dark_h = 0;\r
3036     int row;\r
3037     int col;\r
3038 \r
3039     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3040 \r
3041     if( liteBackTexture != NULL ) {\r
3042         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3043             lite_w = bi.bmWidth;\r
3044             lite_h = bi.bmHeight;\r
3045         }\r
3046     }\r
3047 \r
3048     if( darkBackTexture != NULL ) {\r
3049         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3050             dark_w = bi.bmWidth;\r
3051             dark_h = bi.bmHeight;\r
3052         }\r
3053     }\r
3054 \r
3055     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3056         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3057             if( (col + row) & 1 ) {\r
3058                 /* Lite square */\r
3059                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3060                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3061                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3062                   else\r
3063                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3064                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3065                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3066                   else\r
3067                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3068                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3069                 }\r
3070             }\r
3071             else {\r
3072                 /* Dark square */\r
3073                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3074                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3075                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3076                   else\r
3077                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3078                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3079                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3080                   else\r
3081                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3082                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3083                 }\r
3084             }\r
3085         }\r
3086     }\r
3087 }\r
3088 \r
3089 /* [AS] Arrow highlighting support */\r
3090 \r
3091 static double A_WIDTH = 5; /* Width of arrow body */\r
3092 \r
3093 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3094 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3095 \r
3096 static double Sqr( double x )\r
3097 {\r
3098     return x*x;\r
3099 }\r
3100 \r
3101 static int Round( double x )\r
3102 {\r
3103     return (int) (x + 0.5);\r
3104 }\r
3105 \r
3106 /* Draw an arrow between two points using current settings */\r
3107 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3108 {\r
3109     POINT arrow[7];\r
3110     double dx, dy, j, k, x, y;\r
3111 \r
3112     if( d_x == s_x ) {\r
3113         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3114 \r
3115         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3116         arrow[0].y = s_y;\r
3117 \r
3118         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3119         arrow[1].y = d_y - h;\r
3120 \r
3121         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3122         arrow[2].y = d_y - h;\r
3123 \r
3124         arrow[3].x = d_x;\r
3125         arrow[3].y = d_y;\r
3126 \r
3127         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3128         arrow[5].y = d_y - h;\r
3129 \r
3130         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3131         arrow[4].y = d_y - h;\r
3132 \r
3133         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3134         arrow[6].y = s_y;\r
3135     }\r
3136     else if( d_y == s_y ) {\r
3137         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3138 \r
3139         arrow[0].x = s_x;\r
3140         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3141 \r
3142         arrow[1].x = d_x - w;\r
3143         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3144 \r
3145         arrow[2].x = d_x - w;\r
3146         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3147 \r
3148         arrow[3].x = d_x;\r
3149         arrow[3].y = d_y;\r
3150 \r
3151         arrow[5].x = d_x - w;\r
3152         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3153 \r
3154         arrow[4].x = d_x - w;\r
3155         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3156 \r
3157         arrow[6].x = s_x;\r
3158         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3159     }\r
3160     else {\r
3161         /* [AS] Needed a lot of paper for this! :-) */\r
3162         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3163         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3164   \r
3165         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3166 \r
3167         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3168 \r
3169         x = s_x;\r
3170         y = s_y;\r
3171 \r
3172         arrow[0].x = Round(x - j);\r
3173         arrow[0].y = Round(y + j*dx);\r
3174 \r
3175         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3176         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3177 \r
3178         if( d_x > s_x ) {\r
3179             x = (double) d_x - k;\r
3180             y = (double) d_y - k*dy;\r
3181         }\r
3182         else {\r
3183             x = (double) d_x + k;\r
3184             y = (double) d_y + k*dy;\r
3185         }\r
3186 \r
3187         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r