2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\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
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\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
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
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\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
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
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
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern int ics_type;
\r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
106 VOID NewVariantPopup(HWND hwnd);
\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
108 /*char*/int promoChar));
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
133 POINT sq[2]; /* board coordinates of from, to squares */
\r
136 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
141 typedef struct { // [HGM] atomic
\r
142 int fromX, fromY, toX, toY, radius;
\r
145 static ExplodeInfo explodeInfo;
\r
147 /* Window class names */
\r
148 char szAppName[] = "WinBoard";
\r
149 char szConsoleName[] = "WBConsole";
\r
151 /* Title bar text */
\r
152 char szTitle[] = "WinBoard";
\r
153 char szConsoleTitle[] = "I C S Interaction";
\r
156 char *settingsFileName;
\r
157 Boolean saveSettingsOnExit;
\r
158 char installDir[MSG_SIZ];
\r
159 int errorExitStatus;
\r
161 BoardSize boardSize;
\r
162 Boolean chessProgram;
\r
163 //static int boardX, boardY;
\r
164 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
165 int squareSize, lineGap, minorSize;
\r
166 static int winW, winH;
\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
168 static int logoHeight = 0;
\r
169 static char messageText[MESSAGE_TEXT_MAX];
\r
170 static int clockTimerEvent = 0;
\r
171 static int loadGameTimerEvent = 0;
\r
172 static int analysisTimerEvent = 0;
\r
173 static DelayedEventCallback delayedTimerCallback;
\r
174 static int delayedTimerEvent = 0;
\r
175 static int buttonCount = 2;
\r
176 char *icsTextMenuString;
\r
178 char *firstChessProgramNames;
\r
179 char *secondChessProgramNames;
\r
181 #define PALETTESIZE 256
\r
183 HINSTANCE hInst; /* current instance */
\r
184 Boolean alwaysOnTop = FALSE;
\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
187 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
189 ColorClass currentColorClass;
\r
191 static HWND savedHwnd;
\r
192 HWND hCommPort = NULL; /* currently open comm port */
\r
193 static HWND hwndPause; /* pause button */
\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
196 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
197 explodeBrush, /* [HGM] atomic */
\r
198 markerBrush, /* [HGM] markers */
\r
199 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
202 static HPEN gridPen = NULL;
\r
203 static HPEN highlightPen = NULL;
\r
204 static HPEN premovePen = NULL;
\r
205 static NPLOGPALETTE pLogPal;
\r
206 static BOOL paletteChanged = FALSE;
\r
207 static HICON iconWhite, iconBlack, iconCurrent;
\r
208 static int doingSizing = FALSE;
\r
209 static int lastSizing = 0;
\r
210 static int prevStderrPort;
\r
211 static HBITMAP userLogo;
\r
213 static HBITMAP liteBackTexture = NULL;
\r
214 static HBITMAP darkBackTexture = NULL;
\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
217 static int backTextureSquareSize = 0;
\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
220 #if __GNUC__ && !defined(_winmajor)
\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
223 #if defined(_winmajor)
\r
224 #define oldDialog (_winmajor < 4)
\r
226 #define oldDialog 0
\r
230 #define INTERNATIONAL
\r
232 #ifdef INTERNATIONAL
\r
233 # define _(s) T_(s)
\r
239 # define Translate(x, y)
\r
240 # define LoadLanguageFile(s)
\r
243 #ifdef INTERNATIONAL
\r
245 Boolean barbaric; // flag indicating if translation is needed
\r
247 // list of item numbers used in each dialog (used to alter language at run time)
\r
249 #define ABOUTBOX -1 /* not sure why these are needed */
\r
250 #define ABOUTBOX2 -1
\r
252 int dialogItems[][40] = {
\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
255 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
257 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
258 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
259 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
260 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
261 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
262 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
263 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
264 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
265 { ABOUTBOX2, IDC_ChessBoard },
\r
266 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
267 OPT_GameListClose, IDC_GameListDoFilter },
\r
268 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
269 { DLG_Error, IDOK },
\r
270 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
271 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
272 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
273 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
274 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
275 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
276 { DLG_IndexNumber, IDC_Index },
\r
277 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
278 { DLG_TypeInName, IDOK, IDCANCEL },
\r
279 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
280 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
281 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
282 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
283 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
284 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
285 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
286 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
287 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
288 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
289 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
290 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
291 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
292 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
293 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
294 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
295 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
296 GPB_General, GPB_Alarm },
\r
297 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
298 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
299 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
300 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
301 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
302 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
303 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
304 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
305 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
306 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
307 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
308 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
309 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
310 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
311 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
312 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
313 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
314 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
315 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
316 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
317 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
318 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
319 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
320 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
321 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
322 { DLG_MoveHistory },
\r
323 { DLG_EvalGraph },
\r
324 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
325 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
326 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
327 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
328 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
329 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
330 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
331 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
332 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
336 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
337 static int lastChecked;
\r
338 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
339 extern int tinyLayout;
\r
340 extern char * menuBarText[][10];
\r
343 LoadLanguageFile(char *name)
\r
344 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
346 int i=0, j=0, n=0, k;
\r
349 if(!name || name[0] == NULLCHAR) return;
\r
350 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
351 appData.language = oldLanguage;
\r
352 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
353 if((f = fopen(buf, "r")) == NULL) return;
\r
354 while((k = fgetc(f)) != EOF) {
\r
355 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
356 languageBuf[i] = k;
\r
358 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
360 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
361 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
362 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
363 english[j] = languageBuf + n + 1; *p = 0;
\r
364 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
365 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
370 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
372 case 'n': k = '\n'; break;
\r
373 case 'r': k = '\r'; break;
\r
374 case 't': k = '\t'; break;
\r
376 languageBuf[--i] = k;
\r
381 barbaric = (j != 0);
\r
382 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
387 { // return the translation of the given string
\r
388 // efficiency can be improved a lot...
\r
390 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
391 if(!barbaric) return s;
\r
392 if(!s) return ""; // sanity
\r
393 while(english[i]) {
\r
394 if(!strcmp(s, english[i])) return foreign[i];
\r
401 Translate(HWND hDlg, int dialogID)
\r
402 { // translate all text items in the given dialog
\r
404 char buf[MSG_SIZ], *s;
\r
405 if(!barbaric) return;
\r
406 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
407 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
408 GetWindowText( hDlg, buf, MSG_SIZ );
\r
410 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
411 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
412 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
413 if(strlen(buf) == 0) continue;
\r
415 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
420 TranslateOneMenu(int i, HMENU subMenu)
\r
423 static MENUITEMINFO info;
\r
425 info.cbSize = sizeof(MENUITEMINFO);
\r
426 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
427 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
429 info.dwTypeData = buf;
\r
430 info.cch = sizeof(buf);
\r
431 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
433 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
434 else menuText[i][j] = strdup(buf); // remember original on first change
\r
436 if(buf[0] == NULLCHAR) continue;
\r
437 info.dwTypeData = T_(buf);
\r
438 info.cch = strlen(buf)+1;
\r
439 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
445 TranslateMenus(int addLanguage)
\r
448 WIN32_FIND_DATA fileData;
\r
450 #define IDM_English 1970
\r
452 HMENU mainMenu = GetMenu(hwndMain);
\r
453 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
454 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
455 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
456 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
457 TranslateOneMenu(i, subMenu);
\r
459 DrawMenuBar(hwndMain);
\r
462 if(!addLanguage) return;
\r
463 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
464 HMENU mainMenu = GetMenu(hwndMain);
\r
465 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
466 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
467 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
468 i = 0; lastChecked = IDM_English;
\r
470 char *p, *q = fileData.cFileName;
\r
471 int checkFlag = MF_UNCHECKED;
\r
472 languageFile[i] = strdup(q);
\r
473 if(barbaric && !strcmp(oldLanguage, q)) {
\r
474 checkFlag = MF_CHECKED;
\r
475 lastChecked = IDM_English + i + 1;
\r
476 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
478 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
479 p = strstr(fileData.cFileName, ".lng");
\r
481 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
482 } while(FindNextFile(hFind, &fileData));
\r
495 int cliWidth, cliHeight;
\r
498 SizeInfo sizeInfo[] =
\r
500 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
501 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
502 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
503 { "petite", 33, 1, 1, 1, 0, 0 },
\r
504 { "slim", 37, 2, 1, 0, 0, 0 },
\r
505 { "small", 40, 2, 1, 0, 0, 0 },
\r
506 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
507 { "middling", 49, 2, 0, 0, 0, 0 },
\r
508 { "average", 54, 2, 0, 0, 0, 0 },
\r
509 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
510 { "medium", 64, 3, 0, 0, 0, 0 },
\r
511 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
512 { "large", 80, 3, 0, 0, 0, 0 },
\r
513 { "big", 87, 3, 0, 0, 0, 0 },
\r
514 { "huge", 95, 3, 0, 0, 0, 0 },
\r
515 { "giant", 108, 3, 0, 0, 0, 0 },
\r
516 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
517 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
518 { NULL, 0, 0, 0, 0, 0, 0 }
\r
521 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
522 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
524 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
525 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
526 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
527 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
528 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
529 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
530 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
531 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
532 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
533 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
534 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
535 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
536 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
537 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
538 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
539 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
540 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },
\r
541 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
544 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
553 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
554 #define N_BUTTONS 5
\r
556 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
558 {"<<", IDM_ToStart, NULL, NULL},
\r
559 {"<", IDM_Backward, NULL, NULL},
\r
560 {"P", IDM_Pause, NULL, NULL},
\r
561 {">", IDM_Forward, NULL, NULL},
\r
562 {">>", IDM_ToEnd, NULL, NULL},
\r
565 int tinyLayout = 0, smallLayout = 0;
\r
566 #define MENU_BAR_ITEMS 9
\r
567 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
568 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
569 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
573 MySound sounds[(int)NSoundClasses];
\r
574 MyTextAttribs textAttribs[(int)NColorClasses];
\r
576 MyColorizeAttribs colorizeAttribs[] = {
\r
577 { (COLORREF)0, 0, N_("Shout Text") },
\r
578 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
579 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
580 { (COLORREF)0, 0, N_("Channel Text") },
\r
581 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
582 { (COLORREF)0, 0, N_("Tell Text") },
\r
583 { (COLORREF)0, 0, N_("Challenge Text") },
\r
584 { (COLORREF)0, 0, N_("Request Text") },
\r
585 { (COLORREF)0, 0, N_("Seek Text") },
\r
586 { (COLORREF)0, 0, N_("Normal Text") },
\r
587 { (COLORREF)0, 0, N_("None") }
\r
592 static char *commentTitle;
\r
593 static char *commentText;
\r
594 static int commentIndex;
\r
595 static Boolean editComment = FALSE;
\r
598 char errorTitle[MSG_SIZ];
\r
599 char errorMessage[2*MSG_SIZ];
\r
600 HWND errorDialog = NULL;
\r
601 BOOLEAN moveErrorMessageUp = FALSE;
\r
602 BOOLEAN consoleEcho = TRUE;
\r
603 CHARFORMAT consoleCF;
\r
604 COLORREF consoleBackgroundColor;
\r
606 char *programVersion;
\r
612 typedef int CPKind;
\r
621 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
624 #define INPUT_SOURCE_BUF_SIZE 4096
\r
626 typedef struct _InputSource {
\r
633 char buf[INPUT_SOURCE_BUF_SIZE];
\r
637 InputCallback func;
\r
638 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
642 InputSource *consoleInputSource;
\r
647 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
648 VOID ConsoleCreate();
\r
650 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
651 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
652 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
653 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
655 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
656 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
657 void ParseIcsTextMenu(char *icsTextMenuString);
\r
658 VOID PopUpNameDialog(char firstchar);
\r
659 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
663 int GameListOptions();
\r
665 int dummy; // [HGM] for obsolete args
\r
667 HWND hwndMain = NULL; /* root window*/
\r
668 HWND hwndConsole = NULL;
\r
669 HWND commentDialog = NULL;
\r
670 HWND moveHistoryDialog = NULL;
\r
671 HWND evalGraphDialog = NULL;
\r
672 HWND engineOutputDialog = NULL;
\r
673 HWND gameListDialog = NULL;
\r
674 HWND editTagsDialog = NULL;
\r
676 int commentUp = FALSE;
\r
678 WindowPlacement wpMain;
\r
679 WindowPlacement wpConsole;
\r
680 WindowPlacement wpComment;
\r
681 WindowPlacement wpMoveHistory;
\r
682 WindowPlacement wpEvalGraph;
\r
683 WindowPlacement wpEngineOutput;
\r
684 WindowPlacement wpGameList;
\r
685 WindowPlacement wpTags;
\r
687 VOID EngineOptionsPopup(); // [HGM] settings
\r
689 VOID GothicPopUp(char *title, VariantClass variant);
\r
691 * Setting "frozen" should disable all user input other than deleting
\r
692 * the window. We do this while engines are initializing themselves.
\r
694 static int frozen = 0;
\r
695 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
701 if (frozen) return;
\r
703 hmenu = GetMenu(hwndMain);
\r
704 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
705 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
707 DrawMenuBar(hwndMain);
\r
710 /* Undo a FreezeUI */
\r
716 if (!frozen) return;
\r
718 hmenu = GetMenu(hwndMain);
\r
719 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
720 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
722 DrawMenuBar(hwndMain);
\r
725 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
727 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
733 #define JAWS_ALT_INTERCEPT
\r
734 #define JAWS_KB_NAVIGATION
\r
735 #define JAWS_MENU_ITEMS
\r
736 #define JAWS_SILENCE
\r
737 #define JAWS_REPLAY
\r
739 #define JAWS_COPYRIGHT
\r
740 #define JAWS_DELETE(X) X
\r
741 #define SAYMACHINEMOVE()
\r
745 /*---------------------------------------------------------------------------*\
\r
749 \*---------------------------------------------------------------------------*/
\r
752 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
753 LPSTR lpCmdLine, int nCmdShow)
\r
756 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
757 // INITCOMMONCONTROLSEX ex;
\r
761 LoadLibrary("RICHED32.DLL");
\r
762 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
764 if (!InitApplication(hInstance)) {
\r
767 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
774 // InitCommonControlsEx(&ex);
\r
775 InitCommonControls();
\r
777 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
778 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
779 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
781 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
783 while (GetMessage(&msg, /* message structure */
\r
784 NULL, /* handle of window receiving the message */
\r
785 0, /* lowest message to examine */
\r
786 0)) /* highest message to examine */
\r
789 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
790 // [HGM] navigate: switch between all windows with tab
\r
791 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
792 int i, currentElement = 0;
\r
794 // first determine what element of the chain we come from (if any)
\r
795 if(appData.icsActive) {
\r
796 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
797 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
799 if(engineOutputDialog && EngineOutputIsUp()) {
\r
800 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
801 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
803 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
804 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
806 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
807 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
808 if(msg.hwnd == e1) currentElement = 2; else
\r
809 if(msg.hwnd == e2) currentElement = 3; else
\r
810 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
811 if(msg.hwnd == mh) currentElement = 4; else
\r
812 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
813 if(msg.hwnd == hText) currentElement = 5; else
\r
814 if(msg.hwnd == hInput) currentElement = 6; else
\r
815 for (i = 0; i < N_BUTTONS; i++) {
\r
816 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
819 // determine where to go to
\r
820 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
822 currentElement = (currentElement + direction) % 7;
\r
823 switch(currentElement) {
\r
825 h = hwndMain; break; // passing this case always makes the loop exit
\r
827 h = buttonDesc[0].hwnd; break; // could be NULL
\r
829 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
832 if(!EngineOutputIsUp()) continue;
\r
835 if(!MoveHistoryIsUp()) continue;
\r
837 // case 6: // input to eval graph does not seem to get here!
\r
838 // if(!EvalGraphIsUp()) continue;
\r
839 // h = evalGraphDialog; break;
\r
841 if(!appData.icsActive) continue;
\r
845 if(!appData.icsActive) continue;
\r
851 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
852 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
855 continue; // this message now has been processed
\r
859 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
860 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
861 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
862 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
863 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
864 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
865 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
866 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
867 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
868 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
869 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
870 for(i=0; i<MAX_CHAT; i++)
\r
871 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
874 if(done) continue; // [HGM] chat: end patch
\r
875 TranslateMessage(&msg); /* Translates virtual key codes */
\r
876 DispatchMessage(&msg); /* Dispatches message to window */
\r
881 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
884 /*---------------------------------------------------------------------------*\
\r
886 * Initialization functions
\r
888 \*---------------------------------------------------------------------------*/
\r
892 { // update user logo if necessary
\r
893 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
895 if(appData.autoLogo) {
\r
896 curName = UserName();
\r
897 if(strcmp(curName, oldUserName)) {
\r
898 GetCurrentDirectory(MSG_SIZ, dir);
\r
899 SetCurrentDirectory(installDir);
\r
900 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
901 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
902 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
903 if(userLogo == NULL)
\r
904 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
905 SetCurrentDirectory(dir); /* return to prev directory */
\r
911 InitApplication(HINSTANCE hInstance)
\r
915 /* Fill in window class structure with parameters that describe the */
\r
918 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
919 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
920 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
921 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
922 wc.hInstance = hInstance; /* Owner of this class */
\r
923 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
924 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
925 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
926 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
927 wc.lpszClassName = szAppName; /* Name to register as */
\r
929 /* Register the window class and return success/failure code. */
\r
930 if (!RegisterClass(&wc)) return FALSE;
\r
932 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
933 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
935 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
936 wc.hInstance = hInstance;
\r
937 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
938 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
939 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
940 wc.lpszMenuName = NULL;
\r
941 wc.lpszClassName = szConsoleName;
\r
943 if (!RegisterClass(&wc)) return FALSE;
\r
948 /* Set by InitInstance, used by EnsureOnScreen */
\r
949 int screenHeight, screenWidth;
\r
952 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
954 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
955 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
956 if (*x > screenWidth - 32) *x = 0;
\r
957 if (*y > screenHeight - 32) *y = 0;
\r
958 if (*x < minX) *x = minX;
\r
959 if (*y < minY) *y = minY;
\r
963 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
965 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
966 GetCurrentDirectory(MSG_SIZ, dir);
\r
967 SetCurrentDirectory(installDir);
\r
968 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
969 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
971 if (cps->programLogo == NULL && appData.debugMode) {
\r
972 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
974 } else if(appData.autoLogo) {
\r
975 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
976 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
977 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
979 if(appData.directory[n] && appData.directory[n][0]) {
\r
980 SetCurrentDirectory(appData.directory[n]);
\r
981 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
984 SetCurrentDirectory(dir); /* return to prev directory */
\r
988 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
990 HWND hwnd; /* Main window handle. */
\r
992 WINDOWPLACEMENT wp;
\r
995 hInst = hInstance; /* Store instance handle in our global variable */
\r
996 programName = szAppName;
\r
998 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
999 *filepart = NULLCHAR;
\r
1001 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1003 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1004 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1005 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1006 /* xboard, and older WinBoards, controlled the move sound with the
\r
1007 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1008 always turn the option on (so that the backend will call us),
\r
1009 then let the user turn the sound off by setting it to silence if
\r
1010 desired. To accommodate old winboard.ini files saved by old
\r
1011 versions of WinBoard, we also turn off the sound if the option
\r
1012 was initially set to false. [HGM] taken out of InitAppData */
\r
1013 if (!appData.ringBellAfterMoves) {
\r
1014 sounds[(int)SoundMove].name = strdup("");
\r
1015 appData.ringBellAfterMoves = TRUE;
\r
1017 if (appData.debugMode) {
\r
1018 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1019 setbuf(debugFP, NULL);
\r
1022 LoadLanguageFile(appData.language);
\r
1026 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1027 // InitEngineUCI( installDir, &second );
\r
1029 /* Create a main window for this application instance. */
\r
1030 hwnd = CreateWindow(szAppName, szTitle,
\r
1031 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1032 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1033 NULL, NULL, hInstance, NULL);
\r
1036 /* If window could not be created, return "failure" */
\r
1041 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1042 LoadLogo(&first, 0, FALSE);
\r
1043 LoadLogo(&second, 1, appData.icsActive);
\r
1047 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1048 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1049 iconCurrent = iconWhite;
\r
1050 InitDrawingColors();
\r
1051 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1052 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1053 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1054 /* Compute window size for each board size, and use the largest
\r
1055 size that fits on this screen as the default. */
\r
1056 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1057 if (boardSize == (BoardSize)-1 &&
\r
1058 winH <= screenHeight
\r
1059 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1060 && winW <= screenWidth) {
\r
1061 boardSize = (BoardSize)ibs;
\r
1065 InitDrawingSizes(boardSize, 0);
\r
1067 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1069 /* [AS] Load textures if specified */
\r
1070 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1072 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1073 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1074 liteBackTextureMode = appData.liteBackTextureMode;
\r
1076 if (liteBackTexture == NULL && appData.debugMode) {
\r
1077 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1081 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1082 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1083 darkBackTextureMode = appData.darkBackTextureMode;
\r
1085 if (darkBackTexture == NULL && appData.debugMode) {
\r
1086 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1090 mysrandom( (unsigned) time(NULL) );
\r
1092 /* [AS] Restore layout */
\r
1093 if( wpMoveHistory.visible ) {
\r
1094 MoveHistoryPopUp();
\r
1097 if( wpEvalGraph.visible ) {
\r
1101 if( wpEngineOutput.visible ) {
\r
1102 EngineOutputPopUp();
\r
1105 /* Make the window visible; update its client area; and return "success" */
\r
1106 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1107 wp.length = sizeof(WINDOWPLACEMENT);
\r
1109 wp.showCmd = nCmdShow;
\r
1110 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1111 wp.rcNormalPosition.left = wpMain.x;
\r
1112 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1113 wp.rcNormalPosition.top = wpMain.y;
\r
1114 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1115 SetWindowPlacement(hwndMain, &wp);
\r
1117 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1119 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1120 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1122 if (hwndConsole) {
\r
1124 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1125 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1127 ShowWindow(hwndConsole, nCmdShow);
\r
1128 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1129 char buf[MSG_SIZ], *p = buf, *q;
\r
1130 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1132 q = strchr(p, ';');
\r
1134 if(*p) ChatPopUp(p);
\r
1137 SetActiveWindow(hwndConsole);
\r
1139 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1140 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1149 HMENU hmenu = GetMenu(hwndMain);
\r
1151 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1152 MF_BYCOMMAND|((appData.icsActive &&
\r
1153 *appData.icsCommPort != NULLCHAR) ?
\r
1154 MF_ENABLED : MF_GRAYED));
\r
1155 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1156 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1157 MF_CHECKED : MF_UNCHECKED));
\r
1160 //---------------------------------------------------------------------------------------------------------
\r
1162 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1163 #define XBOARD FALSE
\r
1165 #define OPTCHAR "/"
\r
1166 #define SEPCHAR "="
\r
1170 // front-end part of option handling
\r
1173 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1175 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1176 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1179 lf->lfEscapement = 0;
\r
1180 lf->lfOrientation = 0;
\r
1181 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1182 lf->lfItalic = mfp->italic;
\r
1183 lf->lfUnderline = mfp->underline;
\r
1184 lf->lfStrikeOut = mfp->strikeout;
\r
1185 lf->lfCharSet = mfp->charset;
\r
1186 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1187 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1188 lf->lfQuality = DEFAULT_QUALITY;
\r
1189 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1190 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1194 CreateFontInMF(MyFont *mf)
\r
1196 LFfromMFP(&mf->lf, &mf->mfp);
\r
1197 if (mf->hf) DeleteObject(mf->hf);
\r
1198 mf->hf = CreateFontIndirect(&mf->lf);
\r
1201 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1203 colorVariable[] = {
\r
1204 &whitePieceColor,
\r
1205 &blackPieceColor,
\r
1206 &lightSquareColor,
\r
1207 &darkSquareColor,
\r
1208 &highlightSquareColor,
\r
1209 &premoveHighlightColor,
\r
1211 &consoleBackgroundColor,
\r
1212 &appData.fontForeColorWhite,
\r
1213 &appData.fontBackColorWhite,
\r
1214 &appData.fontForeColorBlack,
\r
1215 &appData.fontBackColorBlack,
\r
1216 &appData.evalHistColorWhite,
\r
1217 &appData.evalHistColorBlack,
\r
1218 &appData.highlightArrowColor,
\r
1221 /* Command line font name parser. NULL name means do nothing.
\r
1222 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1223 For backward compatibility, syntax without the colon is also
\r
1224 accepted, but font names with digits in them won't work in that case.
\r
1227 ParseFontName(char *name, MyFontParams *mfp)
\r
1230 if (name == NULL) return;
\r
1232 q = strchr(p, ':');
\r
1234 if (q - p >= sizeof(mfp->faceName))
\r
1235 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1236 memcpy(mfp->faceName, p, q - p);
\r
1237 mfp->faceName[q - p] = NULLCHAR;
\r
1240 q = mfp->faceName;
\r
1241 while (*p && !isdigit(*p)) {
\r
1243 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1244 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1246 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1249 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1250 mfp->pointSize = (float) atof(p);
\r
1251 mfp->bold = (strchr(p, 'b') != NULL);
\r
1252 mfp->italic = (strchr(p, 'i') != NULL);
\r
1253 mfp->underline = (strchr(p, 'u') != NULL);
\r
1254 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1255 mfp->charset = DEFAULT_CHARSET;
\r
1256 q = strchr(p, 'c');
\r
1258 mfp->charset = (BYTE) atoi(q+1);
\r
1262 ParseFont(char *name, int number)
\r
1263 { // wrapper to shield back-end from 'font'
\r
1264 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1269 { // in WB we have a 2D array of fonts; this initializes their description
\r
1271 /* Point font array elements to structures and
\r
1272 parse default font names */
\r
1273 for (i=0; i<NUM_FONTS; i++) {
\r
1274 for (j=0; j<NUM_SIZES; j++) {
\r
1275 font[j][i] = &fontRec[j][i];
\r
1276 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1283 { // here we create the actual fonts from the selected descriptions
\r
1285 for (i=0; i<NUM_FONTS; i++) {
\r
1286 for (j=0; j<NUM_SIZES; j++) {
\r
1287 CreateFontInMF(font[j][i]);
\r
1291 /* Color name parser.
\r
1292 X version accepts X color names, but this one
\r
1293 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1295 ParseColorName(char *name)
\r
1297 int red, green, blue, count;
\r
1298 char buf[MSG_SIZ];
\r
1300 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1302 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1303 &red, &green, &blue);
\r
1306 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1307 DisplayError(buf, 0);
\r
1308 return RGB(0, 0, 0);
\r
1310 return PALETTERGB(red, green, blue);
\r
1314 ParseColor(int n, char *name)
\r
1315 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1316 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1320 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1322 char *e = argValue;
\r
1326 if (*e == 'b') eff |= CFE_BOLD;
\r
1327 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1328 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1329 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1330 else if (*e == '#' || isdigit(*e)) break;
\r
1334 *color = ParseColorName(e);
\r
1338 ParseTextAttribs(ColorClass cc, char *s)
\r
1339 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1340 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1341 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1345 ParseBoardSize(void *addr, char *name)
\r
1346 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1347 BoardSize bs = SizeTiny;
\r
1348 while (sizeInfo[bs].name != NULL) {
\r
1349 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1350 *(BoardSize *)addr = bs;
\r
1355 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1360 { // [HGM] import name from appData first
\r
1363 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1364 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1365 textAttribs[cc].sound.data = NULL;
\r
1366 MyLoadSound(&textAttribs[cc].sound);
\r
1368 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1369 textAttribs[cc].sound.name = strdup("");
\r
1370 textAttribs[cc].sound.data = NULL;
\r
1372 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1373 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1374 sounds[sc].data = NULL;
\r
1375 MyLoadSound(&sounds[sc]);
\r
1380 SetCommPortDefaults()
\r
1382 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1383 dcb.DCBlength = sizeof(DCB);
\r
1384 dcb.BaudRate = 9600;
\r
1385 dcb.fBinary = TRUE;
\r
1386 dcb.fParity = FALSE;
\r
1387 dcb.fOutxCtsFlow = FALSE;
\r
1388 dcb.fOutxDsrFlow = FALSE;
\r
1389 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1390 dcb.fDsrSensitivity = FALSE;
\r
1391 dcb.fTXContinueOnXoff = TRUE;
\r
1392 dcb.fOutX = FALSE;
\r
1394 dcb.fNull = FALSE;
\r
1395 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1396 dcb.fAbortOnError = FALSE;
\r
1398 dcb.Parity = SPACEPARITY;
\r
1399 dcb.StopBits = ONESTOPBIT;
\r
1402 // [HGM] args: these three cases taken out to stay in front-end
\r
1404 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1405 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1406 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1407 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1409 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1410 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1411 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1412 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1413 ad->argName, mfp->faceName, mfp->pointSize,
\r
1414 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1415 mfp->bold ? "b" : "",
\r
1416 mfp->italic ? "i" : "",
\r
1417 mfp->underline ? "u" : "",
\r
1418 mfp->strikeout ? "s" : "",
\r
1419 (int)mfp->charset);
\r
1425 { // [HGM] copy the names from the internal WB variables to appData
\r
1428 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1429 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1430 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1431 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1435 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1436 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1437 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1438 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1439 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1440 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1441 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1442 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1443 (ta->effects) ? " " : "",
\r
1444 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1448 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1449 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1450 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1451 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1452 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1456 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1457 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1458 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1462 ParseCommPortSettings(char *s)
\r
1463 { // wrapper to keep dcb from back-end
\r
1464 ParseCommSettings(s, &dcb);
\r
1469 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1470 GetActualPlacement(hwndMain, &wpMain);
\r
1471 GetActualPlacement(hwndConsole, &wpConsole);
\r
1472 GetActualPlacement(commentDialog, &wpComment);
\r
1473 GetActualPlacement(editTagsDialog, &wpTags);
\r
1474 GetActualPlacement(gameListDialog, &wpGameList);
\r
1475 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1476 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1477 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1481 PrintCommPortSettings(FILE *f, char *name)
\r
1482 { // wrapper to shield back-end from DCB
\r
1483 PrintCommSettings(f, name, &dcb);
\r
1487 MySearchPath(char *installDir, char *name, char *fullname)
\r
1489 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1490 if(name[0]== '%') {
\r
1491 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1492 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1493 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1494 *strchr(buf, '%') = 0;
\r
1495 strcat(fullname, getenv(buf));
\r
1496 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1498 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1499 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1500 return (int) strlen(fullname);
\r
1502 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1506 MyGetFullPathName(char *name, char *fullname)
\r
1509 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1514 { // [HGM] args: allows testing if main window is realized from back-end
\r
1515 return hwndMain != NULL;
\r
1519 PopUpStartupDialog()
\r
1523 LoadLanguageFile(appData.language);
\r
1524 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1525 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1526 FreeProcInstance(lpProc);
\r
1529 /*---------------------------------------------------------------------------*\
\r
1531 * GDI board drawing routines
\r
1533 \*---------------------------------------------------------------------------*/
\r
1535 /* [AS] Draw square using background texture */
\r
1536 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1541 return; /* Should never happen! */
\r
1544 SetGraphicsMode( dst, GM_ADVANCED );
\r
1551 /* X reflection */
\r
1556 x.eDx = (FLOAT) dw + dx - 1;
\r
1559 SetWorldTransform( dst, &x );
\r
1562 /* Y reflection */
\r
1568 x.eDy = (FLOAT) dh + dy - 1;
\r
1570 SetWorldTransform( dst, &x );
\r
1578 x.eDx = (FLOAT) dx;
\r
1579 x.eDy = (FLOAT) dy;
\r
1582 SetWorldTransform( dst, &x );
\r
1586 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1594 SetWorldTransform( dst, &x );
\r
1596 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1599 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1601 PM_WP = (int) WhitePawn,
\r
1602 PM_WN = (int) WhiteKnight,
\r
1603 PM_WB = (int) WhiteBishop,
\r
1604 PM_WR = (int) WhiteRook,
\r
1605 PM_WQ = (int) WhiteQueen,
\r
1606 PM_WF = (int) WhiteFerz,
\r
1607 PM_WW = (int) WhiteWazir,
\r
1608 PM_WE = (int) WhiteAlfil,
\r
1609 PM_WM = (int) WhiteMan,
\r
1610 PM_WO = (int) WhiteCannon,
\r
1611 PM_WU = (int) WhiteUnicorn,
\r
1612 PM_WH = (int) WhiteNightrider,
\r
1613 PM_WA = (int) WhiteAngel,
\r
1614 PM_WC = (int) WhiteMarshall,
\r
1615 PM_WAB = (int) WhiteCardinal,
\r
1616 PM_WD = (int) WhiteDragon,
\r
1617 PM_WL = (int) WhiteLance,
\r
1618 PM_WS = (int) WhiteCobra,
\r
1619 PM_WV = (int) WhiteFalcon,
\r
1620 PM_WSG = (int) WhiteSilver,
\r
1621 PM_WG = (int) WhiteGrasshopper,
\r
1622 PM_WK = (int) WhiteKing,
\r
1623 PM_BP = (int) BlackPawn,
\r
1624 PM_BN = (int) BlackKnight,
\r
1625 PM_BB = (int) BlackBishop,
\r
1626 PM_BR = (int) BlackRook,
\r
1627 PM_BQ = (int) BlackQueen,
\r
1628 PM_BF = (int) BlackFerz,
\r
1629 PM_BW = (int) BlackWazir,
\r
1630 PM_BE = (int) BlackAlfil,
\r
1631 PM_BM = (int) BlackMan,
\r
1632 PM_BO = (int) BlackCannon,
\r
1633 PM_BU = (int) BlackUnicorn,
\r
1634 PM_BH = (int) BlackNightrider,
\r
1635 PM_BA = (int) BlackAngel,
\r
1636 PM_BC = (int) BlackMarshall,
\r
1637 PM_BG = (int) BlackGrasshopper,
\r
1638 PM_BAB = (int) BlackCardinal,
\r
1639 PM_BD = (int) BlackDragon,
\r
1640 PM_BL = (int) BlackLance,
\r
1641 PM_BS = (int) BlackCobra,
\r
1642 PM_BV = (int) BlackFalcon,
\r
1643 PM_BSG = (int) BlackSilver,
\r
1644 PM_BK = (int) BlackKing
\r
1647 static HFONT hPieceFont = NULL;
\r
1648 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1649 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1650 static int fontBitmapSquareSize = 0;
\r
1651 static char pieceToFontChar[(int) EmptySquare] =
\r
1652 { 'p', 'n', 'b', 'r', 'q',
\r
1653 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1654 'k', 'o', 'm', 'v', 't', 'w',
\r
1655 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1658 extern BOOL SetCharTable( char *table, const char * map );
\r
1659 /* [HGM] moved to backend.c */
\r
1661 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1664 BYTE r1 = GetRValue( color );
\r
1665 BYTE g1 = GetGValue( color );
\r
1666 BYTE b1 = GetBValue( color );
\r
1672 /* Create a uniform background first */
\r
1673 hbrush = CreateSolidBrush( color );
\r
1674 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1675 FillRect( hdc, &rc, hbrush );
\r
1676 DeleteObject( hbrush );
\r
1679 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1680 int steps = squareSize / 2;
\r
1683 for( i=0; i<steps; i++ ) {
\r
1684 BYTE r = r1 - (r1-r2) * i / steps;
\r
1685 BYTE g = g1 - (g1-g2) * i / steps;
\r
1686 BYTE b = b1 - (b1-b2) * i / steps;
\r
1688 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1689 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1690 FillRect( hdc, &rc, hbrush );
\r
1691 DeleteObject(hbrush);
\r
1694 else if( mode == 2 ) {
\r
1695 /* Diagonal gradient, good more or less for every piece */
\r
1696 POINT triangle[3];
\r
1697 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1698 HBRUSH hbrush_old;
\r
1699 int steps = squareSize;
\r
1702 triangle[0].x = squareSize - steps;
\r
1703 triangle[0].y = squareSize;
\r
1704 triangle[1].x = squareSize;
\r
1705 triangle[1].y = squareSize;
\r
1706 triangle[2].x = squareSize;
\r
1707 triangle[2].y = squareSize - steps;
\r
1709 for( i=0; i<steps; i++ ) {
\r
1710 BYTE r = r1 - (r1-r2) * i / steps;
\r
1711 BYTE g = g1 - (g1-g2) * i / steps;
\r
1712 BYTE b = b1 - (b1-b2) * i / steps;
\r
1714 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1715 hbrush_old = SelectObject( hdc, hbrush );
\r
1716 Polygon( hdc, triangle, 3 );
\r
1717 SelectObject( hdc, hbrush_old );
\r
1718 DeleteObject(hbrush);
\r
1723 SelectObject( hdc, hpen );
\r
1728 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1729 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1730 piece: follow the steps as explained below.
\r
1732 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1736 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1740 int backColor = whitePieceColor;
\r
1741 int foreColor = blackPieceColor;
\r
1743 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1744 backColor = appData.fontBackColorWhite;
\r
1745 foreColor = appData.fontForeColorWhite;
\r
1747 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1748 backColor = appData.fontBackColorBlack;
\r
1749 foreColor = appData.fontForeColorBlack;
\r
1753 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1755 hbm_old = SelectObject( hdc, hbm );
\r
1759 rc.right = squareSize;
\r
1760 rc.bottom = squareSize;
\r
1762 /* Step 1: background is now black */
\r
1763 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1765 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1767 pt.x = (squareSize - sz.cx) / 2;
\r
1768 pt.y = (squareSize - sz.cy) / 2;
\r
1770 SetBkMode( hdc, TRANSPARENT );
\r
1771 SetTextColor( hdc, chroma );
\r
1772 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1773 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1775 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1776 /* Step 3: the area outside the piece is filled with white */
\r
1777 // FloodFill( hdc, 0, 0, chroma );
\r
1778 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1779 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1780 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1781 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1782 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1784 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1785 but if the start point is not inside the piece we're lost!
\r
1786 There should be a better way to do this... if we could create a region or path
\r
1787 from the fill operation we would be fine for example.
\r
1789 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1790 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1792 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1793 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1794 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1796 SelectObject( dc2, bm2 );
\r
1797 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1798 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1799 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1800 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1801 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1804 DeleteObject( bm2 );
\r
1807 SetTextColor( hdc, 0 );
\r
1809 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1810 draw the piece again in black for safety.
\r
1812 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1814 SelectObject( hdc, hbm_old );
\r
1816 if( hPieceMask[index] != NULL ) {
\r
1817 DeleteObject( hPieceMask[index] );
\r
1820 hPieceMask[index] = hbm;
\r
1823 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1825 SelectObject( hdc, hbm );
\r
1828 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1829 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1830 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1832 SelectObject( dc1, hPieceMask[index] );
\r
1833 SelectObject( dc2, bm2 );
\r
1834 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1835 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1838 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1839 the piece background and deletes (makes transparent) the rest.
\r
1840 Thanks to that mask, we are free to paint the background with the greates
\r
1841 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1842 We use this, to make gradients and give the pieces a "roundish" look.
\r
1844 SetPieceBackground( hdc, backColor, 2 );
\r
1845 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1849 DeleteObject( bm2 );
\r
1852 SetTextColor( hdc, foreColor );
\r
1853 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1855 SelectObject( hdc, hbm_old );
\r
1857 if( hPieceFace[index] != NULL ) {
\r
1858 DeleteObject( hPieceFace[index] );
\r
1861 hPieceFace[index] = hbm;
\r
1864 static int TranslatePieceToFontPiece( int piece )
\r
1894 case BlackMarshall:
\r
1898 case BlackNightrider:
\r
1904 case BlackUnicorn:
\r
1908 case BlackGrasshopper:
\r
1920 case BlackCardinal:
\r
1927 case WhiteMarshall:
\r
1931 case WhiteNightrider:
\r
1937 case WhiteUnicorn:
\r
1941 case WhiteGrasshopper:
\r
1953 case WhiteCardinal:
\r
1962 void CreatePiecesFromFont()
\r
1965 HDC hdc_window = NULL;
\r
1971 if( fontBitmapSquareSize < 0 ) {
\r
1972 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1976 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1977 fontBitmapSquareSize = -1;
\r
1981 if( fontBitmapSquareSize != squareSize ) {
\r
1982 hdc_window = GetDC( hwndMain );
\r
1983 hdc = CreateCompatibleDC( hdc_window );
\r
1985 if( hPieceFont != NULL ) {
\r
1986 DeleteObject( hPieceFont );
\r
1989 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1990 hPieceMask[i] = NULL;
\r
1991 hPieceFace[i] = NULL;
\r
1997 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1998 fontHeight = appData.fontPieceSize;
\r
2001 fontHeight = (fontHeight * squareSize) / 100;
\r
2003 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2005 lf.lfEscapement = 0;
\r
2006 lf.lfOrientation = 0;
\r
2007 lf.lfWeight = FW_NORMAL;
\r
2009 lf.lfUnderline = 0;
\r
2010 lf.lfStrikeOut = 0;
\r
2011 lf.lfCharSet = DEFAULT_CHARSET;
\r
2012 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2013 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2014 lf.lfQuality = PROOF_QUALITY;
\r
2015 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2016 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2017 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2019 hPieceFont = CreateFontIndirect( &lf );
\r
2021 if( hPieceFont == NULL ) {
\r
2022 fontBitmapSquareSize = -2;
\r
2025 /* Setup font-to-piece character table */
\r
2026 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2027 /* No (or wrong) global settings, try to detect the font */
\r
2028 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2030 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2032 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2033 /* DiagramTT* family */
\r
2034 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2036 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2037 /* Fairy symbols */
\r
2038 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2040 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2041 /* Good Companion (Some characters get warped as literal :-( */
\r
2042 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2043 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2044 SetCharTable(pieceToFontChar, s);
\r
2047 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2048 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2052 /* Create bitmaps */
\r
2053 hfont_old = SelectObject( hdc, hPieceFont );
\r
2054 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2055 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2056 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2058 SelectObject( hdc, hfont_old );
\r
2060 fontBitmapSquareSize = squareSize;
\r
2064 if( hdc != NULL ) {
\r
2068 if( hdc_window != NULL ) {
\r
2069 ReleaseDC( hwndMain, hdc_window );
\r
2074 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2078 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2079 if (gameInfo.event &&
\r
2080 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2081 strcmp(name, "k80s") == 0) {
\r
2082 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2084 return LoadBitmap(hinst, name);
\r
2088 /* Insert a color into the program's logical palette
\r
2089 structure. This code assumes the given color is
\r
2090 the result of the RGB or PALETTERGB macro, and it
\r
2091 knows how those macros work (which is documented).
\r
2094 InsertInPalette(COLORREF color)
\r
2096 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2098 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2099 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2100 pLogPal->palNumEntries--;
\r
2104 pe->peFlags = (char) 0;
\r
2105 pe->peRed = (char) (0xFF & color);
\r
2106 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2107 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2113 InitDrawingColors()
\r
2115 if (pLogPal == NULL) {
\r
2116 /* Allocate enough memory for a logical palette with
\r
2117 * PALETTESIZE entries and set the size and version fields
\r
2118 * of the logical palette structure.
\r
2120 pLogPal = (NPLOGPALETTE)
\r
2121 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2122 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2123 pLogPal->palVersion = 0x300;
\r
2125 pLogPal->palNumEntries = 0;
\r
2127 InsertInPalette(lightSquareColor);
\r
2128 InsertInPalette(darkSquareColor);
\r
2129 InsertInPalette(whitePieceColor);
\r
2130 InsertInPalette(blackPieceColor);
\r
2131 InsertInPalette(highlightSquareColor);
\r
2132 InsertInPalette(premoveHighlightColor);
\r
2134 /* create a logical color palette according the information
\r
2135 * in the LOGPALETTE structure.
\r
2137 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2139 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2140 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2141 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2142 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2143 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2144 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2145 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2146 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2147 /* [AS] Force rendering of the font-based pieces */
\r
2148 if( fontBitmapSquareSize > 0 ) {
\r
2149 fontBitmapSquareSize = 0;
\r
2155 BoardWidth(int boardSize, int n)
\r
2156 { /* [HGM] argument n added to allow different width and height */
\r
2157 int lineGap = sizeInfo[boardSize].lineGap;
\r
2159 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2160 lineGap = appData.overrideLineGap;
\r
2163 return (n + 1) * lineGap +
\r
2164 n * sizeInfo[boardSize].squareSize;
\r
2167 /* Respond to board resize by dragging edge */
\r
2169 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2171 BoardSize newSize = NUM_SIZES - 1;
\r
2172 static int recurse = 0;
\r
2173 if (IsIconic(hwndMain)) return;
\r
2174 if (recurse > 0) return;
\r
2176 while (newSize > 0) {
\r
2177 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2178 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2179 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2182 boardSize = newSize;
\r
2183 InitDrawingSizes(boardSize, flags);
\r
2188 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2191 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2193 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2194 ChessSquare piece;
\r
2195 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2197 SIZE clockSize, messageSize;
\r
2199 char buf[MSG_SIZ];
\r
2201 HMENU hmenu = GetMenu(hwndMain);
\r
2202 RECT crect, wrect, oldRect;
\r
2204 LOGBRUSH logbrush;
\r
2206 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2207 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2209 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2210 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2212 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2213 oldRect.top = wpMain.y;
\r
2214 oldRect.right = wpMain.x + wpMain.width;
\r
2215 oldRect.bottom = wpMain.y + wpMain.height;
\r
2217 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2218 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2219 squareSize = sizeInfo[boardSize].squareSize;
\r
2220 lineGap = sizeInfo[boardSize].lineGap;
\r
2221 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2223 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2224 lineGap = appData.overrideLineGap;
\r
2227 if (tinyLayout != oldTinyLayout) {
\r
2228 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2230 style &= ~WS_SYSMENU;
\r
2231 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2232 "&Minimize\tCtrl+F4");
\r
2234 style |= WS_SYSMENU;
\r
2235 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2237 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2239 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2240 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2241 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2243 DrawMenuBar(hwndMain);
\r
2246 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2247 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2249 /* Get text area sizes */
\r
2250 hdc = GetDC(hwndMain);
\r
2251 if (appData.clockMode) {
\r
2252 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2254 snprintf(buf, MSG_SIZ, _("White"));
\r
2256 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2257 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2258 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2259 str = _("We only care about the height here");
\r
2260 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2261 SelectObject(hdc, oldFont);
\r
2262 ReleaseDC(hwndMain, hdc);
\r
2264 /* Compute where everything goes */
\r
2265 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2266 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2267 logoHeight = 2*clockSize.cy;
\r
2268 leftLogoRect.left = OUTER_MARGIN;
\r
2269 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2270 leftLogoRect.top = OUTER_MARGIN;
\r
2271 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2273 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2274 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2275 rightLogoRect.top = OUTER_MARGIN;
\r
2276 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2279 whiteRect.left = leftLogoRect.right;
\r
2280 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2281 whiteRect.top = OUTER_MARGIN;
\r
2282 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2284 blackRect.right = rightLogoRect.left;
\r
2285 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2286 blackRect.top = whiteRect.top;
\r
2287 blackRect.bottom = whiteRect.bottom;
\r
2289 whiteRect.left = OUTER_MARGIN;
\r
2290 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2291 whiteRect.top = OUTER_MARGIN;
\r
2292 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2294 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2295 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2296 blackRect.top = whiteRect.top;
\r
2297 blackRect.bottom = whiteRect.bottom;
\r
2299 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2302 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2303 if (appData.showButtonBar) {
\r
2304 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2305 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2307 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2309 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2310 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2312 boardRect.left = OUTER_MARGIN;
\r
2313 boardRect.right = boardRect.left + boardWidth;
\r
2314 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2315 boardRect.bottom = boardRect.top + boardHeight;
\r
2317 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2318 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2319 oldBoardSize = boardSize;
\r
2320 oldTinyLayout = tinyLayout;
\r
2321 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2322 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2323 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2324 winW *= 1 + twoBoards;
\r
2325 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2326 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2327 wpMain.height = winH; // without disturbing window attachments
\r
2328 GetWindowRect(hwndMain, &wrect);
\r
2329 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2330 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2332 // [HGM] placement: let attached windows follow size change.
\r
2333 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2334 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2335 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2336 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2337 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2339 /* compensate if menu bar wrapped */
\r
2340 GetClientRect(hwndMain, &crect);
\r
2341 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2342 wpMain.height += offby;
\r
2344 case WMSZ_TOPLEFT:
\r
2345 SetWindowPos(hwndMain, NULL,
\r
2346 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2347 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2350 case WMSZ_TOPRIGHT:
\r
2352 SetWindowPos(hwndMain, NULL,
\r
2353 wrect.left, wrect.bottom - wpMain.height,
\r
2354 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2357 case WMSZ_BOTTOMLEFT:
\r
2359 SetWindowPos(hwndMain, NULL,
\r
2360 wrect.right - wpMain.width, wrect.top,
\r
2361 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2364 case WMSZ_BOTTOMRIGHT:
\r
2368 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2369 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2374 for (i = 0; i < N_BUTTONS; i++) {
\r
2375 if (buttonDesc[i].hwnd != NULL) {
\r
2376 DestroyWindow(buttonDesc[i].hwnd);
\r
2377 buttonDesc[i].hwnd = NULL;
\r
2379 if (appData.showButtonBar) {
\r
2380 buttonDesc[i].hwnd =
\r
2381 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2382 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2383 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2384 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2385 (HMENU) buttonDesc[i].id,
\r
2386 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2388 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2389 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2390 MAKELPARAM(FALSE, 0));
\r
2392 if (buttonDesc[i].id == IDM_Pause)
\r
2393 hwndPause = buttonDesc[i].hwnd;
\r
2394 buttonDesc[i].wndproc = (WNDPROC)
\r
2395 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2398 if (gridPen != NULL) DeleteObject(gridPen);
\r
2399 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2400 if (premovePen != NULL) DeleteObject(premovePen);
\r
2401 if (lineGap != 0) {
\r
2402 logbrush.lbStyle = BS_SOLID;
\r
2403 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2405 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2406 lineGap, &logbrush, 0, NULL);
\r
2407 logbrush.lbColor = highlightSquareColor;
\r
2409 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2410 lineGap, &logbrush, 0, NULL);
\r
2412 logbrush.lbColor = premoveHighlightColor;
\r
2414 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2415 lineGap, &logbrush, 0, NULL);
\r
2417 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2418 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2419 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2420 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2421 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2422 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2423 BOARD_WIDTH * (squareSize + lineGap);
\r
2424 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2426 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2427 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2428 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2429 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2430 lineGap / 2 + (i * (squareSize + lineGap));
\r
2431 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2432 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2433 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2437 /* [HGM] Licensing requirement */
\r
2439 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2442 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2444 GothicPopUp( "", VariantNormal);
\r
2447 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2449 /* Load piece bitmaps for this board size */
\r
2450 for (i=0; i<=2; i++) {
\r
2451 for (piece = WhitePawn;
\r
2452 (int) piece < (int) BlackPawn;
\r
2453 piece = (ChessSquare) ((int) piece + 1)) {
\r
2454 if (pieceBitmap[i][piece] != NULL)
\r
2455 DeleteObject(pieceBitmap[i][piece]);
\r
2459 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2460 // Orthodox Chess pieces
\r
2461 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2462 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2463 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2464 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2465 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2466 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2467 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2468 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2469 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2470 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2471 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2472 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2473 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2474 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2475 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2476 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2477 // in Shogi, Hijack the unused Queen for Lance
\r
2478 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2479 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2480 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2482 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2483 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2484 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2487 if(squareSize <= 72 && squareSize >= 33) {
\r
2488 /* A & C are available in most sizes now */
\r
2489 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2490 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2491 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2492 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2493 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2494 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2495 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2496 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2497 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2498 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2499 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2500 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2501 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2502 } else { // Smirf-like
\r
2503 if(gameInfo.variant == VariantSChess) {
\r
2504 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2505 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2506 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2508 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2509 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2510 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2513 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2514 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2515 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2516 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2517 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2518 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2519 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2520 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2521 } else { // WinBoard standard
\r
2522 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2523 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2524 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2529 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2530 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2531 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2532 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2533 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2534 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2535 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2536 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2537 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2538 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2539 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2542 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2545 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2548 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2561 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2562 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2563 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2564 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2565 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2566 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2567 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2568 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2569 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2570 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2571 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2575 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2576 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2577 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2578 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2579 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2580 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2581 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2582 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2583 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2584 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2585 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2586 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2589 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2590 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2591 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2592 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2593 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2594 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2595 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2596 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2597 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2598 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2599 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2600 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2601 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2602 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2603 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2607 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2608 /* special Shogi support in this size */
\r
2609 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2610 for (piece = WhitePawn;
\r
2611 (int) piece < (int) BlackPawn;
\r
2612 piece = (ChessSquare) ((int) piece + 1)) {
\r
2613 if (pieceBitmap[i][piece] != NULL)
\r
2614 DeleteObject(pieceBitmap[i][piece]);
\r
2617 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2618 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2619 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2620 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2621 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2622 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2623 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2624 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2625 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2626 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2627 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2628 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2629 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2630 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2631 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2632 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2633 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2634 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2635 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2636 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2637 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2638 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2639 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2640 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2641 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2642 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2643 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2644 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2645 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2646 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2647 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2648 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2649 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2650 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2651 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2652 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2653 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2654 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2657 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2658 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2664 PieceBitmap(ChessSquare p, int kind)
\r
2666 if ((int) p >= (int) BlackPawn)
\r
2667 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2669 return pieceBitmap[kind][(int) p];
\r
2672 /***************************************************************/
\r
2674 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2675 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2677 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2678 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2682 SquareToPos(int row, int column, int * x, int * y)
\r
2685 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2686 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2688 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2689 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2694 DrawCoordsOnDC(HDC hdc)
\r
2696 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2697 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2698 char str[2] = { NULLCHAR, NULLCHAR };
\r
2699 int oldMode, oldAlign, x, y, start, i;
\r
2703 if (!appData.showCoords)
\r
2706 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2708 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2709 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2710 oldAlign = GetTextAlign(hdc);
\r
2711 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2713 y = boardRect.top + lineGap;
\r
2714 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2716 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2717 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2718 str[0] = files[start + i];
\r
2719 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2720 y += squareSize + lineGap;
\r
2723 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2725 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2726 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2727 str[0] = ranks[start + i];
\r
2728 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2729 x += squareSize + lineGap;
\r
2732 SelectObject(hdc, oldBrush);
\r
2733 SetBkMode(hdc, oldMode);
\r
2734 SetTextAlign(hdc, oldAlign);
\r
2735 SelectObject(hdc, oldFont);
\r
2739 DrawGridOnDC(HDC hdc)
\r
2743 if (lineGap != 0) {
\r
2744 oldPen = SelectObject(hdc, gridPen);
\r
2745 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2746 SelectObject(hdc, oldPen);
\r
2750 #define HIGHLIGHT_PEN 0
\r
2751 #define PREMOVE_PEN 1
\r
2754 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2757 HPEN oldPen, hPen;
\r
2758 if (lineGap == 0) return;
\r
2760 x1 = boardRect.left +
\r
2761 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2762 y1 = boardRect.top +
\r
2763 lineGap/2 + y * (squareSize + lineGap);
\r
2765 x1 = boardRect.left +
\r
2766 lineGap/2 + x * (squareSize + lineGap);
\r
2767 y1 = boardRect.top +
\r
2768 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2770 hPen = pen ? premovePen : highlightPen;
\r
2771 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2772 MoveToEx(hdc, x1, y1, NULL);
\r
2773 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2774 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2775 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2776 LineTo(hdc, x1, y1);
\r
2777 SelectObject(hdc, oldPen);
\r
2781 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2784 for (i=0; i<2; i++) {
\r
2785 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2786 DrawHighlightOnDC(hdc, TRUE,
\r
2787 h->sq[i].x, h->sq[i].y,
\r
2792 /* Note: sqcolor is used only in monoMode */
\r
2793 /* Note that this code is largely duplicated in woptions.c,
\r
2794 function DrawSampleSquare, so that needs to be updated too */
\r
2796 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2798 HBITMAP oldBitmap;
\r
2802 if (appData.blindfold) return;
\r
2804 /* [AS] Use font-based pieces if needed */
\r
2805 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2806 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2807 CreatePiecesFromFont();
\r
2809 if( fontBitmapSquareSize == squareSize ) {
\r
2810 int index = TranslatePieceToFontPiece(piece);
\r
2812 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2814 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2815 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2819 squareSize, squareSize,
\r
2824 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2826 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2827 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2831 squareSize, squareSize,
\r
2840 if (appData.monoMode) {
\r
2841 SelectObject(tmphdc, PieceBitmap(piece,
\r
2842 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2843 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2844 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2846 tmpSize = squareSize;
\r
2848 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2849 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2850 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2851 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2852 x += (squareSize - minorSize)>>1;
\r
2853 y += squareSize - minorSize - 2;
\r
2854 tmpSize = minorSize;
\r
2856 if (color || appData.allWhite ) {
\r
2857 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2859 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2860 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2861 if(appData.upsideDown && color==flipView)
\r
2862 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2864 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2865 /* Use black for outline of white pieces */
\r
2866 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2867 if(appData.upsideDown && color==flipView)
\r
2868 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2870 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2872 /* Use square color for details of black pieces */
\r
2873 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2874 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2875 if(appData.upsideDown && !flipView)
\r
2876 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2878 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2880 SelectObject(hdc, oldBrush);
\r
2881 SelectObject(tmphdc, oldBitmap);
\r
2885 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2886 int GetBackTextureMode( int algo )
\r
2888 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2892 case BACK_TEXTURE_MODE_PLAIN:
\r
2893 result = 1; /* Always use identity map */
\r
2895 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2896 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2904 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2905 to handle redraws cleanly (as random numbers would always be different).
\r
2907 VOID RebuildTextureSquareInfo()
\r
2917 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2919 if( liteBackTexture != NULL ) {
\r
2920 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2921 lite_w = bi.bmWidth;
\r
2922 lite_h = bi.bmHeight;
\r
2926 if( darkBackTexture != NULL ) {
\r
2927 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2928 dark_w = bi.bmWidth;
\r
2929 dark_h = bi.bmHeight;
\r
2933 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2934 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2935 if( (col + row) & 1 ) {
\r
2937 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2938 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2939 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2941 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2942 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2943 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2945 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2946 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2951 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2952 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2953 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2955 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2956 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2957 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2959 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2960 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2967 /* [AS] Arrow highlighting support */
\r
2969 static double A_WIDTH = 5; /* Width of arrow body */
\r
2971 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2972 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2974 static double Sqr( double x )
\r
2979 static int Round( double x )
\r
2981 return (int) (x + 0.5);
\r
2984 /* Draw an arrow between two points using current settings */
\r
2985 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2988 double dx, dy, j, k, x, y;
\r
2990 if( d_x == s_x ) {
\r
2991 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2993 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
2996 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
2997 arrow[1].y = d_y - h;
\r
2999 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3000 arrow[2].y = d_y - h;
\r
3005 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3006 arrow[5].y = d_y - h;
\r
3008 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3009 arrow[4].y = d_y - h;
\r
3011 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3014 else if( d_y == s_y ) {
\r
3015 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3018 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3020 arrow[1].x = d_x - w;
\r
3021 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3023 arrow[2].x = d_x - w;
\r
3024 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3029 arrow[5].x = d_x - w;
\r
3030 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3032 arrow[4].x = d_x - w;
\r
3033 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3036 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3039 /* [AS] Needed a lot of paper for this! :-) */
\r
3040 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3041 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3043 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3045 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3050 arrow[0].x = Round(x - j);
\r
3051 arrow[0].y = Round(y + j*dx);
\r
3053 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3054 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3057 x = (double) d_x - k;
\r
3058 y = (double) d_y - k*dy;
\r
3061 x = (double) d_x + k;
\r
3062 y = (double) d_y + k*dy;
\r
3065 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3067 arrow[6].x = Round(x - j);
\r
3068 arrow[6].y = Round(y + j*dx);
\r
3070 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3071 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3073 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3074 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3079 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3080 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3083 Polygon( hdc, arrow, 7 );
\r
3086 /* [AS] Draw an arrow between two squares */
\r
3087 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3089 int s_x, s_y, d_x, d_y;
\r
3096 if( s_col == d_col && s_row == d_row ) {
\r
3100 /* Get source and destination points */
\r
3101 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3102 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3105 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3107 else if( d_y < s_y ) {
\r
3108 d_y += squareSize / 2 + squareSize / 4;
\r
3111 d_y += squareSize / 2;
\r
3115 d_x += squareSize / 2 - squareSize / 4;
\r
3117 else if( d_x < s_x ) {
\r
3118 d_x += squareSize / 2 + squareSize / 4;
\r
3121 d_x += squareSize / 2;
\r
3124 s_x += squareSize / 2;
\r
3125 s_y += squareSize / 2;
\r
3127 /* Adjust width */
\r
3128 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3131 stLB.lbStyle = BS_SOLID;
\r
3132 stLB.lbColor = appData.highlightArrowColor;
\r
3135 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3136 holdpen = SelectObject( hdc, hpen );
\r
3137 hbrush = CreateBrushIndirect( &stLB );
\r
3138 holdbrush = SelectObject( hdc, hbrush );
\r
3140 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3142 SelectObject( hdc, holdpen );
\r
3143 SelectObject( hdc, holdbrush );
\r
3144 DeleteObject( hpen );
\r
3145 DeleteObject( hbrush );
\r
3148 BOOL HasHighlightInfo()
\r
3150 BOOL result = FALSE;
\r
3152 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3153 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3161 BOOL IsDrawArrowEnabled()
\r
3163 BOOL result = FALSE;
\r
3165 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3172 VOID DrawArrowHighlight( HDC hdc )
\r
3174 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3175 DrawArrowBetweenSquares( hdc,
\r
3176 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3177 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3181 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3183 HRGN result = NULL;
\r
3185 if( HasHighlightInfo() ) {
\r
3186 int x1, y1, x2, y2;
\r
3187 int sx, sy, dx, dy;
\r
3189 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3190 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3192 sx = MIN( x1, x2 );
\r
3193 sy = MIN( y1, y2 );
\r
3194 dx = MAX( x1, x2 ) + squareSize;
\r
3195 dy = MAX( y1, y2 ) + squareSize;
\r
3197 result = CreateRectRgn( sx, sy, dx, dy );
\r