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