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[][41 ] = {
\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,
\r
257 OPT_elo1, OPT_elo2, OPT_date, IDOK, IDCANCEL },
\r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
259 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
262 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
265 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
266 { ABOUTBOX2, IDC_ChessBoard },
\r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
268 OPT_GameListClose, IDC_GameListDoFilter },
\r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
270 { DLG_Error, IDOK },
\r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
272 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
275 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
276 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
277 { DLG_IndexNumber, IDC_Index },
\r
278 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
279 { DLG_TypeInName, IDOK, IDCANCEL },
\r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
281 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
283 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
284 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
285 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
286 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
287 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
288 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
290 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
291 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
292 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
293 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
294 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
295 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
296 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
297 GPB_General, GPB_Alarm },
\r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
299 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
300 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
301 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
302 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
303 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
304 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
305 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont },
\r
306 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
307 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
308 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
309 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
310 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
311 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
312 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
313 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
314 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
315 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
316 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
317 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
318 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
319 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
320 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
321 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
322 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
323 { DLG_MoveHistory },
\r
324 { DLG_EvalGraph },
\r
325 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
326 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
327 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
328 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
329 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
330 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
331 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
332 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
333 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
337 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
338 static int lastChecked;
\r
339 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
340 extern int tinyLayout;
\r
341 extern char * menuBarText[][10];
\r
344 LoadLanguageFile(char *name)
\r
345 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
347 int i=0, j=0, n=0, k;
\r
350 if(!name || name[0] == NULLCHAR) return;
\r
351 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
352 appData.language = oldLanguage;
\r
353 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
354 if((f = fopen(buf, "r")) == NULL) return;
\r
355 while((k = fgetc(f)) != EOF) {
\r
356 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
357 languageBuf[i] = k;
\r
359 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
361 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
362 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
363 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
364 english[j] = languageBuf + n + 1; *p = 0;
\r
365 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
366 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
371 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
373 case 'n': k = '\n'; break;
\r
374 case 'r': k = '\r'; break;
\r
375 case 't': k = '\t'; break;
\r
377 languageBuf[--i] = k;
\r
382 barbaric = (j != 0);
\r
383 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
388 { // return the translation of the given string
\r
389 // efficiency can be improved a lot...
\r
391 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
392 if(!barbaric) return s;
\r
393 if(!s) return ""; // sanity
\r
394 while(english[i]) {
\r
395 if(!strcmp(s, english[i])) return foreign[i];
\r
402 Translate(HWND hDlg, int dialogID)
\r
403 { // translate all text items in the given dialog
\r
405 char buf[MSG_SIZ], *s;
\r
406 if(!barbaric) return;
\r
407 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
408 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
409 GetWindowText( hDlg, buf, MSG_SIZ );
\r
411 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
412 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
413 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
414 if(strlen(buf) == 0) continue;
\r
416 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
421 TranslateOneMenu(int i, HMENU subMenu)
\r
424 static MENUITEMINFO info;
\r
426 info.cbSize = sizeof(MENUITEMINFO);
\r
427 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
428 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
430 info.dwTypeData = buf;
\r
431 info.cch = sizeof(buf);
\r
432 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
434 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
435 else menuText[i][j] = strdup(buf); // remember original on first change
\r
437 if(buf[0] == NULLCHAR) continue;
\r
438 info.dwTypeData = T_(buf);
\r
439 info.cch = strlen(buf)+1;
\r
440 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
446 TranslateMenus(int addLanguage)
\r
449 WIN32_FIND_DATA fileData;
\r
451 #define IDM_English 1970
\r
453 HMENU mainMenu = GetMenu(hwndMain);
\r
454 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
455 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
456 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
457 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
458 TranslateOneMenu(i, subMenu);
\r
460 DrawMenuBar(hwndMain);
\r
463 if(!addLanguage) return;
\r
464 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
465 HMENU mainMenu = GetMenu(hwndMain);
\r
466 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
467 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
468 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
469 i = 0; lastChecked = IDM_English;
\r
471 char *p, *q = fileData.cFileName;
\r
472 int checkFlag = MF_UNCHECKED;
\r
473 languageFile[i] = strdup(q);
\r
474 if(barbaric && !strcmp(oldLanguage, q)) {
\r
475 checkFlag = MF_CHECKED;
\r
476 lastChecked = IDM_English + i + 1;
\r
477 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
479 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
480 p = strstr(fileData.cFileName, ".lng");
\r
482 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
483 } while(FindNextFile(hFind, &fileData));
\r
496 int cliWidth, cliHeight;
\r
499 SizeInfo sizeInfo[] =
\r
501 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
502 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
503 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
504 { "petite", 33, 1, 1, 1, 0, 0 },
\r
505 { "slim", 37, 2, 1, 0, 0, 0 },
\r
506 { "small", 40, 2, 1, 0, 0, 0 },
\r
507 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
508 { "middling", 49, 2, 0, 0, 0, 0 },
\r
509 { "average", 54, 2, 0, 0, 0, 0 },
\r
510 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
511 { "medium", 64, 3, 0, 0, 0, 0 },
\r
512 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
513 { "large", 80, 3, 0, 0, 0, 0 },
\r
514 { "big", 87, 3, 0, 0, 0, 0 },
\r
515 { "huge", 95, 3, 0, 0, 0, 0 },
\r
516 { "giant", 108, 3, 0, 0, 0, 0 },
\r
517 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
518 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
519 { NULL, 0, 0, 0, 0, 0, 0 }
\r
522 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
523 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
525 { 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
526 { 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
527 { 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
528 { 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
529 { 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
530 { 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
531 { 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
532 { 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
533 { 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
534 { 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
535 { 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
536 { 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
537 { 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
538 { 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
539 { 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
540 { 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
541 { 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
542 { 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
545 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
554 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
555 #define N_BUTTONS 5
\r
557 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
559 {"<<", IDM_ToStart, NULL, NULL},
\r
560 {"<", IDM_Backward, NULL, NULL},
\r
561 {"P", IDM_Pause, NULL, NULL},
\r
562 {">", IDM_Forward, NULL, NULL},
\r
563 {">>", IDM_ToEnd, NULL, NULL},
\r
566 int tinyLayout = 0, smallLayout = 0;
\r
567 #define MENU_BAR_ITEMS 9
\r
568 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
569 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
570 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
574 MySound sounds[(int)NSoundClasses];
\r
575 MyTextAttribs textAttribs[(int)NColorClasses];
\r
577 MyColorizeAttribs colorizeAttribs[] = {
\r
578 { (COLORREF)0, 0, N_("Shout Text") },
\r
579 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
580 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
581 { (COLORREF)0, 0, N_("Channel Text") },
\r
582 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
583 { (COLORREF)0, 0, N_("Tell Text") },
\r
584 { (COLORREF)0, 0, N_("Challenge Text") },
\r
585 { (COLORREF)0, 0, N_("Request Text") },
\r
586 { (COLORREF)0, 0, N_("Seek Text") },
\r
587 { (COLORREF)0, 0, N_("Normal Text") },
\r
588 { (COLORREF)0, 0, N_("None") }
\r
593 static char *commentTitle;
\r
594 static char *commentText;
\r
595 static int commentIndex;
\r
596 static Boolean editComment = FALSE;
\r
599 char errorTitle[MSG_SIZ];
\r
600 char errorMessage[2*MSG_SIZ];
\r
601 HWND errorDialog = NULL;
\r
602 BOOLEAN moveErrorMessageUp = FALSE;
\r
603 BOOLEAN consoleEcho = TRUE;
\r
604 CHARFORMAT consoleCF;
\r
605 COLORREF consoleBackgroundColor;
\r
607 char *programVersion;
\r
613 typedef int CPKind;
\r
622 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
625 #define INPUT_SOURCE_BUF_SIZE 4096
\r
627 typedef struct _InputSource {
\r
634 char buf[INPUT_SOURCE_BUF_SIZE];
\r
638 InputCallback func;
\r
639 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
643 InputSource *consoleInputSource;
\r
648 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
649 VOID ConsoleCreate();
\r
651 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
652 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
653 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
654 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
656 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
657 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
658 void ParseIcsTextMenu(char *icsTextMenuString);
\r
659 VOID PopUpNameDialog(char firstchar);
\r
660 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
664 int GameListOptions();
\r
666 int dummy; // [HGM] for obsolete args
\r
668 HWND hwndMain = NULL; /* root window*/
\r
669 HWND hwndConsole = NULL;
\r
670 HWND commentDialog = NULL;
\r
671 HWND moveHistoryDialog = NULL;
\r
672 HWND evalGraphDialog = NULL;
\r
673 HWND engineOutputDialog = NULL;
\r
674 HWND gameListDialog = NULL;
\r
675 HWND editTagsDialog = NULL;
\r
677 int commentUp = FALSE;
\r
679 WindowPlacement wpMain;
\r
680 WindowPlacement wpConsole;
\r
681 WindowPlacement wpComment;
\r
682 WindowPlacement wpMoveHistory;
\r
683 WindowPlacement wpEvalGraph;
\r
684 WindowPlacement wpEngineOutput;
\r
685 WindowPlacement wpGameList;
\r
686 WindowPlacement wpTags;
\r
688 VOID EngineOptionsPopup(); // [HGM] settings
\r
690 VOID GothicPopUp(char *title, VariantClass variant);
\r
692 * Setting "frozen" should disable all user input other than deleting
\r
693 * the window. We do this while engines are initializing themselves.
\r
695 static int frozen = 0;
\r
696 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
702 if (frozen) return;
\r
704 hmenu = GetMenu(hwndMain);
\r
705 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
706 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
708 DrawMenuBar(hwndMain);
\r
711 /* Undo a FreezeUI */
\r
717 if (!frozen) return;
\r
719 hmenu = GetMenu(hwndMain);
\r
720 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
721 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
723 DrawMenuBar(hwndMain);
\r
726 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
728 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
734 #define JAWS_ALT_INTERCEPT
\r
735 #define JAWS_KB_NAVIGATION
\r
736 #define JAWS_MENU_ITEMS
\r
737 #define JAWS_SILENCE
\r
738 #define JAWS_REPLAY
\r
740 #define JAWS_COPYRIGHT
\r
741 #define JAWS_DELETE(X) X
\r
742 #define SAYMACHINEMOVE()
\r
746 /*---------------------------------------------------------------------------*\
\r
750 \*---------------------------------------------------------------------------*/
\r
753 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
754 LPSTR lpCmdLine, int nCmdShow)
\r
757 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
758 // INITCOMMONCONTROLSEX ex;
\r
762 LoadLibrary("RICHED32.DLL");
\r
763 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
765 if (!InitApplication(hInstance)) {
\r
768 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
775 // InitCommonControlsEx(&ex);
\r
776 InitCommonControls();
\r
778 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
779 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
780 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
782 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
784 while (GetMessage(&msg, /* message structure */
\r
785 NULL, /* handle of window receiving the message */
\r
786 0, /* lowest message to examine */
\r
787 0)) /* highest message to examine */
\r
790 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
791 // [HGM] navigate: switch between all windows with tab
\r
792 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
793 int i, currentElement = 0;
\r
795 // first determine what element of the chain we come from (if any)
\r
796 if(appData.icsActive) {
\r
797 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
798 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
800 if(engineOutputDialog && EngineOutputIsUp()) {
\r
801 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
802 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
804 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
805 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
807 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
808 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
809 if(msg.hwnd == e1) currentElement = 2; else
\r
810 if(msg.hwnd == e2) currentElement = 3; else
\r
811 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
812 if(msg.hwnd == mh) currentElement = 4; else
\r
813 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
814 if(msg.hwnd == hText) currentElement = 5; else
\r
815 if(msg.hwnd == hInput) currentElement = 6; else
\r
816 for (i = 0; i < N_BUTTONS; i++) {
\r
817 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
820 // determine where to go to
\r
821 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
823 currentElement = (currentElement + direction) % 7;
\r
824 switch(currentElement) {
\r
826 h = hwndMain; break; // passing this case always makes the loop exit
\r
828 h = buttonDesc[0].hwnd; break; // could be NULL
\r
830 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
833 if(!EngineOutputIsUp()) continue;
\r
836 if(!MoveHistoryIsUp()) continue;
\r
838 // case 6: // input to eval graph does not seem to get here!
\r
839 // if(!EvalGraphIsUp()) continue;
\r
840 // h = evalGraphDialog; break;
\r
842 if(!appData.icsActive) continue;
\r
846 if(!appData.icsActive) continue;
\r
852 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
853 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
856 continue; // this message now has been processed
\r
860 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
861 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
862 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
863 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
864 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
865 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
866 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
867 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
868 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
869 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
870 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
871 for(i=0; i<MAX_CHAT; i++)
\r
872 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
875 if(done) continue; // [HGM] chat: end patch
\r
876 TranslateMessage(&msg); /* Translates virtual key codes */
\r
877 DispatchMessage(&msg); /* Dispatches message to window */
\r
882 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
885 /*---------------------------------------------------------------------------*\
\r
887 * Initialization functions
\r
889 \*---------------------------------------------------------------------------*/
\r
893 { // update user logo if necessary
\r
894 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
896 if(appData.autoLogo) {
\r
897 curName = UserName();
\r
898 if(strcmp(curName, oldUserName)) {
\r
899 GetCurrentDirectory(MSG_SIZ, dir);
\r
900 SetCurrentDirectory(installDir);
\r
901 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
902 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
903 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
904 if(userLogo == NULL)
\r
905 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
906 SetCurrentDirectory(dir); /* return to prev directory */
\r
912 InitApplication(HINSTANCE hInstance)
\r
916 /* Fill in window class structure with parameters that describe the */
\r
919 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
920 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
921 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
922 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
923 wc.hInstance = hInstance; /* Owner of this class */
\r
924 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
925 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
926 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
927 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
928 wc.lpszClassName = szAppName; /* Name to register as */
\r
930 /* Register the window class and return success/failure code. */
\r
931 if (!RegisterClass(&wc)) return FALSE;
\r
933 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
934 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
936 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
937 wc.hInstance = hInstance;
\r
938 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
939 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
940 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
941 wc.lpszMenuName = NULL;
\r
942 wc.lpszClassName = szConsoleName;
\r
944 if (!RegisterClass(&wc)) return FALSE;
\r
949 /* Set by InitInstance, used by EnsureOnScreen */
\r
950 int screenHeight, screenWidth;
\r
953 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
955 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
956 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
957 if (*x > screenWidth - 32) *x = 0;
\r
958 if (*y > screenHeight - 32) *y = 0;
\r
959 if (*x < minX) *x = minX;
\r
960 if (*y < minY) *y = minY;
\r
964 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
966 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
967 GetCurrentDirectory(MSG_SIZ, dir);
\r
968 SetCurrentDirectory(installDir);
\r
969 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
970 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
972 if (cps->programLogo == NULL && appData.debugMode) {
\r
973 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
975 } else if(appData.autoLogo) {
\r
976 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
977 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
978 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
980 if(appData.directory[n] && appData.directory[n][0]) {
\r
981 SetCurrentDirectory(appData.directory[n]);
\r
982 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
985 SetCurrentDirectory(dir); /* return to prev directory */
\r
991 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
992 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
994 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
995 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
996 liteBackTextureMode = appData.liteBackTextureMode;
\r
998 if (liteBackTexture == NULL && appData.debugMode) {
\r
999 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1003 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1004 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1005 darkBackTextureMode = appData.darkBackTextureMode;
\r
1007 if (darkBackTexture == NULL && appData.debugMode) {
\r
1008 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1014 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1016 HWND hwnd; /* Main window handle. */
\r
1018 WINDOWPLACEMENT wp;
\r
1021 hInst = hInstance; /* Store instance handle in our global variable */
\r
1022 programName = szAppName;
\r
1024 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1025 *filepart = NULLCHAR;
\r
1027 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1029 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1030 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1031 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1032 /* xboard, and older WinBoards, controlled the move sound with the
\r
1033 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1034 always turn the option on (so that the backend will call us),
\r
1035 then let the user turn the sound off by setting it to silence if
\r
1036 desired. To accommodate old winboard.ini files saved by old
\r
1037 versions of WinBoard, we also turn off the sound if the option
\r
1038 was initially set to false. [HGM] taken out of InitAppData */
\r
1039 if (!appData.ringBellAfterMoves) {
\r
1040 sounds[(int)SoundMove].name = strdup("");
\r
1041 appData.ringBellAfterMoves = TRUE;
\r
1043 if (appData.debugMode) {
\r
1044 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1045 setbuf(debugFP, NULL);
\r
1048 LoadLanguageFile(appData.language);
\r
1052 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1053 // InitEngineUCI( installDir, &second );
\r
1055 /* Create a main window for this application instance. */
\r
1056 hwnd = CreateWindow(szAppName, szTitle,
\r
1057 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1058 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1059 NULL, NULL, hInstance, NULL);
\r
1062 /* If window could not be created, return "failure" */
\r
1067 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1068 LoadLogo(&first, 0, FALSE);
\r
1069 LoadLogo(&second, 1, appData.icsActive);
\r
1073 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1074 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1075 iconCurrent = iconWhite;
\r
1076 InitDrawingColors();
\r
1077 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1078 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1079 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1080 /* Compute window size for each board size, and use the largest
\r
1081 size that fits on this screen as the default. */
\r
1082 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1083 if (boardSize == (BoardSize)-1 &&
\r
1084 winH <= screenHeight
\r
1085 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1086 && winW <= screenWidth) {
\r
1087 boardSize = (BoardSize)ibs;
\r
1091 InitDrawingSizes(boardSize, 0);
\r
1093 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1095 /* [AS] Load textures if specified */
\r
1098 mysrandom( (unsigned) time(NULL) );
\r
1100 /* [AS] Restore layout */
\r
1101 if( wpMoveHistory.visible ) {
\r
1102 MoveHistoryPopUp();
\r
1105 if( wpEvalGraph.visible ) {
\r
1109 if( wpEngineOutput.visible ) {
\r
1110 EngineOutputPopUp();
\r
1113 /* Make the window visible; update its client area; and return "success" */
\r
1114 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1115 wp.length = sizeof(WINDOWPLACEMENT);
\r
1117 wp.showCmd = nCmdShow;
\r
1118 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1119 wp.rcNormalPosition.left = wpMain.x;
\r
1120 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1121 wp.rcNormalPosition.top = wpMain.y;
\r
1122 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1123 SetWindowPlacement(hwndMain, &wp);
\r
1125 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1127 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1128 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1130 if (hwndConsole) {
\r
1132 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1133 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1135 ShowWindow(hwndConsole, nCmdShow);
\r
1136 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1137 char buf[MSG_SIZ], *p = buf, *q;
\r
1138 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1140 q = strchr(p, ';');
\r
1142 if(*p) ChatPopUp(p);
\r
1145 SetActiveWindow(hwndConsole);
\r
1147 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1148 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1157 HMENU hmenu = GetMenu(hwndMain);
\r
1159 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1160 MF_BYCOMMAND|((appData.icsActive &&
\r
1161 *appData.icsCommPort != NULLCHAR) ?
\r
1162 MF_ENABLED : MF_GRAYED));
\r
1163 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1164 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1165 MF_CHECKED : MF_UNCHECKED));
\r
1168 //---------------------------------------------------------------------------------------------------------
\r
1170 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1171 #define XBOARD FALSE
\r
1173 #define OPTCHAR "/"
\r
1174 #define SEPCHAR "="
\r
1178 // front-end part of option handling
\r
1181 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1183 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1184 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1187 lf->lfEscapement = 0;
\r
1188 lf->lfOrientation = 0;
\r
1189 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1190 lf->lfItalic = mfp->italic;
\r
1191 lf->lfUnderline = mfp->underline;
\r
1192 lf->lfStrikeOut = mfp->strikeout;
\r
1193 lf->lfCharSet = mfp->charset;
\r
1194 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1195 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1196 lf->lfQuality = DEFAULT_QUALITY;
\r
1197 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1198 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1202 CreateFontInMF(MyFont *mf)
\r
1204 LFfromMFP(&mf->lf, &mf->mfp);
\r
1205 if (mf->hf) DeleteObject(mf->hf);
\r
1206 mf->hf = CreateFontIndirect(&mf->lf);
\r
1209 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1211 colorVariable[] = {
\r
1212 &whitePieceColor,
\r
1213 &blackPieceColor,
\r
1214 &lightSquareColor,
\r
1215 &darkSquareColor,
\r
1216 &highlightSquareColor,
\r
1217 &premoveHighlightColor,
\r
1219 &consoleBackgroundColor,
\r
1220 &appData.fontForeColorWhite,
\r
1221 &appData.fontBackColorWhite,
\r
1222 &appData.fontForeColorBlack,
\r
1223 &appData.fontBackColorBlack,
\r
1224 &appData.evalHistColorWhite,
\r
1225 &appData.evalHistColorBlack,
\r
1226 &appData.highlightArrowColor,
\r
1229 /* Command line font name parser. NULL name means do nothing.
\r
1230 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1231 For backward compatibility, syntax without the colon is also
\r
1232 accepted, but font names with digits in them won't work in that case.
\r
1235 ParseFontName(char *name, MyFontParams *mfp)
\r
1238 if (name == NULL) return;
\r
1240 q = strchr(p, ':');
\r
1242 if (q - p >= sizeof(mfp->faceName))
\r
1243 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1244 memcpy(mfp->faceName, p, q - p);
\r
1245 mfp->faceName[q - p] = NULLCHAR;
\r
1248 q = mfp->faceName;
\r
1249 while (*p && !isdigit(*p)) {
\r
1251 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1252 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1254 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1257 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1258 mfp->pointSize = (float) atof(p);
\r
1259 mfp->bold = (strchr(p, 'b') != NULL);
\r
1260 mfp->italic = (strchr(p, 'i') != NULL);
\r
1261 mfp->underline = (strchr(p, 'u') != NULL);
\r
1262 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1263 mfp->charset = DEFAULT_CHARSET;
\r
1264 q = strchr(p, 'c');
\r
1266 mfp->charset = (BYTE) atoi(q+1);
\r
1270 ParseFont(char *name, int number)
\r
1271 { // wrapper to shield back-end from 'font'
\r
1272 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1277 { // in WB we have a 2D array of fonts; this initializes their description
\r
1279 /* Point font array elements to structures and
\r
1280 parse default font names */
\r
1281 for (i=0; i<NUM_FONTS; i++) {
\r
1282 for (j=0; j<NUM_SIZES; j++) {
\r
1283 font[j][i] = &fontRec[j][i];
\r
1284 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1291 { // here we create the actual fonts from the selected descriptions
\r
1293 for (i=0; i<NUM_FONTS; i++) {
\r
1294 for (j=0; j<NUM_SIZES; j++) {
\r
1295 CreateFontInMF(font[j][i]);
\r
1299 /* Color name parser.
\r
1300 X version accepts X color names, but this one
\r
1301 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1303 ParseColorName(char *name)
\r
1305 int red, green, blue, count;
\r
1306 char buf[MSG_SIZ];
\r
1308 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1310 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1311 &red, &green, &blue);
\r
1314 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1315 DisplayError(buf, 0);
\r
1316 return RGB(0, 0, 0);
\r
1318 return PALETTERGB(red, green, blue);
\r
1322 ParseColor(int n, char *name)
\r
1323 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1324 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1328 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1330 char *e = argValue;
\r
1334 if (*e == 'b') eff |= CFE_BOLD;
\r
1335 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1336 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1337 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1338 else if (*e == '#' || isdigit(*e)) break;
\r
1342 *color = ParseColorName(e);
\r
1346 ParseTextAttribs(ColorClass cc, char *s)
\r
1347 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1348 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1349 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1353 ParseBoardSize(void *addr, char *name)
\r
1354 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1355 BoardSize bs = SizeTiny;
\r
1356 while (sizeInfo[bs].name != NULL) {
\r
1357 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1358 *(BoardSize *)addr = bs;
\r
1363 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1368 { // [HGM] import name from appData first
\r
1371 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1372 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1373 textAttribs[cc].sound.data = NULL;
\r
1374 MyLoadSound(&textAttribs[cc].sound);
\r
1376 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1377 textAttribs[cc].sound.name = strdup("");
\r
1378 textAttribs[cc].sound.data = NULL;
\r
1380 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1381 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1382 sounds[sc].data = NULL;
\r
1383 MyLoadSound(&sounds[sc]);
\r
1388 SetCommPortDefaults()
\r
1390 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1391 dcb.DCBlength = sizeof(DCB);
\r
1392 dcb.BaudRate = 9600;
\r
1393 dcb.fBinary = TRUE;
\r
1394 dcb.fParity = FALSE;
\r
1395 dcb.fOutxCtsFlow = FALSE;
\r
1396 dcb.fOutxDsrFlow = FALSE;
\r
1397 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1398 dcb.fDsrSensitivity = FALSE;
\r
1399 dcb.fTXContinueOnXoff = TRUE;
\r
1400 dcb.fOutX = FALSE;
\r
1402 dcb.fNull = FALSE;
\r
1403 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1404 dcb.fAbortOnError = FALSE;
\r
1406 dcb.Parity = SPACEPARITY;
\r
1407 dcb.StopBits = ONESTOPBIT;
\r
1410 // [HGM] args: these three cases taken out to stay in front-end
\r
1412 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1413 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1414 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1415 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1417 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1418 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1419 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1420 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1421 ad->argName, mfp->faceName, mfp->pointSize,
\r
1422 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1423 mfp->bold ? "b" : "",
\r
1424 mfp->italic ? "i" : "",
\r
1425 mfp->underline ? "u" : "",
\r
1426 mfp->strikeout ? "s" : "",
\r
1427 (int)mfp->charset);
\r
1433 { // [HGM] copy the names from the internal WB variables to appData
\r
1436 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1437 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1438 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1439 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1443 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1444 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1445 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1446 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1447 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1448 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1449 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1450 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1451 (ta->effects) ? " " : "",
\r
1452 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1456 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1457 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1458 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1459 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1460 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1464 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1465 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1466 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1470 ParseCommPortSettings(char *s)
\r
1471 { // wrapper to keep dcb from back-end
\r
1472 ParseCommSettings(s, &dcb);
\r
1477 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1478 GetActualPlacement(hwndMain, &wpMain);
\r
1479 GetActualPlacement(hwndConsole, &wpConsole);
\r
1480 GetActualPlacement(commentDialog, &wpComment);
\r
1481 GetActualPlacement(editTagsDialog, &wpTags);
\r
1482 GetActualPlacement(gameListDialog, &wpGameList);
\r
1483 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1484 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1485 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1489 PrintCommPortSettings(FILE *f, char *name)
\r
1490 { // wrapper to shield back-end from DCB
\r
1491 PrintCommSettings(f, name, &dcb);
\r
1495 MySearchPath(char *installDir, char *name, char *fullname)
\r
1497 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1498 if(name[0]== '%') {
\r
1499 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1500 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1501 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1502 *strchr(buf, '%') = 0;
\r
1503 strcat(fullname, getenv(buf));
\r
1504 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1506 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1507 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1508 return (int) strlen(fullname);
\r
1510 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1514 MyGetFullPathName(char *name, char *fullname)
\r
1517 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1522 { // [HGM] args: allows testing if main window is realized from back-end
\r
1523 return hwndMain != NULL;
\r
1527 PopUpStartupDialog()
\r
1531 LoadLanguageFile(appData.language);
\r
1532 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1533 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1534 FreeProcInstance(lpProc);
\r
1537 /*---------------------------------------------------------------------------*\
\r
1539 * GDI board drawing routines
\r
1541 \*---------------------------------------------------------------------------*/
\r
1543 /* [AS] Draw square using background texture */
\r
1544 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1549 return; /* Should never happen! */
\r
1552 SetGraphicsMode( dst, GM_ADVANCED );
\r
1559 /* X reflection */
\r
1564 x.eDx = (FLOAT) dw + dx - 1;
\r
1567 SetWorldTransform( dst, &x );
\r
1570 /* Y reflection */
\r
1576 x.eDy = (FLOAT) dh + dy - 1;
\r
1578 SetWorldTransform( dst, &x );
\r
1586 x.eDx = (FLOAT) dx;
\r
1587 x.eDy = (FLOAT) dy;
\r
1590 SetWorldTransform( dst, &x );
\r
1594 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1602 SetWorldTransform( dst, &x );
\r
1604 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1607 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1609 PM_WP = (int) WhitePawn,
\r
1610 PM_WN = (int) WhiteKnight,
\r
1611 PM_WB = (int) WhiteBishop,
\r
1612 PM_WR = (int) WhiteRook,
\r
1613 PM_WQ = (int) WhiteQueen,
\r
1614 PM_WF = (int) WhiteFerz,
\r
1615 PM_WW = (int) WhiteWazir,
\r
1616 PM_WE = (int) WhiteAlfil,
\r
1617 PM_WM = (int) WhiteMan,
\r
1618 PM_WO = (int) WhiteCannon,
\r
1619 PM_WU = (int) WhiteUnicorn,
\r
1620 PM_WH = (int) WhiteNightrider,
\r
1621 PM_WA = (int) WhiteAngel,
\r
1622 PM_WC = (int) WhiteMarshall,
\r
1623 PM_WAB = (int) WhiteCardinal,
\r
1624 PM_WD = (int) WhiteDragon,
\r
1625 PM_WL = (int) WhiteLance,
\r
1626 PM_WS = (int) WhiteCobra,
\r
1627 PM_WV = (int) WhiteFalcon,
\r
1628 PM_WSG = (int) WhiteSilver,
\r
1629 PM_WG = (int) WhiteGrasshopper,
\r
1630 PM_WK = (int) WhiteKing,
\r
1631 PM_BP = (int) BlackPawn,
\r
1632 PM_BN = (int) BlackKnight,
\r
1633 PM_BB = (int) BlackBishop,
\r
1634 PM_BR = (int) BlackRook,
\r
1635 PM_BQ = (int) BlackQueen,
\r
1636 PM_BF = (int) BlackFerz,
\r
1637 PM_BW = (int) BlackWazir,
\r
1638 PM_BE = (int) BlackAlfil,
\r
1639 PM_BM = (int) BlackMan,
\r
1640 PM_BO = (int) BlackCannon,
\r
1641 PM_BU = (int) BlackUnicorn,
\r
1642 PM_BH = (int) BlackNightrider,
\r
1643 PM_BA = (int) BlackAngel,
\r
1644 PM_BC = (int) BlackMarshall,
\r
1645 PM_BG = (int) BlackGrasshopper,
\r
1646 PM_BAB = (int) BlackCardinal,
\r
1647 PM_BD = (int) BlackDragon,
\r
1648 PM_BL = (int) BlackLance,
\r
1649 PM_BS = (int) BlackCobra,
\r
1650 PM_BV = (int) BlackFalcon,
\r
1651 PM_BSG = (int) BlackSilver,
\r
1652 PM_BK = (int) BlackKing
\r
1655 static HFONT hPieceFont = NULL;
\r
1656 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1657 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1658 static int fontBitmapSquareSize = 0;
\r
1659 static char pieceToFontChar[(int) EmptySquare] =
\r
1660 { 'p', 'n', 'b', 'r', 'q',
\r
1661 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1662 'k', 'o', 'm', 'v', 't', 'w',
\r
1663 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1666 extern BOOL SetCharTable( char *table, const char * map );
\r
1667 /* [HGM] moved to backend.c */
\r
1669 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1672 BYTE r1 = GetRValue( color );
\r
1673 BYTE g1 = GetGValue( color );
\r
1674 BYTE b1 = GetBValue( color );
\r
1680 /* Create a uniform background first */
\r
1681 hbrush = CreateSolidBrush( color );
\r
1682 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1683 FillRect( hdc, &rc, hbrush );
\r
1684 DeleteObject( hbrush );
\r
1687 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1688 int steps = squareSize / 2;
\r
1691 for( i=0; i<steps; i++ ) {
\r
1692 BYTE r = r1 - (r1-r2) * i / steps;
\r
1693 BYTE g = g1 - (g1-g2) * i / steps;
\r
1694 BYTE b = b1 - (b1-b2) * i / steps;
\r
1696 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1697 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1698 FillRect( hdc, &rc, hbrush );
\r
1699 DeleteObject(hbrush);
\r
1702 else if( mode == 2 ) {
\r
1703 /* Diagonal gradient, good more or less for every piece */
\r
1704 POINT triangle[3];
\r
1705 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1706 HBRUSH hbrush_old;
\r
1707 int steps = squareSize;
\r
1710 triangle[0].x = squareSize - steps;
\r
1711 triangle[0].y = squareSize;
\r
1712 triangle[1].x = squareSize;
\r
1713 triangle[1].y = squareSize;
\r
1714 triangle[2].x = squareSize;
\r
1715 triangle[2].y = squareSize - steps;
\r
1717 for( i=0; i<steps; i++ ) {
\r
1718 BYTE r = r1 - (r1-r2) * i / steps;
\r
1719 BYTE g = g1 - (g1-g2) * i / steps;
\r
1720 BYTE b = b1 - (b1-b2) * i / steps;
\r
1722 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1723 hbrush_old = SelectObject( hdc, hbrush );
\r
1724 Polygon( hdc, triangle, 3 );
\r
1725 SelectObject( hdc, hbrush_old );
\r
1726 DeleteObject(hbrush);
\r
1731 SelectObject( hdc, hpen );
\r
1736 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1737 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1738 piece: follow the steps as explained below.
\r
1740 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1744 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1748 int backColor = whitePieceColor;
\r
1749 int foreColor = blackPieceColor;
\r
1751 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1752 backColor = appData.fontBackColorWhite;
\r
1753 foreColor = appData.fontForeColorWhite;
\r
1755 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1756 backColor = appData.fontBackColorBlack;
\r
1757 foreColor = appData.fontForeColorBlack;
\r
1761 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1763 hbm_old = SelectObject( hdc, hbm );
\r
1767 rc.right = squareSize;
\r
1768 rc.bottom = squareSize;
\r
1770 /* Step 1: background is now black */
\r
1771 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1773 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1775 pt.x = (squareSize - sz.cx) / 2;
\r
1776 pt.y = (squareSize - sz.cy) / 2;
\r
1778 SetBkMode( hdc, TRANSPARENT );
\r
1779 SetTextColor( hdc, chroma );
\r
1780 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1781 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1783 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1784 /* Step 3: the area outside the piece is filled with white */
\r
1785 // FloodFill( hdc, 0, 0, chroma );
\r
1786 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1787 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1788 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1789 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1790 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1792 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1793 but if the start point is not inside the piece we're lost!
\r
1794 There should be a better way to do this... if we could create a region or path
\r
1795 from the fill operation we would be fine for example.
\r
1797 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1798 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1800 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1801 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1802 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1804 SelectObject( dc2, bm2 );
\r
1805 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1806 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1807 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1808 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1809 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1812 DeleteObject( bm2 );
\r
1815 SetTextColor( hdc, 0 );
\r
1817 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1818 draw the piece again in black for safety.
\r
1820 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1822 SelectObject( hdc, hbm_old );
\r
1824 if( hPieceMask[index] != NULL ) {
\r
1825 DeleteObject( hPieceMask[index] );
\r
1828 hPieceMask[index] = hbm;
\r
1831 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1833 SelectObject( hdc, hbm );
\r
1836 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1837 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1838 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1840 SelectObject( dc1, hPieceMask[index] );
\r
1841 SelectObject( dc2, bm2 );
\r
1842 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1843 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1846 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1847 the piece background and deletes (makes transparent) the rest.
\r
1848 Thanks to that mask, we are free to paint the background with the greates
\r
1849 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1850 We use this, to make gradients and give the pieces a "roundish" look.
\r
1852 SetPieceBackground( hdc, backColor, 2 );
\r
1853 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1857 DeleteObject( bm2 );
\r
1860 SetTextColor( hdc, foreColor );
\r
1861 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1863 SelectObject( hdc, hbm_old );
\r
1865 if( hPieceFace[index] != NULL ) {
\r
1866 DeleteObject( hPieceFace[index] );
\r
1869 hPieceFace[index] = hbm;
\r
1872 static int TranslatePieceToFontPiece( int piece )
\r
1902 case BlackMarshall:
\r
1906 case BlackNightrider:
\r
1912 case BlackUnicorn:
\r
1916 case BlackGrasshopper:
\r
1928 case BlackCardinal:
\r
1935 case WhiteMarshall:
\r
1939 case WhiteNightrider:
\r
1945 case WhiteUnicorn:
\r
1949 case WhiteGrasshopper:
\r
1961 case WhiteCardinal:
\r
1970 void CreatePiecesFromFont()
\r
1973 HDC hdc_window = NULL;
\r
1979 if( fontBitmapSquareSize < 0 ) {
\r
1980 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1984 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
1985 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1986 fontBitmapSquareSize = -1;
\r
1990 if( fontBitmapSquareSize != squareSize ) {
\r
1991 hdc_window = GetDC( hwndMain );
\r
1992 hdc = CreateCompatibleDC( hdc_window );
\r
1994 if( hPieceFont != NULL ) {
\r
1995 DeleteObject( hPieceFont );
\r
1998 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1999 hPieceMask[i] = NULL;
\r
2000 hPieceFace[i] = NULL;
\r
2006 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2007 fontHeight = appData.fontPieceSize;
\r
2010 fontHeight = (fontHeight * squareSize) / 100;
\r
2012 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2014 lf.lfEscapement = 0;
\r
2015 lf.lfOrientation = 0;
\r
2016 lf.lfWeight = FW_NORMAL;
\r
2018 lf.lfUnderline = 0;
\r
2019 lf.lfStrikeOut = 0;
\r
2020 lf.lfCharSet = DEFAULT_CHARSET;
\r
2021 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2022 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2023 lf.lfQuality = PROOF_QUALITY;
\r
2024 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2025 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2026 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2028 hPieceFont = CreateFontIndirect( &lf );
\r
2030 if( hPieceFont == NULL ) {
\r
2031 fontBitmapSquareSize = -2;
\r
2034 /* Setup font-to-piece character table */
\r
2035 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2036 /* No (or wrong) global settings, try to detect the font */
\r
2037 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2039 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2041 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2042 /* DiagramTT* family */
\r
2043 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2045 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2046 /* Fairy symbols */
\r
2047 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2049 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2050 /* Good Companion (Some characters get warped as literal :-( */
\r
2051 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2052 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2053 SetCharTable(pieceToFontChar, s);
\r
2056 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2057 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2061 /* Create bitmaps */
\r
2062 hfont_old = SelectObject( hdc, hPieceFont );
\r
2063 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2064 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2065 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2067 SelectObject( hdc, hfont_old );
\r
2069 fontBitmapSquareSize = squareSize;
\r
2073 if( hdc != NULL ) {
\r
2077 if( hdc_window != NULL ) {
\r
2078 ReleaseDC( hwndMain, hdc_window );
\r
2083 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2087 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2088 if (gameInfo.event &&
\r
2089 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2090 strcmp(name, "k80s") == 0) {
\r
2091 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2093 return LoadBitmap(hinst, name);
\r
2097 /* Insert a color into the program's logical palette
\r
2098 structure. This code assumes the given color is
\r
2099 the result of the RGB or PALETTERGB macro, and it
\r
2100 knows how those macros work (which is documented).
\r
2103 InsertInPalette(COLORREF color)
\r
2105 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2107 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2108 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2109 pLogPal->palNumEntries--;
\r
2113 pe->peFlags = (char) 0;
\r
2114 pe->peRed = (char) (0xFF & color);
\r
2115 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2116 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2122 InitDrawingColors()
\r
2124 if (pLogPal == NULL) {
\r
2125 /* Allocate enough memory for a logical palette with
\r
2126 * PALETTESIZE entries and set the size and version fields
\r
2127 * of the logical palette structure.
\r
2129 pLogPal = (NPLOGPALETTE)
\r
2130 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2131 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2132 pLogPal->palVersion = 0x300;
\r
2134 pLogPal->palNumEntries = 0;
\r
2136 InsertInPalette(lightSquareColor);
\r
2137 InsertInPalette(darkSquareColor);
\r
2138 InsertInPalette(whitePieceColor);
\r
2139 InsertInPalette(blackPieceColor);
\r
2140 InsertInPalette(highlightSquareColor);
\r
2141 InsertInPalette(premoveHighlightColor);
\r
2143 /* create a logical color palette according the information
\r
2144 * in the LOGPALETTE structure.
\r
2146 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2148 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2149 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2150 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2151 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2152 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2153 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2154 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2155 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2156 /* [AS] Force rendering of the font-based pieces */
\r
2157 if( fontBitmapSquareSize > 0 ) {
\r
2158 fontBitmapSquareSize = 0;
\r
2164 BoardWidth(int boardSize, int n)
\r
2165 { /* [HGM] argument n added to allow different width and height */
\r
2166 int lineGap = sizeInfo[boardSize].lineGap;
\r
2168 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2169 lineGap = appData.overrideLineGap;
\r
2172 return (n + 1) * lineGap +
\r
2173 n * sizeInfo[boardSize].squareSize;
\r
2176 /* Respond to board resize by dragging edge */
\r
2178 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2180 BoardSize newSize = NUM_SIZES - 1;
\r
2181 static int recurse = 0;
\r
2182 if (IsIconic(hwndMain)) return;
\r
2183 if (recurse > 0) return;
\r
2185 while (newSize > 0) {
\r
2186 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2187 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2188 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2191 boardSize = newSize;
\r
2192 InitDrawingSizes(boardSize, flags);
\r
2197 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2200 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2202 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2203 ChessSquare piece;
\r
2204 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2206 SIZE clockSize, messageSize;
\r
2208 char buf[MSG_SIZ];
\r
2210 HMENU hmenu = GetMenu(hwndMain);
\r
2211 RECT crect, wrect, oldRect;
\r
2213 LOGBRUSH logbrush;
\r
2215 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2216 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2218 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2219 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2221 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2222 oldRect.top = wpMain.y;
\r
2223 oldRect.right = wpMain.x + wpMain.width;
\r
2224 oldRect.bottom = wpMain.y + wpMain.height;
\r
2226 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2227 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2228 squareSize = sizeInfo[boardSize].squareSize;
\r
2229 lineGap = sizeInfo[boardSize].lineGap;
\r
2230 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2232 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2233 lineGap = appData.overrideLineGap;
\r
2236 if (tinyLayout != oldTinyLayout) {
\r
2237 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2239 style &= ~WS_SYSMENU;
\r
2240 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2241 "&Minimize\tCtrl+F4");
\r
2243 style |= WS_SYSMENU;
\r
2244 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2246 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2248 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2249 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2250 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2252 DrawMenuBar(hwndMain);
\r
2255 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2256 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2258 /* Get text area sizes */
\r
2259 hdc = GetDC(hwndMain);
\r
2260 if (appData.clockMode) {
\r
2261 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2263 snprintf(buf, MSG_SIZ, _("White"));
\r
2265 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2266 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2267 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2268 str = _("We only care about the height here");
\r
2269 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2270 SelectObject(hdc, oldFont);
\r
2271 ReleaseDC(hwndMain, hdc);
\r
2273 /* Compute where everything goes */
\r
2274 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2275 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2276 logoHeight = 2*clockSize.cy;
\r
2277 leftLogoRect.left = OUTER_MARGIN;
\r
2278 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2279 leftLogoRect.top = OUTER_MARGIN;
\r
2280 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2282 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2283 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2284 rightLogoRect.top = OUTER_MARGIN;
\r
2285 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2288 whiteRect.left = leftLogoRect.right;
\r
2289 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2290 whiteRect.top = OUTER_MARGIN;
\r
2291 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2293 blackRect.right = rightLogoRect.left;
\r
2294 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2295 blackRect.top = whiteRect.top;
\r
2296 blackRect.bottom = whiteRect.bottom;
\r
2298 whiteRect.left = OUTER_MARGIN;
\r
2299 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2300 whiteRect.top = OUTER_MARGIN;
\r
2301 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2303 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2304 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2305 blackRect.top = whiteRect.top;
\r
2306 blackRect.bottom = whiteRect.bottom;
\r
2308 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2311 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2312 if (appData.showButtonBar) {
\r
2313 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2314 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2316 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2318 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2319 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2321 boardRect.left = OUTER_MARGIN;
\r
2322 boardRect.right = boardRect.left + boardWidth;
\r
2323 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2324 boardRect.bottom = boardRect.top + boardHeight;
\r
2326 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2327 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2328 oldBoardSize = boardSize;
\r
2329 oldTinyLayout = tinyLayout;
\r
2330 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2331 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2332 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2333 winW *= 1 + twoBoards;
\r
2334 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2335 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2336 wpMain.height = winH; // without disturbing window attachments
\r
2337 GetWindowRect(hwndMain, &wrect);
\r
2338 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2339 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2341 // [HGM] placement: let attached windows follow size change.
\r
2342 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2343 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2344 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2345 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2346 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2348 /* compensate if menu bar wrapped */
\r
2349 GetClientRect(hwndMain, &crect);
\r
2350 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2351 wpMain.height += offby;
\r
2353 case WMSZ_TOPLEFT:
\r
2354 SetWindowPos(hwndMain, NULL,
\r
2355 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2356 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2359 case WMSZ_TOPRIGHT:
\r
2361 SetWindowPos(hwndMain, NULL,
\r
2362 wrect.left, wrect.bottom - wpMain.height,
\r
2363 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2366 case WMSZ_BOTTOMLEFT:
\r
2368 SetWindowPos(hwndMain, NULL,
\r
2369 wrect.right - wpMain.width, wrect.top,
\r
2370 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2373 case WMSZ_BOTTOMRIGHT:
\r
2377 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2378 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2383 for (i = 0; i < N_BUTTONS; i++) {
\r
2384 if (buttonDesc[i].hwnd != NULL) {
\r
2385 DestroyWindow(buttonDesc[i].hwnd);
\r
2386 buttonDesc[i].hwnd = NULL;
\r
2388 if (appData.showButtonBar) {
\r
2389 buttonDesc[i].hwnd =
\r
2390 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2391 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2392 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2393 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2394 (HMENU) buttonDesc[i].id,
\r
2395 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2397 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2398 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2399 MAKELPARAM(FALSE, 0));
\r
2401 if (buttonDesc[i].id == IDM_Pause)
\r
2402 hwndPause = buttonDesc[i].hwnd;
\r
2403 buttonDesc[i].wndproc = (WNDPROC)
\r
2404 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2407 if (gridPen != NULL) DeleteObject(gridPen);
\r
2408 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2409 if (premovePen != NULL) DeleteObject(premovePen);
\r
2410 if (lineGap != 0) {
\r
2411 logbrush.lbStyle = BS_SOLID;
\r
2412 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2414 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2415 lineGap, &logbrush, 0, NULL);
\r
2416 logbrush.lbColor = highlightSquareColor;
\r
2418 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2419 lineGap, &logbrush, 0, NULL);
\r
2421 logbrush.lbColor = premoveHighlightColor;
\r
2423 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2424 lineGap, &logbrush, 0, NULL);
\r
2426 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2427 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2428 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2429 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2430 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2431 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2432 BOARD_WIDTH * (squareSize + lineGap);
\r
2433 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2435 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2436 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2437 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2438 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2439 lineGap / 2 + (i * (squareSize + lineGap));
\r
2440 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2441 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2442 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2446 /* [HGM] Licensing requirement */
\r
2448 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2451 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2453 GothicPopUp( "", VariantNormal);
\r
2456 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2458 /* Load piece bitmaps for this board size */
\r
2459 for (i=0; i<=2; i++) {
\r
2460 for (piece = WhitePawn;
\r
2461 (int) piece < (int) BlackPawn;
\r
2462 piece = (ChessSquare) ((int) piece + 1)) {
\r
2463 if (pieceBitmap[i][piece] != NULL)
\r
2464 DeleteObject(pieceBitmap[i][piece]);
\r
2468 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2469 // Orthodox Chess pieces
\r
2470 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2471 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2472 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2473 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2474 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2475 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2476 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2477 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2478 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2479 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2480 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2481 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2482 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2483 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2484 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2485 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2486 // in Shogi, Hijack the unused Queen for Lance
\r
2487 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2488 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2489 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2491 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2492 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2493 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2496 if(squareSize <= 72 && squareSize >= 33) {
\r
2497 /* A & C are available in most sizes now */
\r
2498 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2499 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2500 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2501 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2502 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2503 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2504 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2505 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2506 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2507 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2508 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2509 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2510 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2511 } else { // Smirf-like
\r
2512 if(gameInfo.variant == VariantSChess) {
\r
2513 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2514 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2515 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2517 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2518 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2519 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2522 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2523 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2524 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2525 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2526 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2527 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2528 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2529 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2530 } else { // WinBoard standard
\r
2531 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2532 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2533 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2538 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2539 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2542 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2545 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2548 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2560 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2561 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2562 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2563 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2566 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2567 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2568 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2570 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2571 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2574 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2575 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2576 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2577 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2578 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2579 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2580 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2581 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2582 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2584 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2585 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2586 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2587 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2588 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2589 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2590 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2591 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2592 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2593 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2594 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2595 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2598 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2599 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2600 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2601 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2602 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2603 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2604 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2605 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2606 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2607 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2608 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2609 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2610 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2611 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2612 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2616 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2617 /* special Shogi support in this size */
\r
2618 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2619 for (piece = WhitePawn;
\r
2620 (int) piece < (int) BlackPawn;
\r
2621 piece = (ChessSquare) ((int) piece + 1)) {
\r
2622 if (pieceBitmap[i][piece] != NULL)
\r
2623 DeleteObject(pieceBitmap[i][piece]);
\r
2626 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2627 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2628 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2629 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2630 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2631 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2632 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2633 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2634 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2635 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2636 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2637 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2638 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2639 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2640 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2641 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2642 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2643 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2644 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2645 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2646 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2647 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2648 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2649 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2650 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2651 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2652 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2653 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2654 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2657 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2658 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2659 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2660 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2661 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2662 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2663 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2664 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2665 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2666 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2667 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2673 PieceBitmap(ChessSquare p, int kind)
\r
2675 if ((int) p >= (int) BlackPawn)
\r
2676 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2678 return pieceBitmap[kind][(int) p];
\r
2681 /***************************************************************/
\r
2683 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2684 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2686 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2687 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2691 SquareToPos(int row, int column, int * x, int * y)
\r
2694 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2695 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2697 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2698 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2703 DrawCoordsOnDC(HDC hdc)
\r
2705 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2706 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2707 char str[2] = { NULLCHAR, NULLCHAR };
\r
2708 int oldMode, oldAlign, x, y, start, i;
\r
2712 if (!appData.showCoords)
\r
2715 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2717 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2718 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2719 oldAlign = GetTextAlign(hdc);
\r
2720 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2722 y = boardRect.top + lineGap;
\r
2723 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2725 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2726 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2727 str[0] = files[start + i];
\r
2728 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2729 y += squareSize + lineGap;
\r
2732 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2734 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2735 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2736 str[0] = ranks[start + i];
\r
2737 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2738 x += squareSize + lineGap;
\r
2741 SelectObject(hdc, oldBrush);
\r
2742 SetBkMode(hdc, oldMode);
\r
2743 SetTextAlign(hdc, oldAlign);
\r
2744 SelectObject(hdc, oldFont);
\r
2748 DrawGridOnDC(HDC hdc)
\r
2752 if (lineGap != 0) {
\r
2753 oldPen = SelectObject(hdc, gridPen);
\r
2754 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2755 SelectObject(hdc, oldPen);
\r
2759 #define HIGHLIGHT_PEN 0
\r
2760 #define PREMOVE_PEN 1
\r
2763 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2766 HPEN oldPen, hPen;
\r
2767 if (lineGap == 0) return;
\r
2769 x1 = boardRect.left +
\r
2770 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2771 y1 = boardRect.top +
\r
2772 lineGap/2 + y * (squareSize + lineGap);
\r
2774 x1 = boardRect.left +
\r
2775 lineGap/2 + x * (squareSize + lineGap);
\r
2776 y1 = boardRect.top +
\r
2777 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2779 hPen = pen ? premovePen : highlightPen;
\r
2780 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2781 MoveToEx(hdc, x1, y1, NULL);
\r
2782 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2783 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2784 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2785 LineTo(hdc, x1, y1);
\r
2786 SelectObject(hdc, oldPen);
\r
2790 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2793 for (i=0; i<2; i++) {
\r
2794 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2795 DrawHighlightOnDC(hdc, TRUE,
\r
2796 h->sq[i].x, h->sq[i].y,
\r
2801 /* Note: sqcolor is used only in monoMode */
\r
2802 /* Note that this code is largely duplicated in woptions.c,
\r
2803 function DrawSampleSquare, so that needs to be updated too */
\r
2805 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2807 HBITMAP oldBitmap;
\r
2811 if (appData.blindfold) return;
\r
2813 /* [AS] Use font-based pieces if needed */
\r
2814 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2815 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2816 CreatePiecesFromFont();
\r
2818 if( fontBitmapSquareSize == squareSize ) {
\r
2819 int index = TranslatePieceToFontPiece(piece);
\r
2821 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2823 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2824 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2828 squareSize, squareSize,
\r
2833 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2835 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2836 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2840 squareSize, squareSize,
\r
2849 if (appData.monoMode) {
\r
2850 SelectObject(tmphdc, PieceBitmap(piece,
\r
2851 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2852 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2853 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2855 tmpSize = squareSize;
\r
2857 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2858 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2859 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2860 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2861 x += (squareSize - minorSize)>>1;
\r
2862 y += squareSize - minorSize - 2;
\r
2863 tmpSize = minorSize;
\r
2865 if (color || appData.allWhite ) {
\r
2866 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2868 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2869 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2870 if(appData.upsideDown && color==flipView)
\r
2871 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2873 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2874 /* Use black for outline of white pieces */
\r
2875 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2876 if(appData.upsideDown && color==flipView)
\r
2877 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2879 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2881 /* Use square color for details of black pieces */
\r
2882 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2883 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2884 if(appData.upsideDown && !flipView)
\r
2885 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2887 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2889 SelectObject(hdc, oldBrush);
\r
2890 SelectObject(tmphdc, oldBitmap);
\r
2894 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2895 int GetBackTextureMode( int algo )
\r
2897 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2901 case BACK_TEXTURE_MODE_PLAIN:
\r
2902 result = 1; /* Always use identity map */
\r
2904 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2905 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2913 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2914 to handle redraws cleanly (as random numbers would always be different).
\r
2916 VOID RebuildTextureSquareInfo()
\r
2926 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2928 if( liteBackTexture != NULL ) {
\r
2929 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2930 lite_w = bi.bmWidth;
\r
2931 lite_h = bi.bmHeight;
\r
2935 if( darkBackTexture != NULL ) {
\r
2936 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2937 dark_w = bi.bmWidth;
\r
2938 dark_h = bi.bmHeight;
\r
2942 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2943 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2944 if( (col + row) & 1 ) {
\r
2946 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2947 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2948 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2950 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2951 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2952 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2954 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2955 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2960 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2961 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2962 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2964 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2965 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2966 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2968 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2969 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2976 /* [AS] Arrow highlighting support */
\r
2978 static double A_WIDTH = 5; /* Width of arrow body */
\r
2980 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2981 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2983 static double Sqr( double x )
\r
2988 static int Round( double x )
\r
2990 return (int) (x + 0.5);
\r
2993 /* Draw an arrow between two points using current settings */
\r
2994 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2997 double dx, dy, j, k, x, y;
\r
2999 if( d_x == s_x ) {
\r
3000 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3002 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3005 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3006 arrow[1].y = d_y - h;
\r
3008 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3009 arrow[2].y = d_y - h;
\r
3014 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3015 arrow[5].y = d_y - h;
\r
3017 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3018 arrow[4].y = d_y - h;
\r
3020 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3023 else if( d_y == s_y ) {
\r
3024 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3027 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3029 arrow[1].x = d_x - w;
\r
3030 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3032 arrow[2].x = d_x - w;
\r
3033 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3038 arrow[5].x = d_x - w;
\r
3039 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3041 arrow[4].x = d_x - w;
\r
3042 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3045 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3048 /* [AS] Needed a lot of paper for this! :-) */
\r
3049 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3050 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3052 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3054 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3059 arrow[0].x = Round(x - j);
\r
3060 arrow[0].y = Round(y + j*dx);
\r
3062 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3063 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3066 x = (double) d_x - k;
\r
3067 y = (double) d_y - k*dy;
\r
3070 x = (double) d_x + k;
\r
3071 y = (double) d_y + k*dy;
\r
3074 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3076 arrow[6].x = Round(x - j);
\r
3077 arrow[6].y = Round(y + j*dx);
\r
3079 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3080 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3082 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3083 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3088 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3089 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3092 Polygon( hdc, arrow, 7 );
\r
3095 /* [AS] Draw an arrow between two squares */
\r
3096 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3098 int s_x, s_y, d_x, d_y;
\r
3105 if( s_col == d_col && s_row == d_row ) {
\r
3109 /* Get source and destination points */
\r
3110 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3111 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3114 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3116 else if( d_y < s_y ) {
\r
3117 d_y += squareSize / 2 + squareSize / 4;
\r
3120 d_y += squareSize / 2;
\r
3124 d_x += squareSize / 2 - squareSize / 4;
\r
3126 else if( d_x < s_x ) {
\r
3127 d_x += squareSize / 2 + squareSize / 4;
\r
3130 d_x += squareSize / 2;
\r
3133 s_x += squareSize / 2;
\r
3134 s_y += squareSize / 2;
\r
3136 /* Adjust width */
\r
3137 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3140 stLB.lbStyle = BS_SOLID;
\r
3141 stLB.lbColor = appData.highlightArrowColor;
\r
3144 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3145 holdpen = SelectObject( hdc, hpen );
\r
3146 hbrush = CreateBrushIndirect( &stLB );
\r
3147 holdbrush = SelectObject( hdc, hbrush );
\r
3149 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3151 SelectObject( hdc, holdpen );
\r
3152 SelectObject( hdc, holdbrush );
\r
3153 DeleteObject( hpen );
\r
3154 DeleteObject( hbrush );
\r
3157 BOOL HasHighlightInfo()
\r
3159 BOOL result = FALSE;
\r
3161 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3162 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3170 BOOL IsDrawArrowEnabled()
\r
3172 BOOL result = FALSE;
\r
3174 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3181 VOID DrawArrowHighlight( HDC hdc )
\r
3183 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3184 DrawArrowBetweenSquares( hdc,
\r
3185 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3186 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3190 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3192 HRGN result = NULL;
\r
3194 if( HasHighlightInfo() ) {
\r
3195 int x1, y1, x2, y2;
\r
3196 int sx, sy, dx, dy;
\r
3198 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3199 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r