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