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, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
257 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, 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[70000], *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 static char buf[MSG_SIZ];
\r
392 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
393 if(!barbaric) return s;
\r
394 if(!s) return ""; // sanity
\r
395 while(english[i]) {
\r
396 if(!strcmp(s, english[i])) return foreign[i];
\r
397 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
398 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
407 Translate(HWND hDlg, int dialogID)
\r
408 { // translate all text items in the given dialog
\r
410 char buf[MSG_SIZ], *s;
\r
411 if(!barbaric) return;
\r
412 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
413 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
414 GetWindowText( hDlg, buf, MSG_SIZ );
\r
416 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
417 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
418 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
419 if(strlen(buf) == 0) continue;
\r
421 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
426 TranslateOneMenu(int i, HMENU subMenu)
\r
429 static MENUITEMINFO info;
\r
431 info.cbSize = sizeof(MENUITEMINFO);
\r
432 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
433 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
435 info.dwTypeData = buf;
\r
436 info.cch = sizeof(buf);
\r
437 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
439 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
440 else menuText[i][j] = strdup(buf); // remember original on first change
\r
442 if(buf[0] == NULLCHAR) continue;
\r
443 info.dwTypeData = T_(buf);
\r
444 info.cch = strlen(buf)+1;
\r
445 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
451 TranslateMenus(int addLanguage)
\r
454 WIN32_FIND_DATA fileData;
\r
456 #define IDM_English 1970
\r
458 HMENU mainMenu = GetMenu(hwndMain);
\r
459 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
460 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
461 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
462 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
463 TranslateOneMenu(i, subMenu);
\r
465 DrawMenuBar(hwndMain);
\r
468 if(!addLanguage) return;
\r
469 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
470 HMENU mainMenu = GetMenu(hwndMain);
\r
471 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
472 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
473 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
474 i = 0; lastChecked = IDM_English;
\r
476 char *p, *q = fileData.cFileName;
\r
477 int checkFlag = MF_UNCHECKED;
\r
478 languageFile[i] = strdup(q);
\r
479 if(barbaric && !strcmp(oldLanguage, q)) {
\r
480 checkFlag = MF_CHECKED;
\r
481 lastChecked = IDM_English + i + 1;
\r
482 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
484 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
485 p = strstr(fileData.cFileName, ".lng");
\r
487 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
488 } while(FindNextFile(hFind, &fileData));
\r
501 int cliWidth, cliHeight;
\r
504 SizeInfo sizeInfo[] =
\r
506 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
507 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
508 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
509 { "petite", 33, 1, 1, 1, 0, 0 },
\r
510 { "slim", 37, 2, 1, 0, 0, 0 },
\r
511 { "small", 40, 2, 1, 0, 0, 0 },
\r
512 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
513 { "middling", 49, 2, 0, 0, 0, 0 },
\r
514 { "average", 54, 2, 0, 0, 0, 0 },
\r
515 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
516 { "medium", 64, 3, 0, 0, 0, 0 },
\r
517 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
518 { "large", 80, 3, 0, 0, 0, 0 },
\r
519 { "big", 87, 3, 0, 0, 0, 0 },
\r
520 { "huge", 95, 3, 0, 0, 0, 0 },
\r
521 { "giant", 108, 3, 0, 0, 0, 0 },
\r
522 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
523 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
524 { NULL, 0, 0, 0, 0, 0, 0 }
\r
527 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
528 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
530 { 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
531 { 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
532 { 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
533 { 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
534 { 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
535 { 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
536 { 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
537 { 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
538 { 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
539 { 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
540 { 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
541 { 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
542 { 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
543 { 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
544 { 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
545 { 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
546 { 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
547 { 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
550 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
559 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
560 #define N_BUTTONS 5
\r
562 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
564 {"<<", IDM_ToStart, NULL, NULL},
\r
565 {"<", IDM_Backward, NULL, NULL},
\r
566 {"P", IDM_Pause, NULL, NULL},
\r
567 {">", IDM_Forward, NULL, NULL},
\r
568 {">>", IDM_ToEnd, NULL, NULL},
\r
571 int tinyLayout = 0, smallLayout = 0;
\r
572 #define MENU_BAR_ITEMS 9
\r
573 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
574 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
575 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
579 MySound sounds[(int)NSoundClasses];
\r
580 MyTextAttribs textAttribs[(int)NColorClasses];
\r
582 MyColorizeAttribs colorizeAttribs[] = {
\r
583 { (COLORREF)0, 0, N_("Shout Text") },
\r
584 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
585 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
586 { (COLORREF)0, 0, N_("Channel Text") },
\r
587 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
588 { (COLORREF)0, 0, N_("Tell Text") },
\r
589 { (COLORREF)0, 0, N_("Challenge Text") },
\r
590 { (COLORREF)0, 0, N_("Request Text") },
\r
591 { (COLORREF)0, 0, N_("Seek Text") },
\r
592 { (COLORREF)0, 0, N_("Normal Text") },
\r
593 { (COLORREF)0, 0, N_("None") }
\r
598 static char *commentTitle;
\r
599 static char *commentText;
\r
600 static int commentIndex;
\r
601 static Boolean editComment = FALSE;
\r
604 char errorTitle[MSG_SIZ];
\r
605 char errorMessage[2*MSG_SIZ];
\r
606 HWND errorDialog = NULL;
\r
607 BOOLEAN moveErrorMessageUp = FALSE;
\r
608 BOOLEAN consoleEcho = TRUE;
\r
609 CHARFORMAT consoleCF;
\r
610 COLORREF consoleBackgroundColor;
\r
612 char *programVersion;
\r
618 typedef int CPKind;
\r
627 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
630 #define INPUT_SOURCE_BUF_SIZE 4096
\r
632 typedef struct _InputSource {
\r
639 char buf[INPUT_SOURCE_BUF_SIZE];
\r
643 InputCallback func;
\r
644 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
648 InputSource *consoleInputSource;
\r
653 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
654 VOID ConsoleCreate();
\r
656 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
657 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
658 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
659 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
661 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
662 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
663 void ParseIcsTextMenu(char *icsTextMenuString);
\r
664 VOID PopUpNameDialog(char firstchar);
\r
665 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
669 int GameListOptions();
\r
671 int dummy; // [HGM] for obsolete args
\r
673 HWND hwndMain = NULL; /* root window*/
\r
674 HWND hwndConsole = NULL;
\r
675 HWND commentDialog = NULL;
\r
676 HWND moveHistoryDialog = NULL;
\r
677 HWND evalGraphDialog = NULL;
\r
678 HWND engineOutputDialog = NULL;
\r
679 HWND gameListDialog = NULL;
\r
680 HWND editTagsDialog = NULL;
\r
682 int commentUp = FALSE;
\r
684 WindowPlacement wpMain;
\r
685 WindowPlacement wpConsole;
\r
686 WindowPlacement wpComment;
\r
687 WindowPlacement wpMoveHistory;
\r
688 WindowPlacement wpEvalGraph;
\r
689 WindowPlacement wpEngineOutput;
\r
690 WindowPlacement wpGameList;
\r
691 WindowPlacement wpTags;
\r
693 VOID EngineOptionsPopup(); // [HGM] settings
\r
695 VOID GothicPopUp(char *title, VariantClass variant);
\r
697 * Setting "frozen" should disable all user input other than deleting
\r
698 * the window. We do this while engines are initializing themselves.
\r
700 static int frozen = 0;
\r
701 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
707 if (frozen) return;
\r
709 hmenu = GetMenu(hwndMain);
\r
710 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
711 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
713 DrawMenuBar(hwndMain);
\r
716 /* Undo a FreezeUI */
\r
722 if (!frozen) return;
\r
724 hmenu = GetMenu(hwndMain);
\r
725 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
726 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
728 DrawMenuBar(hwndMain);
\r
731 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
733 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
739 #define JAWS_ALT_INTERCEPT
\r
740 #define JAWS_KB_NAVIGATION
\r
741 #define JAWS_MENU_ITEMS
\r
742 #define JAWS_SILENCE
\r
743 #define JAWS_REPLAY
\r
745 #define JAWS_COPYRIGHT
\r
746 #define JAWS_DELETE(X) X
\r
747 #define SAYMACHINEMOVE()
\r
751 /*---------------------------------------------------------------------------*\
\r
755 \*---------------------------------------------------------------------------*/
\r
758 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
759 LPSTR lpCmdLine, int nCmdShow)
\r
762 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
763 // INITCOMMONCONTROLSEX ex;
\r
767 LoadLibrary("RICHED32.DLL");
\r
768 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
770 if (!InitApplication(hInstance)) {
\r
773 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
780 // InitCommonControlsEx(&ex);
\r
781 InitCommonControls();
\r
783 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
784 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
785 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
787 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
789 while (GetMessage(&msg, /* message structure */
\r
790 NULL, /* handle of window receiving the message */
\r
791 0, /* lowest message to examine */
\r
792 0)) /* highest message to examine */
\r
795 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
796 // [HGM] navigate: switch between all windows with tab
\r
797 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
798 int i, currentElement = 0;
\r
800 // first determine what element of the chain we come from (if any)
\r
801 if(appData.icsActive) {
\r
802 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
803 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
805 if(engineOutputDialog && EngineOutputIsUp()) {
\r
806 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
807 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
809 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
810 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
812 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
813 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
814 if(msg.hwnd == e1) currentElement = 2; else
\r
815 if(msg.hwnd == e2) currentElement = 3; else
\r
816 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
817 if(msg.hwnd == mh) currentElement = 4; else
\r
818 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
819 if(msg.hwnd == hText) currentElement = 5; else
\r
820 if(msg.hwnd == hInput) currentElement = 6; else
\r
821 for (i = 0; i < N_BUTTONS; i++) {
\r
822 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
825 // determine where to go to
\r
826 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
828 currentElement = (currentElement + direction) % 7;
\r
829 switch(currentElement) {
\r
831 h = hwndMain; break; // passing this case always makes the loop exit
\r
833 h = buttonDesc[0].hwnd; break; // could be NULL
\r
835 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
838 if(!EngineOutputIsUp()) continue;
\r
841 if(!MoveHistoryIsUp()) continue;
\r
843 // case 6: // input to eval graph does not seem to get here!
\r
844 // if(!EvalGraphIsUp()) continue;
\r
845 // h = evalGraphDialog; break;
\r
847 if(!appData.icsActive) continue;
\r
851 if(!appData.icsActive) continue;
\r
857 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
858 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
861 continue; // this message now has been processed
\r
865 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
866 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
867 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
868 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
869 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
870 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
871 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
872 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
873 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
874 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
875 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
876 for(i=0; i<MAX_CHAT; i++)
\r
877 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
880 if(done) continue; // [HGM] chat: end patch
\r
881 TranslateMessage(&msg); /* Translates virtual key codes */
\r
882 DispatchMessage(&msg); /* Dispatches message to window */
\r
887 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
890 /*---------------------------------------------------------------------------*\
\r
892 * Initialization functions
\r
894 \*---------------------------------------------------------------------------*/
\r
898 { // update user logo if necessary
\r
899 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
901 if(appData.autoLogo) {
\r
902 curName = UserName();
\r
903 if(strcmp(curName, oldUserName)) {
\r
904 GetCurrentDirectory(MSG_SIZ, dir);
\r
905 SetCurrentDirectory(installDir);
\r
906 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
907 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
908 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
909 if(userLogo == NULL)
\r
910 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
911 SetCurrentDirectory(dir); /* return to prev directory */
\r
917 InitApplication(HINSTANCE hInstance)
\r
921 /* Fill in window class structure with parameters that describe the */
\r
924 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
925 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
926 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
927 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
928 wc.hInstance = hInstance; /* Owner of this class */
\r
929 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
930 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
931 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
932 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
933 wc.lpszClassName = szAppName; /* Name to register as */
\r
935 /* Register the window class and return success/failure code. */
\r
936 if (!RegisterClass(&wc)) return FALSE;
\r
938 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
939 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
941 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
942 wc.hInstance = hInstance;
\r
943 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
944 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
945 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
946 wc.lpszMenuName = NULL;
\r
947 wc.lpszClassName = szConsoleName;
\r
949 if (!RegisterClass(&wc)) return FALSE;
\r
954 /* Set by InitInstance, used by EnsureOnScreen */
\r
955 int screenHeight, screenWidth;
\r
958 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
960 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
961 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
962 if (*x > screenWidth - 32) *x = 0;
\r
963 if (*y > screenHeight - 32) *y = 0;
\r
964 if (*x < minX) *x = minX;
\r
965 if (*y < minY) *y = minY;
\r
969 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
971 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
972 GetCurrentDirectory(MSG_SIZ, dir);
\r
973 SetCurrentDirectory(installDir);
\r
974 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
975 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
977 if (cps->programLogo == NULL && appData.debugMode) {
\r
978 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
980 } else if(appData.autoLogo) {
\r
981 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
982 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
983 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
985 if(appData.directory[n] && appData.directory[n][0]) {
\r
986 SetCurrentDirectory(appData.directory[n]);
\r
987 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
990 SetCurrentDirectory(dir); /* return to prev directory */
\r
996 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
997 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
999 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1000 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1001 liteBackTextureMode = appData.liteBackTextureMode;
\r
1003 if (liteBackTexture == NULL && appData.debugMode) {
\r
1004 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1008 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1009 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1010 darkBackTextureMode = appData.darkBackTextureMode;
\r
1012 if (darkBackTexture == NULL && appData.debugMode) {
\r
1013 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1019 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1021 HWND hwnd; /* Main window handle. */
\r
1023 WINDOWPLACEMENT wp;
\r
1026 hInst = hInstance; /* Store instance handle in our global variable */
\r
1027 programName = szAppName;
\r
1029 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1030 *filepart = NULLCHAR;
\r
1032 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1034 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1035 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1036 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1037 /* xboard, and older WinBoards, controlled the move sound with the
\r
1038 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1039 always turn the option on (so that the backend will call us),
\r
1040 then let the user turn the sound off by setting it to silence if
\r
1041 desired. To accommodate old winboard.ini files saved by old
\r
1042 versions of WinBoard, we also turn off the sound if the option
\r
1043 was initially set to false. [HGM] taken out of InitAppData */
\r
1044 if (!appData.ringBellAfterMoves) {
\r
1045 sounds[(int)SoundMove].name = strdup("");
\r
1046 appData.ringBellAfterMoves = TRUE;
\r
1048 if (appData.debugMode) {
\r
1049 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1050 setbuf(debugFP, NULL);
\r
1053 LoadLanguageFile(appData.language);
\r
1057 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1058 // InitEngineUCI( installDir, &second );
\r
1060 /* Create a main window for this application instance. */
\r
1061 hwnd = CreateWindow(szAppName, szTitle,
\r
1062 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1063 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1064 NULL, NULL, hInstance, NULL);
\r
1067 /* If window could not be created, return "failure" */
\r
1072 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1073 LoadLogo(&first, 0, FALSE);
\r
1074 LoadLogo(&second, 1, appData.icsActive);
\r
1078 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1079 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1080 iconCurrent = iconWhite;
\r
1081 InitDrawingColors();
\r
1082 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1083 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1084 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1085 /* Compute window size for each board size, and use the largest
\r
1086 size that fits on this screen as the default. */
\r
1087 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1088 if (boardSize == (BoardSize)-1 &&
\r
1089 winH <= screenHeight
\r
1090 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1091 && winW <= screenWidth) {
\r
1092 boardSize = (BoardSize)ibs;
\r
1096 InitDrawingSizes(boardSize, 0);
\r
1098 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1100 /* [AS] Load textures if specified */
\r
1103 mysrandom( (unsigned) time(NULL) );
\r
1105 /* [AS] Restore layout */
\r
1106 if( wpMoveHistory.visible ) {
\r
1107 MoveHistoryPopUp();
\r
1110 if( wpEvalGraph.visible ) {
\r
1114 if( wpEngineOutput.visible ) {
\r
1115 EngineOutputPopUp();
\r
1118 /* Make the window visible; update its client area; and return "success" */
\r
1119 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1120 wp.length = sizeof(WINDOWPLACEMENT);
\r
1122 wp.showCmd = nCmdShow;
\r
1123 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1124 wp.rcNormalPosition.left = wpMain.x;
\r
1125 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1126 wp.rcNormalPosition.top = wpMain.y;
\r
1127 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1128 SetWindowPlacement(hwndMain, &wp);
\r
1130 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1132 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1133 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1135 if (hwndConsole) {
\r
1137 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1138 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1140 ShowWindow(hwndConsole, nCmdShow);
\r
1141 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1142 char buf[MSG_SIZ], *p = buf, *q;
\r
1143 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1145 q = strchr(p, ';');
\r
1147 if(*p) ChatPopUp(p);
\r
1150 SetActiveWindow(hwndConsole);
\r
1152 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1153 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1162 HMENU hmenu = GetMenu(hwndMain);
\r
1164 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1165 MF_BYCOMMAND|((appData.icsActive &&
\r
1166 *appData.icsCommPort != NULLCHAR) ?
\r
1167 MF_ENABLED : MF_GRAYED));
\r
1168 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1169 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1170 MF_CHECKED : MF_UNCHECKED));
\r
1173 //---------------------------------------------------------------------------------------------------------
\r
1175 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1176 #define XBOARD FALSE
\r
1178 #define OPTCHAR "/"
\r
1179 #define SEPCHAR "="
\r
1183 // front-end part of option handling
\r
1186 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1188 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1189 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1192 lf->lfEscapement = 0;
\r
1193 lf->lfOrientation = 0;
\r
1194 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1195 lf->lfItalic = mfp->italic;
\r
1196 lf->lfUnderline = mfp->underline;
\r
1197 lf->lfStrikeOut = mfp->strikeout;
\r
1198 lf->lfCharSet = mfp->charset;
\r
1199 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1200 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1201 lf->lfQuality = DEFAULT_QUALITY;
\r
1202 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1203 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1207 CreateFontInMF(MyFont *mf)
\r
1209 LFfromMFP(&mf->lf, &mf->mfp);
\r
1210 if (mf->hf) DeleteObject(mf->hf);
\r
1211 mf->hf = CreateFontIndirect(&mf->lf);
\r
1214 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1216 colorVariable[] = {
\r
1217 &whitePieceColor,
\r
1218 &blackPieceColor,
\r
1219 &lightSquareColor,
\r
1220 &darkSquareColor,
\r
1221 &highlightSquareColor,
\r
1222 &premoveHighlightColor,
\r
1224 &consoleBackgroundColor,
\r
1225 &appData.fontForeColorWhite,
\r
1226 &appData.fontBackColorWhite,
\r
1227 &appData.fontForeColorBlack,
\r
1228 &appData.fontBackColorBlack,
\r
1229 &appData.evalHistColorWhite,
\r
1230 &appData.evalHistColorBlack,
\r
1231 &appData.highlightArrowColor,
\r
1234 /* Command line font name parser. NULL name means do nothing.
\r
1235 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1236 For backward compatibility, syntax without the colon is also
\r
1237 accepted, but font names with digits in them won't work in that case.
\r
1240 ParseFontName(char *name, MyFontParams *mfp)
\r
1243 if (name == NULL) return;
\r
1245 q = strchr(p, ':');
\r
1247 if (q - p >= sizeof(mfp->faceName))
\r
1248 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1249 memcpy(mfp->faceName, p, q - p);
\r
1250 mfp->faceName[q - p] = NULLCHAR;
\r
1253 q = mfp->faceName;
\r
1254 while (*p && !isdigit(*p)) {
\r
1256 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1257 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1259 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1262 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1263 mfp->pointSize = (float) atof(p);
\r
1264 mfp->bold = (strchr(p, 'b') != NULL);
\r
1265 mfp->italic = (strchr(p, 'i') != NULL);
\r
1266 mfp->underline = (strchr(p, 'u') != NULL);
\r
1267 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1268 mfp->charset = DEFAULT_CHARSET;
\r
1269 q = strchr(p, 'c');
\r
1271 mfp->charset = (BYTE) atoi(q+1);
\r
1275 ParseFont(char *name, int number)
\r
1276 { // wrapper to shield back-end from 'font'
\r
1277 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1282 { // in WB we have a 2D array of fonts; this initializes their description
\r
1284 /* Point font array elements to structures and
\r
1285 parse default font names */
\r
1286 for (i=0; i<NUM_FONTS; i++) {
\r
1287 for (j=0; j<NUM_SIZES; j++) {
\r
1288 font[j][i] = &fontRec[j][i];
\r
1289 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1296 { // here we create the actual fonts from the selected descriptions
\r
1298 for (i=0; i<NUM_FONTS; i++) {
\r
1299 for (j=0; j<NUM_SIZES; j++) {
\r
1300 CreateFontInMF(font[j][i]);
\r
1304 /* Color name parser.
\r
1305 X version accepts X color names, but this one
\r
1306 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1308 ParseColorName(char *name)
\r
1310 int red, green, blue, count;
\r
1311 char buf[MSG_SIZ];
\r
1313 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1315 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1316 &red, &green, &blue);
\r
1319 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1320 DisplayError(buf, 0);
\r
1321 return RGB(0, 0, 0);
\r
1323 return PALETTERGB(red, green, blue);
\r
1327 ParseColor(int n, char *name)
\r
1328 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1329 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1333 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1335 char *e = argValue;
\r
1339 if (*e == 'b') eff |= CFE_BOLD;
\r
1340 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1341 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1342 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1343 else if (*e == '#' || isdigit(*e)) break;
\r
1347 *color = ParseColorName(e);
\r
1351 ParseTextAttribs(ColorClass cc, char *s)
\r
1352 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1353 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1354 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1358 ParseBoardSize(void *addr, char *name)
\r
1359 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1360 BoardSize bs = SizeTiny;
\r
1361 while (sizeInfo[bs].name != NULL) {
\r
1362 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1363 *(BoardSize *)addr = bs;
\r
1368 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1373 { // [HGM] import name from appData first
\r
1376 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1377 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1378 textAttribs[cc].sound.data = NULL;
\r
1379 MyLoadSound(&textAttribs[cc].sound);
\r
1381 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1382 textAttribs[cc].sound.name = strdup("");
\r
1383 textAttribs[cc].sound.data = NULL;
\r
1385 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1386 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1387 sounds[sc].data = NULL;
\r
1388 MyLoadSound(&sounds[sc]);
\r
1393 SetCommPortDefaults()
\r
1395 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1396 dcb.DCBlength = sizeof(DCB);
\r
1397 dcb.BaudRate = 9600;
\r
1398 dcb.fBinary = TRUE;
\r
1399 dcb.fParity = FALSE;
\r
1400 dcb.fOutxCtsFlow = FALSE;
\r
1401 dcb.fOutxDsrFlow = FALSE;
\r
1402 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1403 dcb.fDsrSensitivity = FALSE;
\r
1404 dcb.fTXContinueOnXoff = TRUE;
\r
1405 dcb.fOutX = FALSE;
\r
1407 dcb.fNull = FALSE;
\r
1408 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1409 dcb.fAbortOnError = FALSE;
\r
1411 dcb.Parity = SPACEPARITY;
\r
1412 dcb.StopBits = ONESTOPBIT;
\r
1415 // [HGM] args: these three cases taken out to stay in front-end
\r
1417 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1418 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1419 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1420 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1422 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1423 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1424 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1425 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1426 ad->argName, mfp->faceName, mfp->pointSize,
\r
1427 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1428 mfp->bold ? "b" : "",
\r
1429 mfp->italic ? "i" : "",
\r
1430 mfp->underline ? "u" : "",
\r
1431 mfp->strikeout ? "s" : "",
\r
1432 (int)mfp->charset);
\r
1438 { // [HGM] copy the names from the internal WB variables to appData
\r
1441 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1442 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1443 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1444 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1448 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1449 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1450 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1451 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1452 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1453 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1454 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1455 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1456 (ta->effects) ? " " : "",
\r
1457 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1461 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1462 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1463 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1464 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1465 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1469 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1470 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1471 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1475 ParseCommPortSettings(char *s)
\r
1476 { // wrapper to keep dcb from back-end
\r
1477 ParseCommSettings(s, &dcb);
\r
1482 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1483 GetActualPlacement(hwndMain, &wpMain);
\r
1484 GetActualPlacement(hwndConsole, &wpConsole);
\r
1485 GetActualPlacement(commentDialog, &wpComment);
\r
1486 GetActualPlacement(editTagsDialog, &wpTags);
\r
1487 GetActualPlacement(gameListDialog, &wpGameList);
\r
1488 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1489 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1490 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1494 PrintCommPortSettings(FILE *f, char *name)
\r
1495 { // wrapper to shield back-end from DCB
\r
1496 PrintCommSettings(f, name, &dcb);
\r
1500 MySearchPath(char *installDir, char *name, char *fullname)
\r
1502 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1503 if(name[0]== '%') {
\r
1504 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1505 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1506 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1507 *strchr(buf, '%') = 0;
\r
1508 strcat(fullname, getenv(buf));
\r
1509 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1511 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1512 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1513 return (int) strlen(fullname);
\r
1515 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1519 MyGetFullPathName(char *name, char *fullname)
\r
1522 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1527 { // [HGM] args: allows testing if main window is realized from back-end
\r
1528 return hwndMain != NULL;
\r
1532 PopUpStartupDialog()
\r
1536 LoadLanguageFile(appData.language);
\r
1537 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1538 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1539 FreeProcInstance(lpProc);
\r
1542 /*---------------------------------------------------------------------------*\
\r
1544 * GDI board drawing routines
\r
1546 \*---------------------------------------------------------------------------*/
\r
1548 /* [AS] Draw square using background texture */
\r
1549 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1554 return; /* Should never happen! */
\r
1557 SetGraphicsMode( dst, GM_ADVANCED );
\r
1564 /* X reflection */
\r
1569 x.eDx = (FLOAT) dw + dx - 1;
\r
1572 SetWorldTransform( dst, &x );
\r
1575 /* Y reflection */
\r
1581 x.eDy = (FLOAT) dh + dy - 1;
\r
1583 SetWorldTransform( dst, &x );
\r
1591 x.eDx = (FLOAT) dx;
\r
1592 x.eDy = (FLOAT) dy;
\r
1595 SetWorldTransform( dst, &x );
\r
1599 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1607 SetWorldTransform( dst, &x );
\r
1609 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1612 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1614 PM_WP = (int) WhitePawn,
\r
1615 PM_WN = (int) WhiteKnight,
\r
1616 PM_WB = (int) WhiteBishop,
\r
1617 PM_WR = (int) WhiteRook,
\r
1618 PM_WQ = (int) WhiteQueen,
\r
1619 PM_WF = (int) WhiteFerz,
\r
1620 PM_WW = (int) WhiteWazir,
\r
1621 PM_WE = (int) WhiteAlfil,
\r
1622 PM_WM = (int) WhiteMan,
\r
1623 PM_WO = (int) WhiteCannon,
\r
1624 PM_WU = (int) WhiteUnicorn,
\r
1625 PM_WH = (int) WhiteNightrider,
\r
1626 PM_WA = (int) WhiteAngel,
\r
1627 PM_WC = (int) WhiteMarshall,
\r
1628 PM_WAB = (int) WhiteCardinal,
\r
1629 PM_WD = (int) WhiteDragon,
\r
1630 PM_WL = (int) WhiteLance,
\r
1631 PM_WS = (int) WhiteCobra,
\r
1632 PM_WV = (int) WhiteFalcon,
\r
1633 PM_WSG = (int) WhiteSilver,
\r
1634 PM_WG = (int) WhiteGrasshopper,
\r
1635 PM_WK = (int) WhiteKing,
\r
1636 PM_BP = (int) BlackPawn,
\r
1637 PM_BN = (int) BlackKnight,
\r
1638 PM_BB = (int) BlackBishop,
\r
1639 PM_BR = (int) BlackRook,
\r
1640 PM_BQ = (int) BlackQueen,
\r
1641 PM_BF = (int) BlackFerz,
\r
1642 PM_BW = (int) BlackWazir,
\r
1643 PM_BE = (int) BlackAlfil,
\r
1644 PM_BM = (int) BlackMan,
\r
1645 PM_BO = (int) BlackCannon,
\r
1646 PM_BU = (int) BlackUnicorn,
\r
1647 PM_BH = (int) BlackNightrider,
\r
1648 PM_BA = (int) BlackAngel,
\r
1649 PM_BC = (int) BlackMarshall,
\r
1650 PM_BG = (int) BlackGrasshopper,
\r
1651 PM_BAB = (int) BlackCardinal,
\r
1652 PM_BD = (int) BlackDragon,
\r
1653 PM_BL = (int) BlackLance,
\r
1654 PM_BS = (int) BlackCobra,
\r
1655 PM_BV = (int) BlackFalcon,
\r
1656 PM_BSG = (int) BlackSilver,
\r
1657 PM_BK = (int) BlackKing
\r
1660 static HFONT hPieceFont = NULL;
\r
1661 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1662 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1663 static int fontBitmapSquareSize = 0;
\r
1664 static char pieceToFontChar[(int) EmptySquare] =
\r
1665 { 'p', 'n', 'b', 'r', 'q',
\r
1666 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1667 'k', 'o', 'm', 'v', 't', 'w',
\r
1668 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1671 extern BOOL SetCharTable( char *table, const char * map );
\r
1672 /* [HGM] moved to backend.c */
\r
1674 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1677 BYTE r1 = GetRValue( color );
\r
1678 BYTE g1 = GetGValue( color );
\r
1679 BYTE b1 = GetBValue( color );
\r
1685 /* Create a uniform background first */
\r
1686 hbrush = CreateSolidBrush( color );
\r
1687 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1688 FillRect( hdc, &rc, hbrush );
\r
1689 DeleteObject( hbrush );
\r
1692 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1693 int steps = squareSize / 2;
\r
1696 for( i=0; i<steps; i++ ) {
\r
1697 BYTE r = r1 - (r1-r2) * i / steps;
\r
1698 BYTE g = g1 - (g1-g2) * i / steps;
\r
1699 BYTE b = b1 - (b1-b2) * i / steps;
\r
1701 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1702 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1703 FillRect( hdc, &rc, hbrush );
\r
1704 DeleteObject(hbrush);
\r
1707 else if( mode == 2 ) {
\r
1708 /* Diagonal gradient, good more or less for every piece */
\r
1709 POINT triangle[3];
\r
1710 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1711 HBRUSH hbrush_old;
\r
1712 int steps = squareSize;
\r
1715 triangle[0].x = squareSize - steps;
\r
1716 triangle[0].y = squareSize;
\r
1717 triangle[1].x = squareSize;
\r
1718 triangle[1].y = squareSize;
\r
1719 triangle[2].x = squareSize;
\r
1720 triangle[2].y = squareSize - steps;
\r
1722 for( i=0; i<steps; i++ ) {
\r
1723 BYTE r = r1 - (r1-r2) * i / steps;
\r
1724 BYTE g = g1 - (g1-g2) * i / steps;
\r
1725 BYTE b = b1 - (b1-b2) * i / steps;
\r
1727 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1728 hbrush_old = SelectObject( hdc, hbrush );
\r
1729 Polygon( hdc, triangle, 3 );
\r
1730 SelectObject( hdc, hbrush_old );
\r
1731 DeleteObject(hbrush);
\r
1736 SelectObject( hdc, hpen );
\r
1741 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1742 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1743 piece: follow the steps as explained below.
\r
1745 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1749 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1753 int backColor = whitePieceColor;
\r
1754 int foreColor = blackPieceColor;
\r
1756 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1757 backColor = appData.fontBackColorWhite;
\r
1758 foreColor = appData.fontForeColorWhite;
\r
1760 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1761 backColor = appData.fontBackColorBlack;
\r
1762 foreColor = appData.fontForeColorBlack;
\r
1766 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1768 hbm_old = SelectObject( hdc, hbm );
\r
1772 rc.right = squareSize;
\r
1773 rc.bottom = squareSize;
\r
1775 /* Step 1: background is now black */
\r
1776 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1778 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1780 pt.x = (squareSize - sz.cx) / 2;
\r
1781 pt.y = (squareSize - sz.cy) / 2;
\r
1783 SetBkMode( hdc, TRANSPARENT );
\r
1784 SetTextColor( hdc, chroma );
\r
1785 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1786 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1788 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1789 /* Step 3: the area outside the piece is filled with white */
\r
1790 // FloodFill( hdc, 0, 0, chroma );
\r
1791 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1792 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1793 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1794 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1795 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1797 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1798 but if the start point is not inside the piece we're lost!
\r
1799 There should be a better way to do this... if we could create a region or path
\r
1800 from the fill operation we would be fine for example.
\r
1802 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1803 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1805 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1806 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1807 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1809 SelectObject( dc2, bm2 );
\r
1810 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1811 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1812 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1813 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1814 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1817 DeleteObject( bm2 );
\r
1820 SetTextColor( hdc, 0 );
\r
1822 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1823 draw the piece again in black for safety.
\r
1825 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1827 SelectObject( hdc, hbm_old );
\r
1829 if( hPieceMask[index] != NULL ) {
\r
1830 DeleteObject( hPieceMask[index] );
\r
1833 hPieceMask[index] = hbm;
\r
1836 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1838 SelectObject( hdc, hbm );
\r
1841 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1842 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1843 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1845 SelectObject( dc1, hPieceMask[index] );
\r
1846 SelectObject( dc2, bm2 );
\r
1847 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1848 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1851 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1852 the piece background and deletes (makes transparent) the rest.
\r
1853 Thanks to that mask, we are free to paint the background with the greates
\r
1854 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1855 We use this, to make gradients and give the pieces a "roundish" look.
\r
1857 SetPieceBackground( hdc, backColor, 2 );
\r
1858 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1862 DeleteObject( bm2 );
\r
1865 SetTextColor( hdc, foreColor );
\r
1866 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1868 SelectObject( hdc, hbm_old );
\r
1870 if( hPieceFace[index] != NULL ) {
\r
1871 DeleteObject( hPieceFace[index] );
\r
1874 hPieceFace[index] = hbm;
\r
1877 static int TranslatePieceToFontPiece( int piece )
\r
1907 case BlackMarshall:
\r
1911 case BlackNightrider:
\r
1917 case BlackUnicorn:
\r
1921 case BlackGrasshopper:
\r
1933 case BlackCardinal:
\r
1940 case WhiteMarshall:
\r
1944 case WhiteNightrider:
\r
1950 case WhiteUnicorn:
\r
1954 case WhiteGrasshopper:
\r
1966 case WhiteCardinal:
\r
1975 void CreatePiecesFromFont()
\r
1978 HDC hdc_window = NULL;
\r
1984 if( fontBitmapSquareSize < 0 ) {
\r
1985 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1989 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
1990 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1991 fontBitmapSquareSize = -1;
\r
1995 if( fontBitmapSquareSize != squareSize ) {
\r
1996 hdc_window = GetDC( hwndMain );
\r
1997 hdc = CreateCompatibleDC( hdc_window );
\r
1999 if( hPieceFont != NULL ) {
\r
2000 DeleteObject( hPieceFont );
\r
2003 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2004 hPieceMask[i] = NULL;
\r
2005 hPieceFace[i] = NULL;
\r
2011 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2012 fontHeight = appData.fontPieceSize;
\r
2015 fontHeight = (fontHeight * squareSize) / 100;
\r
2017 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2019 lf.lfEscapement = 0;
\r
2020 lf.lfOrientation = 0;
\r
2021 lf.lfWeight = FW_NORMAL;
\r
2023 lf.lfUnderline = 0;
\r
2024 lf.lfStrikeOut = 0;
\r
2025 lf.lfCharSet = DEFAULT_CHARSET;
\r
2026 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2027 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2028 lf.lfQuality = PROOF_QUALITY;
\r
2029 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2030 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2031 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2033 hPieceFont = CreateFontIndirect( &lf );
\r
2035 if( hPieceFont == NULL ) {
\r
2036 fontBitmapSquareSize = -2;
\r
2039 /* Setup font-to-piece character table */
\r
2040 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2041 /* No (or wrong) global settings, try to detect the font */
\r
2042 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2044 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2046 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2047 /* DiagramTT* family */
\r
2048 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2050 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2051 /* Fairy symbols */
\r
2052 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2054 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2055 /* Good Companion (Some characters get warped as literal :-( */
\r
2056 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2057 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2058 SetCharTable(pieceToFontChar, s);
\r
2061 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2062 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2066 /* Create bitmaps */
\r
2067 hfont_old = SelectObject( hdc, hPieceFont );
\r
2068 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2069 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2070 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2072 SelectObject( hdc, hfont_old );
\r
2074 fontBitmapSquareSize = squareSize;
\r
2078 if( hdc != NULL ) {
\r
2082 if( hdc_window != NULL ) {
\r
2083 ReleaseDC( hwndMain, hdc_window );
\r
2088 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2092 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2093 if (gameInfo.event &&
\r
2094 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2095 strcmp(name, "k80s") == 0) {
\r
2096 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2098 return LoadBitmap(hinst, name);
\r
2102 /* Insert a color into the program's logical palette
\r
2103 structure. This code assumes the given color is
\r
2104 the result of the RGB or PALETTERGB macro, and it
\r
2105 knows how those macros work (which is documented).
\r
2108 InsertInPalette(COLORREF color)
\r
2110 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2112 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2113 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2114 pLogPal->palNumEntries--;
\r
2118 pe->peFlags = (char) 0;
\r
2119 pe->peRed = (char) (0xFF & color);
\r
2120 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2121 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2127 InitDrawingColors()
\r
2129 if (pLogPal == NULL) {
\r
2130 /* Allocate enough memory for a logical palette with
\r
2131 * PALETTESIZE entries and set the size and version fields
\r
2132 * of the logical palette structure.
\r
2134 pLogPal = (NPLOGPALETTE)
\r
2135 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2136 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2137 pLogPal->palVersion = 0x300;
\r
2139 pLogPal->palNumEntries = 0;
\r
2141 InsertInPalette(lightSquareColor);
\r
2142 InsertInPalette(darkSquareColor);
\r
2143 InsertInPalette(whitePieceColor);
\r
2144 InsertInPalette(blackPieceColor);
\r
2145 InsertInPalette(highlightSquareColor);
\r
2146 InsertInPalette(premoveHighlightColor);
\r
2148 /* create a logical color palette according the information
\r
2149 * in the LOGPALETTE structure.
\r
2151 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2153 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2154 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2155 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2156 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2157 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2158 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2159 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2160 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2161 /* [AS] Force rendering of the font-based pieces */
\r
2162 if( fontBitmapSquareSize > 0 ) {
\r
2163 fontBitmapSquareSize = 0;
\r
2169 BoardWidth(int boardSize, int n)
\r
2170 { /* [HGM] argument n added to allow different width and height */
\r
2171 int lineGap = sizeInfo[boardSize].lineGap;
\r
2173 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2174 lineGap = appData.overrideLineGap;
\r
2177 return (n + 1) * lineGap +
\r
2178 n * sizeInfo[boardSize].squareSize;
\r
2181 /* Respond to board resize by dragging edge */
\r
2183 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2185 BoardSize newSize = NUM_SIZES - 1;
\r
2186 static int recurse = 0;
\r
2187 if (IsIconic(hwndMain)) return;
\r
2188 if (recurse > 0) return;
\r
2190 while (newSize > 0) {
\r
2191 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2192 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2193 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2196 boardSize = newSize;
\r
2197 InitDrawingSizes(boardSize, flags);
\r
2202 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2205 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2207 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2208 ChessSquare piece;
\r
2209 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2211 SIZE clockSize, messageSize;
\r
2213 char buf[MSG_SIZ];
\r
2215 HMENU hmenu = GetMenu(hwndMain);
\r
2216 RECT crect, wrect, oldRect;
\r
2218 LOGBRUSH logbrush;
\r
2220 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2221 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2223 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2224 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2226 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2227 oldRect.top = wpMain.y;
\r
2228 oldRect.right = wpMain.x + wpMain.width;
\r
2229 oldRect.bottom = wpMain.y + wpMain.height;
\r
2231 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2232 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2233 squareSize = sizeInfo[boardSize].squareSize;
\r
2234 lineGap = sizeInfo[boardSize].lineGap;
\r
2235 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2237 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2238 lineGap = appData.overrideLineGap;
\r
2241 if (tinyLayout != oldTinyLayout) {
\r
2242 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2244 style &= ~WS_SYSMENU;
\r
2245 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2246 "&Minimize\tCtrl+F4");
\r
2248 style |= WS_SYSMENU;
\r
2249 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2251 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2253 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2254 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2255 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2257 DrawMenuBar(hwndMain);
\r
2260 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2261 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2263 /* Get text area sizes */
\r
2264 hdc = GetDC(hwndMain);
\r
2265 if (appData.clockMode) {
\r
2266 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2268 snprintf(buf, MSG_SIZ, _("White"));
\r
2270 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2271 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2272 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2273 str = _("We only care about the height here");
\r
2274 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2275 SelectObject(hdc, oldFont);
\r
2276 ReleaseDC(hwndMain, hdc);
\r
2278 /* Compute where everything goes */
\r
2279 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2280 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2281 logoHeight = 2*clockSize.cy;
\r
2282 leftLogoRect.left = OUTER_MARGIN;
\r
2283 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2284 leftLogoRect.top = OUTER_MARGIN;
\r
2285 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2287 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2288 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2289 rightLogoRect.top = OUTER_MARGIN;
\r
2290 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2293 whiteRect.left = leftLogoRect.right;
\r
2294 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2295 whiteRect.top = OUTER_MARGIN;
\r
2296 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2298 blackRect.right = rightLogoRect.left;
\r
2299 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2300 blackRect.top = whiteRect.top;
\r
2301 blackRect.bottom = whiteRect.bottom;
\r
2303 whiteRect.left = OUTER_MARGIN;
\r
2304 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2305 whiteRect.top = OUTER_MARGIN;
\r
2306 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2308 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2309 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2310 blackRect.top = whiteRect.top;
\r
2311 blackRect.bottom = whiteRect.bottom;
\r
2313 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2316 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2317 if (appData.showButtonBar) {
\r
2318 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2319 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2321 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2323 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2324 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2326 boardRect.left = OUTER_MARGIN;
\r
2327 boardRect.right = boardRect.left + boardWidth;
\r
2328 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2329 boardRect.bottom = boardRect.top + boardHeight;
\r
2331 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2332 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2333 oldBoardSize = boardSize;
\r
2334 oldTinyLayout = tinyLayout;
\r
2335 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2336 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2337 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2338 winW *= 1 + twoBoards;
\r
2339 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2340 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2341 wpMain.height = winH; // without disturbing window attachments
\r
2342 GetWindowRect(hwndMain, &wrect);
\r
2343 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2344 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2346 // [HGM] placement: let attached windows follow size change.
\r
2347 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2348 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2349 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2350 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2351 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2353 /* compensate if menu bar wrapped */
\r
2354 GetClientRect(hwndMain, &crect);
\r
2355 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2356 wpMain.height += offby;
\r
2358 case WMSZ_TOPLEFT:
\r
2359 SetWindowPos(hwndMain, NULL,
\r
2360 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2361 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2364 case WMSZ_TOPRIGHT:
\r
2366 SetWindowPos(hwndMain, NULL,
\r
2367 wrect.left, wrect.bottom - wpMain.height,
\r
2368 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2371 case WMSZ_BOTTOMLEFT:
\r
2373 SetWindowPos(hwndMain, NULL,
\r
2374 wrect.right - wpMain.width, wrect.top,
\r
2375 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2378 case WMSZ_BOTTOMRIGHT:
\r
2382 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2383 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2388 for (i = 0; i < N_BUTTONS; i++) {
\r
2389 if (buttonDesc[i].hwnd != NULL) {
\r
2390 DestroyWindow(buttonDesc[i].hwnd);
\r
2391 buttonDesc[i].hwnd = NULL;
\r
2393 if (appData.showButtonBar) {
\r
2394 buttonDesc[i].hwnd =
\r
2395 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2396 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2397 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2398 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2399 (HMENU) buttonDesc[i].id,
\r
2400 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2402 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2403 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2404 MAKELPARAM(FALSE, 0));
\r
2406 if (buttonDesc[i].id == IDM_Pause)
\r
2407 hwndPause = buttonDesc[i].hwnd;
\r
2408 buttonDesc[i].wndproc = (WNDPROC)
\r
2409 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2412 if (gridPen != NULL) DeleteObject(gridPen);
\r
2413 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2414 if (premovePen != NULL) DeleteObject(premovePen);
\r
2415 if (lineGap != 0) {
\r
2416 logbrush.lbStyle = BS_SOLID;
\r
2417 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2419 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2420 lineGap, &logbrush, 0, NULL);
\r
2421 logbrush.lbColor = highlightSquareColor;
\r
2423 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2424 lineGap, &logbrush, 0, NULL);
\r
2426 logbrush.lbColor = premoveHighlightColor;
\r
2428 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2429 lineGap, &logbrush, 0, NULL);
\r
2431 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2432 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2433 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2434 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2435 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2436 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2437 BOARD_WIDTH * (squareSize + lineGap);
\r
2438 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2440 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2441 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2442 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2443 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2444 lineGap / 2 + (i * (squareSize + lineGap));
\r
2445 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2446 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2447 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2451 /* [HGM] Licensing requirement */
\r
2453 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2456 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2458 GothicPopUp( "", VariantNormal);
\r
2461 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2463 /* Load piece bitmaps for this board size */
\r
2464 for (i=0; i<=2; i++) {
\r
2465 for (piece = WhitePawn;
\r
2466 (int) piece < (int) BlackPawn;
\r
2467 piece = (ChessSquare) ((int) piece + 1)) {
\r
2468 if (pieceBitmap[i][piece] != NULL)
\r
2469 DeleteObject(pieceBitmap[i][piece]);
\r
2473 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2474 // Orthodox Chess pieces
\r
2475 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2476 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2477 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2478 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2479 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2480 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2481 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2482 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2483 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2484 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2485 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2486 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2487 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2488 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2489 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2490 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2491 // in Shogi, Hijack the unused Queen for Lance
\r
2492 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2493 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2494 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2496 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2497 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2498 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2501 if(squareSize <= 72 && squareSize >= 33) {
\r
2502 /* A & C are available in most sizes now */
\r
2503 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2504 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2505 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2506 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2507 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2508 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2509 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2510 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2511 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2512 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2513 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2514 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2515 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2516 } else { // Smirf-like
\r
2517 if(gameInfo.variant == VariantSChess) {
\r
2518 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2519 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2520 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2522 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2523 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2524 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2527 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2528 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2529 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2530 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2531 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2532 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2533 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2534 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2535 } else { // WinBoard standard
\r
2536 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2537 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2538 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2543 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2544 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2545 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2546 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2547 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2548 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2549 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2550 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2551 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2552 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2553 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2556 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2557 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2558 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2559 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2560 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2561 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2562 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2563 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2564 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2565 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2566 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2567 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2568 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2569 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2570 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2571 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2575 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2576 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2577 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2578 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2579 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2580 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2581 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2582 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2583 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2584 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2585 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2586 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2587 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2589 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2590 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2591 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2592 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2593 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2594 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2595 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2596 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2597 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2598 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2599 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2600 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2603 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2604 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2605 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2606 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2607 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2608 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2609 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2610 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2611 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2612 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2613 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2614 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2615 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2616 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2617 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2621 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2622 /* special Shogi support in this size */
\r
2623 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2624 for (piece = WhitePawn;
\r
2625 (int) piece < (int) BlackPawn;
\r
2626 piece = (ChessSquare) ((int) piece + 1)) {
\r
2627 if (pieceBitmap[i][piece] != NULL)
\r
2628 DeleteObject(pieceBitmap[i][piece]);
\r
2631 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2632 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2633 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2634 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2635 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2636 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2637 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2638 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2639 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2640 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2641 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2642 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2643 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2644 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2645 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2646 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2647 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2648 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2649 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2650 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2651 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2652 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2653 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2654 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2655 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2656 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2657 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2658 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2659 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2660 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2661 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2662 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2663 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2664 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2665 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2666 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2667 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2668 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2669 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2670 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2671 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2672 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2678 PieceBitmap(ChessSquare p, int kind)
\r
2680 if ((int) p >= (int) BlackPawn)
\r
2681 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2683 return pieceBitmap[kind][(int) p];
\r
2686 /***************************************************************/
\r
2688 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2689 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2691 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2692 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2696 SquareToPos(int row, int column, int * x, int * y)
\r
2699 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2700 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2702 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2703 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2708 DrawCoordsOnDC(HDC hdc)
\r
2710 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2711 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2712 char str[2] = { NULLCHAR, NULLCHAR };
\r
2713 int oldMode, oldAlign, x, y, start, i;
\r
2717 if (!appData.showCoords)
\r
2720 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2722 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2723 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2724 oldAlign = GetTextAlign(hdc);
\r
2725 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2727 y = boardRect.top + lineGap;
\r
2728 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2730 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2731 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2732 str[0] = files[start + i];
\r
2733 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2734 y += squareSize + lineGap;
\r
2737 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2739 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2740 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2741 str[0] = ranks[start + i];
\r
2742 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2743 x += squareSize + lineGap;
\r
2746 SelectObject(hdc, oldBrush);
\r
2747 SetBkMode(hdc, oldMode);
\r
2748 SetTextAlign(hdc, oldAlign);
\r
2749 SelectObject(hdc, oldFont);
\r
2753 DrawGridOnDC(HDC hdc)
\r
2757 if (lineGap != 0) {
\r
2758 oldPen = SelectObject(hdc, gridPen);
\r
2759 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2760 SelectObject(hdc, oldPen);
\r
2764 #define HIGHLIGHT_PEN 0
\r
2765 #define PREMOVE_PEN 1
\r
2768 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2771 HPEN oldPen, hPen;
\r
2772 if (lineGap == 0) return;
\r
2774 x1 = boardRect.left +
\r
2775 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2776 y1 = boardRect.top +
\r
2777 lineGap/2 + y * (squareSize + lineGap);
\r
2779 x1 = boardRect.left +
\r
2780 lineGap/2 + x * (squareSize + lineGap);
\r
2781 y1 = boardRect.top +
\r
2782 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2784 hPen = pen ? premovePen : highlightPen;
\r
2785 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2786 MoveToEx(hdc, x1, y1, NULL);
\r
2787 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2788 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2789 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2790 LineTo(hdc, x1, y1);
\r
2791 SelectObject(hdc, oldPen);
\r
2795 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2798 for (i=0; i<2; i++) {
\r
2799 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2800 DrawHighlightOnDC(hdc, TRUE,
\r
2801 h->sq[i].x, h->sq[i].y,
\r
2806 /* Note: sqcolor is used only in monoMode */
\r
2807 /* Note that this code is largely duplicated in woptions.c,
\r
2808 function DrawSampleSquare, so that needs to be updated too */
\r
2810 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2812 HBITMAP oldBitmap;
\r
2816 if (appData.blindfold) return;
\r
2818 /* [AS] Use font-based pieces if needed */
\r
2819 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2820 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2821 CreatePiecesFromFont();
\r
2823 if( fontBitmapSquareSize == squareSize ) {
\r
2824 int index = TranslatePieceToFontPiece(piece);
\r
2826 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2828 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2829 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2833 squareSize, squareSize,
\r
2838 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2840 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2841 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2845 squareSize, squareSize,
\r
2854 if (appData.monoMode) {
\r
2855 SelectObject(tmphdc, PieceBitmap(piece,
\r
2856 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2857 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2858 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2860 tmpSize = squareSize;
\r
2862 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2863 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2864 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2865 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2866 x += (squareSize - minorSize)>>1;
\r
2867 y += squareSize - minorSize - 2;
\r
2868 tmpSize = minorSize;
\r
2870 if (color || appData.allWhite ) {
\r
2871 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2873 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2874 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2875 if(appData.upsideDown && color==flipView)
\r
2876 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2878 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2879 /* Use black for outline of white pieces */
\r
2880 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2881 if(appData.upsideDown && color==flipView)
\r
2882 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2884 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2886 /* Use square color for details of black pieces */
\r
2887 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2888 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2889 if(appData.upsideDown && !flipView)
\r
2890 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2892 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2894 SelectObject(hdc, oldBrush);
\r
2895 SelectObject(tmphdc, oldBitmap);
\r
2899 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2900 int GetBackTextureMode( int algo )
\r
2902 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2906 case BACK_TEXTURE_MODE_PLAIN:
\r
2907 result = 1; /* Always use identity map */
\r
2909 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2910 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2918 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2919 to handle redraws cleanly (as random numbers would always be different).
\r
2921 VOID RebuildTextureSquareInfo()
\r
2931 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2933 if( liteBackTexture != NULL ) {
\r
2934 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2935 lite_w = bi.bmWidth;
\r
2936 lite_h = bi.bmHeight;
\r
2940 if( darkBackTexture != NULL ) {
\r
2941 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2942 dark_w = bi.bmWidth;
\r
2943 dark_h = bi.bmHeight;
\r
2947 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2948 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2949 if( (col + row) & 1 ) {
\r
2951 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2952 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2953 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2955 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2956 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2957 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2959 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2960 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2965 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2966 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2967 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2969 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2970 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2971 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2973 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2974 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2981 /* [AS] Arrow highlighting support */
\r
2983 static double A_WIDTH = 5; /* Width of arrow body */
\r
2985 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2986 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2988 static double Sqr( double x )
\r
2993 static int Round( double x )
\r
2995 return (int) (x + 0.5);
\r
2998 /* Draw an arrow between two points using current settings */
\r
2999 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3002 double dx, dy, j, k, x, y;
\r
3004 if( d_x == s_x ) {
\r
3005 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3007 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3010 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3011 arrow[1].y = d_y - h;
\r
3013 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3014 arrow[2].y = d_y - h;
\r
3019 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3020 arrow[5].y = d_y - h;
\r
3022 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3023 arrow[4].y = d_y - h;
\r
3025 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3028 else if( d_y == s_y ) {
\r
3029 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3032 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3034 arrow[1].x = d_x - w;
\r
3035 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3037 arrow[2].x = d_x - w;
\r
3038 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3043 arrow[5].x = d_x - w;
\r
3044 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3046 arrow[4].x = d_x - w;
\r
3047 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3050 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3053 /* [AS] Needed a lot of paper for this! :-) */
\r
3054 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3055 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3057 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3059 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3064 arrow[0].x = Round(x - j);
\r
3065 arrow[0].y = Round(y + j*dx);
\r
3067 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3068 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3071 x = (double) d_x - k;
\r
3072 y = (double) d_y - k*dy;
\r
3075 x = (double) d_x + k;
\r
3076 y = (double) d_y + k*dy;
\r
3079 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3081 arrow[6].x = Round(x - j);
\r
3082 arrow[6].y = Round(y + j*dx);
\r
3084 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3085 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3087 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3088 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3093 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3094 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3097 Polygon( hdc, arrow, 7 );
\r
3100 /* [AS] Draw an arrow between two squares */
\r
3101 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3103 int s_x, s_y, d_x, d_y;
\r
3110 if( s_col == d_col && s_row == d_row ) {
\r
3114 /* Get source and destination points */
\r
3115 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3116 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3119 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3121 else if( d_y < s_y ) {
\r
3122 d_y += squareSize / 2 + squareSize / 4;
\r
3125 d_y += squareSize / 2;
\r
3129 d_x += squareSize / 2 - squareSize / 4;
\r
3131 else if( d_x < s_x ) {
\r
3132 d_x += squareSize / 2 + squareSize / 4;
\r
3135 d_x += squareSize / 2;
\r
3138 s_x += squareSize / 2;
\r
3139 s_y += squareSize / 2;
\r
3141 /* Adjust width */
\r
3142 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3145 stLB.lbStyle = BS_SOLID;
\r
3146 stLB.lbColor = appData.highlightArrowColor;
\r
3149 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3150 holdpen = SelectObject( hdc, hpen );
\r
3151 hbrush = CreateBrushIndirect( &stLB );
\r
3152 holdbrush = SelectObject( hdc, hbrush );
\r
3154 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3156 SelectObject( hdc, holdpen );
\r
3157 SelectObject( hdc, holdbrush );
\r
3158 DeleteObject( hpen );
\r
3159 DeleteObject( hbrush );
\r
3162 BOOL HasHighlightInfo()
\r
3164 BOOL result = FALSE;
\r
3166 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3167 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3175 BOOL IsDrawArrowEnabled()
\r
3177 BOOL result = FALSE;
\r
3179 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3186 VOID DrawArrowHighlight( HDC hdc )
\r
3188 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3189 DrawArrowBetweenSquares( hdc,
\r
3190 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3191 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3195 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3197 HRGN result = NULL;
\r