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