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, 2012 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, OPT_Grid },
\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_KBUP_NAVIGATION
\r
741 #define JAWS_KBDOWN_NAVIGATION
\r
742 #define JAWS_MENU_ITEMS
\r
743 #define JAWS_SILENCE
\r
744 #define JAWS_REPLAY
\r
746 #define JAWS_COPYRIGHT
\r
747 #define JAWS_DELETE(X) X
\r
748 #define SAYMACHINEMOVE()
\r
752 /*---------------------------------------------------------------------------*\
\r
756 \*---------------------------------------------------------------------------*/
\r
759 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
760 LPSTR lpCmdLine, int nCmdShow)
\r
763 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
764 // INITCOMMONCONTROLSEX ex;
\r
768 LoadLibrary("RICHED32.DLL");
\r
769 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
771 if (!InitApplication(hInstance)) {
\r
774 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
781 // InitCommonControlsEx(&ex);
\r
782 InitCommonControls();
\r
784 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
785 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
786 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
788 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
790 while (GetMessage(&msg, /* message structure */
\r
791 NULL, /* handle of window receiving the message */
\r
792 0, /* lowest message to examine */
\r
793 0)) /* highest message to examine */
\r
796 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
797 // [HGM] navigate: switch between all windows with tab
\r
798 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
799 int i, currentElement = 0;
\r
801 // first determine what element of the chain we come from (if any)
\r
802 if(appData.icsActive) {
\r
803 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
804 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
806 if(engineOutputDialog && EngineOutputIsUp()) {
\r
807 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
808 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
810 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
811 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
813 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
814 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
815 if(msg.hwnd == e1) currentElement = 2; else
\r
816 if(msg.hwnd == e2) currentElement = 3; else
\r
817 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
818 if(msg.hwnd == mh) currentElement = 4; else
\r
819 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
820 if(msg.hwnd == hText) currentElement = 5; else
\r
821 if(msg.hwnd == hInput) currentElement = 6; else
\r
822 for (i = 0; i < N_BUTTONS; i++) {
\r
823 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
826 // determine where to go to
\r
827 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
829 currentElement = (currentElement + direction) % 7;
\r
830 switch(currentElement) {
\r
832 h = hwndMain; break; // passing this case always makes the loop exit
\r
834 h = buttonDesc[0].hwnd; break; // could be NULL
\r
836 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
839 if(!EngineOutputIsUp()) continue;
\r
842 if(!MoveHistoryIsUp()) continue;
\r
844 // case 6: // input to eval graph does not seem to get here!
\r
845 // if(!EvalGraphIsUp()) continue;
\r
846 // h = evalGraphDialog; break;
\r
848 if(!appData.icsActive) continue;
\r
852 if(!appData.icsActive) continue;
\r
858 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
859 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
862 continue; // this message now has been processed
\r
866 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
867 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
868 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
869 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
870 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
871 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
872 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
873 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
874 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
875 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
876 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
877 for(i=0; i<MAX_CHAT; i++)
\r
878 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
881 if(done) continue; // [HGM] chat: end patch
\r
882 TranslateMessage(&msg); /* Translates virtual key codes */
\r
883 DispatchMessage(&msg); /* Dispatches message to window */
\r
888 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
891 /*---------------------------------------------------------------------------*\
\r
893 * Initialization functions
\r
895 \*---------------------------------------------------------------------------*/
\r
899 { // update user logo if necessary
\r
900 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
902 if(appData.autoLogo) {
\r
903 curName = UserName();
\r
904 if(strcmp(curName, oldUserName)) {
\r
905 GetCurrentDirectory(MSG_SIZ, dir);
\r
906 SetCurrentDirectory(installDir);
\r
907 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
908 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
909 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
910 if(userLogo == NULL)
\r
911 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
912 SetCurrentDirectory(dir); /* return to prev directory */
\r
918 InitApplication(HINSTANCE hInstance)
\r
922 /* Fill in window class structure with parameters that describe the */
\r
925 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
926 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
927 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
928 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
929 wc.hInstance = hInstance; /* Owner of this class */
\r
930 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
931 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
932 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
933 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
934 wc.lpszClassName = szAppName; /* Name to register as */
\r
936 /* Register the window class and return success/failure code. */
\r
937 if (!RegisterClass(&wc)) return FALSE;
\r
939 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
940 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
942 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
943 wc.hInstance = hInstance;
\r
944 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
945 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
946 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
947 wc.lpszMenuName = NULL;
\r
948 wc.lpszClassName = szConsoleName;
\r
950 if (!RegisterClass(&wc)) return FALSE;
\r
955 /* Set by InitInstance, used by EnsureOnScreen */
\r
956 int screenHeight, screenWidth;
\r
959 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
961 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
962 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
963 if (*x > screenWidth - 32) *x = 0;
\r
964 if (*y > screenHeight - 32) *y = 0;
\r
965 if (*x < minX) *x = minX;
\r
966 if (*y < minY) *y = minY;
\r
970 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
972 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
973 GetCurrentDirectory(MSG_SIZ, dir);
\r
974 SetCurrentDirectory(installDir);
\r
975 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
976 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
978 if (cps->programLogo == NULL && appData.debugMode) {
\r
979 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
981 } else if(appData.autoLogo) {
\r
982 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
983 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
984 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
986 if(appData.directory[n] && appData.directory[n][0]) {
\r
987 SetCurrentDirectory(appData.directory[n]);
\r
988 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
991 SetCurrentDirectory(dir); /* return to prev directory */
\r
997 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
998 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1000 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1001 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1002 liteBackTextureMode = appData.liteBackTextureMode;
\r
1004 if (liteBackTexture == NULL && appData.debugMode) {
\r
1005 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1009 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1010 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1011 darkBackTextureMode = appData.darkBackTextureMode;
\r
1013 if (darkBackTexture == NULL && appData.debugMode) {
\r
1014 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1020 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1022 HWND hwnd; /* Main window handle. */
\r
1024 WINDOWPLACEMENT wp;
\r
1027 hInst = hInstance; /* Store instance handle in our global variable */
\r
1028 programName = szAppName;
\r
1030 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1031 *filepart = NULLCHAR;
\r
1033 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1035 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1036 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1037 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1038 /* xboard, and older WinBoards, controlled the move sound with the
\r
1039 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1040 always turn the option on (so that the backend will call us),
\r
1041 then let the user turn the sound off by setting it to silence if
\r
1042 desired. To accommodate old winboard.ini files saved by old
\r
1043 versions of WinBoard, we also turn off the sound if the option
\r
1044 was initially set to false. [HGM] taken out of InitAppData */
\r
1045 if (!appData.ringBellAfterMoves) {
\r
1046 sounds[(int)SoundMove].name = strdup("");
\r
1047 appData.ringBellAfterMoves = TRUE;
\r
1049 if (appData.debugMode) {
\r
1050 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1051 setbuf(debugFP, NULL);
\r
1054 LoadLanguageFile(appData.language);
\r
1058 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1059 // InitEngineUCI( installDir, &second );
\r
1061 /* Create a main window for this application instance. */
\r
1062 hwnd = CreateWindow(szAppName, szTitle,
\r
1063 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1064 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1065 NULL, NULL, hInstance, NULL);
\r
1068 /* If window could not be created, return "failure" */
\r
1073 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1074 LoadLogo(&first, 0, FALSE);
\r
1075 LoadLogo(&second, 1, appData.icsActive);
\r
1079 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1080 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1081 iconCurrent = iconWhite;
\r
1082 InitDrawingColors();
\r
1083 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1084 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1085 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1086 /* Compute window size for each board size, and use the largest
\r
1087 size that fits on this screen as the default. */
\r
1088 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1089 if (boardSize == (BoardSize)-1 &&
\r
1090 winH <= screenHeight
\r
1091 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1092 && winW <= screenWidth) {
\r
1093 boardSize = (BoardSize)ibs;
\r
1097 InitDrawingSizes(boardSize, 0);
\r
1099 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1101 /* [AS] Load textures if specified */
\r
1104 mysrandom( (unsigned) time(NULL) );
\r
1106 /* [AS] Restore layout */
\r
1107 if( wpMoveHistory.visible ) {
\r
1108 MoveHistoryPopUp();
\r
1111 if( wpEvalGraph.visible ) {
\r
1115 if( wpEngineOutput.visible ) {
\r
1116 EngineOutputPopUp();
\r
1119 /* Make the window visible; update its client area; and return "success" */
\r
1120 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1121 wp.length = sizeof(WINDOWPLACEMENT);
\r
1123 wp.showCmd = nCmdShow;
\r
1124 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1125 wp.rcNormalPosition.left = wpMain.x;
\r
1126 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1127 wp.rcNormalPosition.top = wpMain.y;
\r
1128 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1129 SetWindowPlacement(hwndMain, &wp);
\r
1131 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1133 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1134 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1136 if (hwndConsole) {
\r
1138 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1139 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1141 ShowWindow(hwndConsole, nCmdShow);
\r
1142 SetActiveWindow(hwndConsole);
\r
1144 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1145 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1154 HMENU hmenu = GetMenu(hwndMain);
\r
1156 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1157 MF_BYCOMMAND|((appData.icsActive &&
\r
1158 *appData.icsCommPort != NULLCHAR) ?
\r
1159 MF_ENABLED : MF_GRAYED));
\r
1160 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1161 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1162 MF_CHECKED : MF_UNCHECKED));
\r
1165 //---------------------------------------------------------------------------------------------------------
\r
1167 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1168 #define XBOARD FALSE
\r
1170 #define OPTCHAR "/"
\r
1171 #define SEPCHAR "="
\r
1175 // front-end part of option handling
\r
1178 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1180 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1181 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1184 lf->lfEscapement = 0;
\r
1185 lf->lfOrientation = 0;
\r
1186 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1187 lf->lfItalic = mfp->italic;
\r
1188 lf->lfUnderline = mfp->underline;
\r
1189 lf->lfStrikeOut = mfp->strikeout;
\r
1190 lf->lfCharSet = mfp->charset;
\r
1191 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1192 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1193 lf->lfQuality = DEFAULT_QUALITY;
\r
1194 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1195 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1199 CreateFontInMF(MyFont *mf)
\r
1201 LFfromMFP(&mf->lf, &mf->mfp);
\r
1202 if (mf->hf) DeleteObject(mf->hf);
\r
1203 mf->hf = CreateFontIndirect(&mf->lf);
\r
1206 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1208 colorVariable[] = {
\r
1209 &whitePieceColor,
\r
1210 &blackPieceColor,
\r
1211 &lightSquareColor,
\r
1212 &darkSquareColor,
\r
1213 &highlightSquareColor,
\r
1214 &premoveHighlightColor,
\r
1216 &consoleBackgroundColor,
\r
1217 &appData.fontForeColorWhite,
\r
1218 &appData.fontBackColorWhite,
\r
1219 &appData.fontForeColorBlack,
\r
1220 &appData.fontBackColorBlack,
\r
1221 &appData.evalHistColorWhite,
\r
1222 &appData.evalHistColorBlack,
\r
1223 &appData.highlightArrowColor,
\r
1226 /* Command line font name parser. NULL name means do nothing.
\r
1227 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1228 For backward compatibility, syntax without the colon is also
\r
1229 accepted, but font names with digits in them won't work in that case.
\r
1232 ParseFontName(char *name, MyFontParams *mfp)
\r
1235 if (name == NULL) return;
\r
1237 q = strchr(p, ':');
\r
1239 if (q - p >= sizeof(mfp->faceName))
\r
1240 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1241 memcpy(mfp->faceName, p, q - p);
\r
1242 mfp->faceName[q - p] = NULLCHAR;
\r
1245 q = mfp->faceName;
\r
1246 while (*p && !isdigit(*p)) {
\r
1248 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1249 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1251 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1254 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1255 mfp->pointSize = (float) atof(p);
\r
1256 mfp->bold = (strchr(p, 'b') != NULL);
\r
1257 mfp->italic = (strchr(p, 'i') != NULL);
\r
1258 mfp->underline = (strchr(p, 'u') != NULL);
\r
1259 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1260 mfp->charset = DEFAULT_CHARSET;
\r
1261 q = strchr(p, 'c');
\r
1263 mfp->charset = (BYTE) atoi(q+1);
\r
1267 ParseFont(char *name, int number)
\r
1268 { // wrapper to shield back-end from 'font'
\r
1269 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1274 { // in WB we have a 2D array of fonts; this initializes their description
\r
1276 /* Point font array elements to structures and
\r
1277 parse default font names */
\r
1278 for (i=0; i<NUM_FONTS; i++) {
\r
1279 for (j=0; j<NUM_SIZES; j++) {
\r
1280 font[j][i] = &fontRec[j][i];
\r
1281 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1288 { // here we create the actual fonts from the selected descriptions
\r
1290 for (i=0; i<NUM_FONTS; i++) {
\r
1291 for (j=0; j<NUM_SIZES; j++) {
\r
1292 CreateFontInMF(font[j][i]);
\r
1296 /* Color name parser.
\r
1297 X version accepts X color names, but this one
\r
1298 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1300 ParseColorName(char *name)
\r
1302 int red, green, blue, count;
\r
1303 char buf[MSG_SIZ];
\r
1305 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1307 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1308 &red, &green, &blue);
\r
1311 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1312 DisplayError(buf, 0);
\r
1313 return RGB(0, 0, 0);
\r
1315 return PALETTERGB(red, green, blue);
\r
1319 ParseColor(int n, char *name)
\r
1320 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1321 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1325 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1327 char *e = argValue;
\r
1331 if (*e == 'b') eff |= CFE_BOLD;
\r
1332 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1333 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1334 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1335 else if (*e == '#' || isdigit(*e)) break;
\r
1339 *color = ParseColorName(e);
\r
1343 ParseTextAttribs(ColorClass cc, char *s)
\r
1344 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1345 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1346 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1350 ParseBoardSize(void *addr, char *name)
\r
1351 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1352 BoardSize bs = SizeTiny;
\r
1353 while (sizeInfo[bs].name != NULL) {
\r
1354 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1355 *(BoardSize *)addr = bs;
\r
1360 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1365 { // [HGM] import name from appData first
\r
1368 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1369 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1370 textAttribs[cc].sound.data = NULL;
\r
1371 MyLoadSound(&textAttribs[cc].sound);
\r
1373 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1374 textAttribs[cc].sound.name = strdup("");
\r
1375 textAttribs[cc].sound.data = NULL;
\r
1377 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1378 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1379 sounds[sc].data = NULL;
\r
1380 MyLoadSound(&sounds[sc]);
\r
1385 SetCommPortDefaults()
\r
1387 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1388 dcb.DCBlength = sizeof(DCB);
\r
1389 dcb.BaudRate = 9600;
\r
1390 dcb.fBinary = TRUE;
\r
1391 dcb.fParity = FALSE;
\r
1392 dcb.fOutxCtsFlow = FALSE;
\r
1393 dcb.fOutxDsrFlow = FALSE;
\r
1394 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1395 dcb.fDsrSensitivity = FALSE;
\r
1396 dcb.fTXContinueOnXoff = TRUE;
\r
1397 dcb.fOutX = FALSE;
\r
1399 dcb.fNull = FALSE;
\r
1400 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1401 dcb.fAbortOnError = FALSE;
\r
1403 dcb.Parity = SPACEPARITY;
\r
1404 dcb.StopBits = ONESTOPBIT;
\r
1407 // [HGM] args: these three cases taken out to stay in front-end
\r
1409 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1410 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1411 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1412 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1414 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1415 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1416 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1417 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1418 ad->argName, mfp->faceName, mfp->pointSize,
\r
1419 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1420 mfp->bold ? "b" : "",
\r
1421 mfp->italic ? "i" : "",
\r
1422 mfp->underline ? "u" : "",
\r
1423 mfp->strikeout ? "s" : "",
\r
1424 (int)mfp->charset);
\r
1430 { // [HGM] copy the names from the internal WB variables to appData
\r
1433 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1434 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1435 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1436 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1440 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1441 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1442 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1443 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1444 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1445 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1446 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1447 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1448 (ta->effects) ? " " : "",
\r
1449 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1453 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1454 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1455 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1456 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1457 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1461 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1462 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1463 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1467 ParseCommPortSettings(char *s)
\r
1468 { // wrapper to keep dcb from back-end
\r
1469 ParseCommSettings(s, &dcb);
\r
1474 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1475 GetActualPlacement(hwndMain, &wpMain);
\r
1476 GetActualPlacement(hwndConsole, &wpConsole);
\r
1477 GetActualPlacement(commentDialog, &wpComment);
\r
1478 GetActualPlacement(editTagsDialog, &wpTags);
\r
1479 GetActualPlacement(gameListDialog, &wpGameList);
\r
1480 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1481 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1482 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1486 PrintCommPortSettings(FILE *f, char *name)
\r
1487 { // wrapper to shield back-end from DCB
\r
1488 PrintCommSettings(f, name, &dcb);
\r
1492 MySearchPath(char *installDir, char *name, char *fullname)
\r
1494 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1495 if(name[0]== '%') {
\r
1496 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1497 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1498 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1499 *strchr(buf, '%') = 0;
\r
1500 strcat(fullname, getenv(buf));
\r
1501 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1503 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1504 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1505 return (int) strlen(fullname);
\r
1507 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1511 MyGetFullPathName(char *name, char *fullname)
\r
1514 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1519 { // [HGM] args: allows testing if main window is realized from back-end
\r
1520 return hwndMain != NULL;
\r
1524 PopUpStartupDialog()
\r
1528 LoadLanguageFile(appData.language);
\r
1529 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1530 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1531 FreeProcInstance(lpProc);
\r
1534 /*---------------------------------------------------------------------------*\
\r
1536 * GDI board drawing routines
\r
1538 \*---------------------------------------------------------------------------*/
\r
1540 /* [AS] Draw square using background texture */
\r
1541 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1546 return; /* Should never happen! */
\r
1549 SetGraphicsMode( dst, GM_ADVANCED );
\r
1556 /* X reflection */
\r
1561 x.eDx = (FLOAT) dw + dx - 1;
\r
1564 SetWorldTransform( dst, &x );
\r
1567 /* Y reflection */
\r
1573 x.eDy = (FLOAT) dh + dy - 1;
\r
1575 SetWorldTransform( dst, &x );
\r
1583 x.eDx = (FLOAT) dx;
\r
1584 x.eDy = (FLOAT) dy;
\r
1587 SetWorldTransform( dst, &x );
\r
1591 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1599 SetWorldTransform( dst, &x );
\r
1601 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1604 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1606 PM_WP = (int) WhitePawn,
\r
1607 PM_WN = (int) WhiteKnight,
\r
1608 PM_WB = (int) WhiteBishop,
\r
1609 PM_WR = (int) WhiteRook,
\r
1610 PM_WQ = (int) WhiteQueen,
\r
1611 PM_WF = (int) WhiteFerz,
\r
1612 PM_WW = (int) WhiteWazir,
\r
1613 PM_WE = (int) WhiteAlfil,
\r
1614 PM_WM = (int) WhiteMan,
\r
1615 PM_WO = (int) WhiteCannon,
\r
1616 PM_WU = (int) WhiteUnicorn,
\r
1617 PM_WH = (int) WhiteNightrider,
\r
1618 PM_WA = (int) WhiteAngel,
\r
1619 PM_WC = (int) WhiteMarshall,
\r
1620 PM_WAB = (int) WhiteCardinal,
\r
1621 PM_WD = (int) WhiteDragon,
\r
1622 PM_WL = (int) WhiteLance,
\r
1623 PM_WS = (int) WhiteCobra,
\r
1624 PM_WV = (int) WhiteFalcon,
\r
1625 PM_WSG = (int) WhiteSilver,
\r
1626 PM_WG = (int) WhiteGrasshopper,
\r
1627 PM_WK = (int) WhiteKing,
\r
1628 PM_BP = (int) BlackPawn,
\r
1629 PM_BN = (int) BlackKnight,
\r
1630 PM_BB = (int) BlackBishop,
\r
1631 PM_BR = (int) BlackRook,
\r
1632 PM_BQ = (int) BlackQueen,
\r
1633 PM_BF = (int) BlackFerz,
\r
1634 PM_BW = (int) BlackWazir,
\r
1635 PM_BE = (int) BlackAlfil,
\r
1636 PM_BM = (int) BlackMan,
\r
1637 PM_BO = (int) BlackCannon,
\r
1638 PM_BU = (int) BlackUnicorn,
\r
1639 PM_BH = (int) BlackNightrider,
\r
1640 PM_BA = (int) BlackAngel,
\r
1641 PM_BC = (int) BlackMarshall,
\r
1642 PM_BG = (int) BlackGrasshopper,
\r
1643 PM_BAB = (int) BlackCardinal,
\r
1644 PM_BD = (int) BlackDragon,
\r
1645 PM_BL = (int) BlackLance,
\r
1646 PM_BS = (int) BlackCobra,
\r
1647 PM_BV = (int) BlackFalcon,
\r
1648 PM_BSG = (int) BlackSilver,
\r
1649 PM_BK = (int) BlackKing
\r
1652 static HFONT hPieceFont = NULL;
\r
1653 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1654 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1655 static int fontBitmapSquareSize = 0;
\r
1656 static char pieceToFontChar[(int) EmptySquare] =
\r
1657 { 'p', 'n', 'b', 'r', 'q',
\r
1658 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1659 'k', 'o', 'm', 'v', 't', 'w',
\r
1660 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1663 extern BOOL SetCharTable( char *table, const char * map );
\r
1664 /* [HGM] moved to backend.c */
\r
1666 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1669 BYTE r1 = GetRValue( color );
\r
1670 BYTE g1 = GetGValue( color );
\r
1671 BYTE b1 = GetBValue( color );
\r
1677 /* Create a uniform background first */
\r
1678 hbrush = CreateSolidBrush( color );
\r
1679 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1680 FillRect( hdc, &rc, hbrush );
\r
1681 DeleteObject( hbrush );
\r
1684 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1685 int steps = squareSize / 2;
\r
1688 for( i=0; i<steps; i++ ) {
\r
1689 BYTE r = r1 - (r1-r2) * i / steps;
\r
1690 BYTE g = g1 - (g1-g2) * i / steps;
\r
1691 BYTE b = b1 - (b1-b2) * i / steps;
\r
1693 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1694 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1695 FillRect( hdc, &rc, hbrush );
\r
1696 DeleteObject(hbrush);
\r
1699 else if( mode == 2 ) {
\r
1700 /* Diagonal gradient, good more or less for every piece */
\r
1701 POINT triangle[3];
\r
1702 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1703 HBRUSH hbrush_old;
\r
1704 int steps = squareSize;
\r
1707 triangle[0].x = squareSize - steps;
\r
1708 triangle[0].y = squareSize;
\r
1709 triangle[1].x = squareSize;
\r
1710 triangle[1].y = squareSize;
\r
1711 triangle[2].x = squareSize;
\r
1712 triangle[2].y = squareSize - steps;
\r
1714 for( i=0; i<steps; i++ ) {
\r
1715 BYTE r = r1 - (r1-r2) * i / steps;
\r
1716 BYTE g = g1 - (g1-g2) * i / steps;
\r
1717 BYTE b = b1 - (b1-b2) * i / steps;
\r
1719 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1720 hbrush_old = SelectObject( hdc, hbrush );
\r
1721 Polygon( hdc, triangle, 3 );
\r
1722 SelectObject( hdc, hbrush_old );
\r
1723 DeleteObject(hbrush);
\r
1728 SelectObject( hdc, hpen );
\r
1733 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1734 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1735 piece: follow the steps as explained below.
\r
1737 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1741 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1745 int backColor = whitePieceColor;
\r
1746 int foreColor = blackPieceColor;
\r
1748 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1749 backColor = appData.fontBackColorWhite;
\r
1750 foreColor = appData.fontForeColorWhite;
\r
1752 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1753 backColor = appData.fontBackColorBlack;
\r
1754 foreColor = appData.fontForeColorBlack;
\r
1758 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1760 hbm_old = SelectObject( hdc, hbm );
\r
1764 rc.right = squareSize;
\r
1765 rc.bottom = squareSize;
\r
1767 /* Step 1: background is now black */
\r
1768 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1770 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1772 pt.x = (squareSize - sz.cx) / 2;
\r
1773 pt.y = (squareSize - sz.cy) / 2;
\r
1775 SetBkMode( hdc, TRANSPARENT );
\r
1776 SetTextColor( hdc, chroma );
\r
1777 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1778 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1780 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1781 /* Step 3: the area outside the piece is filled with white */
\r
1782 // FloodFill( hdc, 0, 0, chroma );
\r
1783 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1784 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1785 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1786 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1787 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1789 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1790 but if the start point is not inside the piece we're lost!
\r
1791 There should be a better way to do this... if we could create a region or path
\r
1792 from the fill operation we would be fine for example.
\r
1794 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1795 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1797 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1798 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1799 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1801 SelectObject( dc2, bm2 );
\r
1802 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1803 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1804 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1805 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1806 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1809 DeleteObject( bm2 );
\r
1812 SetTextColor( hdc, 0 );
\r
1814 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1815 draw the piece again in black for safety.
\r
1817 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1819 SelectObject( hdc, hbm_old );
\r
1821 if( hPieceMask[index] != NULL ) {
\r
1822 DeleteObject( hPieceMask[index] );
\r
1825 hPieceMask[index] = hbm;
\r
1828 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1830 SelectObject( hdc, hbm );
\r
1833 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1834 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1835 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1837 SelectObject( dc1, hPieceMask[index] );
\r
1838 SelectObject( dc2, bm2 );
\r
1839 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1840 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1843 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1844 the piece background and deletes (makes transparent) the rest.
\r
1845 Thanks to that mask, we are free to paint the background with the greates
\r
1846 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1847 We use this, to make gradients and give the pieces a "roundish" look.
\r
1849 SetPieceBackground( hdc, backColor, 2 );
\r
1850 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1854 DeleteObject( bm2 );
\r
1857 SetTextColor( hdc, foreColor );
\r
1858 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1860 SelectObject( hdc, hbm_old );
\r
1862 if( hPieceFace[index] != NULL ) {
\r
1863 DeleteObject( hPieceFace[index] );
\r
1866 hPieceFace[index] = hbm;
\r
1869 static int TranslatePieceToFontPiece( int piece )
\r
1899 case BlackMarshall:
\r
1903 case BlackNightrider:
\r
1909 case BlackUnicorn:
\r
1913 case BlackGrasshopper:
\r
1925 case BlackCardinal:
\r
1932 case WhiteMarshall:
\r
1936 case WhiteNightrider:
\r
1942 case WhiteUnicorn:
\r
1946 case WhiteGrasshopper:
\r
1958 case WhiteCardinal:
\r
1967 void CreatePiecesFromFont()
\r
1970 HDC hdc_window = NULL;
\r
1976 if( fontBitmapSquareSize < 0 ) {
\r
1977 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1981 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
1982 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1983 fontBitmapSquareSize = -1;
\r
1987 if( fontBitmapSquareSize != squareSize ) {
\r
1988 hdc_window = GetDC( hwndMain );
\r
1989 hdc = CreateCompatibleDC( hdc_window );
\r
1991 if( hPieceFont != NULL ) {
\r
1992 DeleteObject( hPieceFont );
\r
1995 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1996 hPieceMask[i] = NULL;
\r
1997 hPieceFace[i] = NULL;
\r
2003 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2004 fontHeight = appData.fontPieceSize;
\r
2007 fontHeight = (fontHeight * squareSize) / 100;
\r
2009 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2011 lf.lfEscapement = 0;
\r
2012 lf.lfOrientation = 0;
\r
2013 lf.lfWeight = FW_NORMAL;
\r
2015 lf.lfUnderline = 0;
\r
2016 lf.lfStrikeOut = 0;
\r
2017 lf.lfCharSet = DEFAULT_CHARSET;
\r
2018 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2019 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2020 lf.lfQuality = PROOF_QUALITY;
\r
2021 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2022 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2023 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2025 hPieceFont = CreateFontIndirect( &lf );
\r
2027 if( hPieceFont == NULL ) {
\r
2028 fontBitmapSquareSize = -2;
\r
2031 /* Setup font-to-piece character table */
\r
2032 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2033 /* No (or wrong) global settings, try to detect the font */
\r
2034 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2036 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2038 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2039 /* DiagramTT* family */
\r
2040 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2042 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2043 /* Fairy symbols */
\r
2044 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2046 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2047 /* Good Companion (Some characters get warped as literal :-( */
\r
2048 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2049 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2050 SetCharTable(pieceToFontChar, s);
\r
2053 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2054 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2058 /* Create bitmaps */
\r
2059 hfont_old = SelectObject( hdc, hPieceFont );
\r
2060 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2061 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2062 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2064 SelectObject( hdc, hfont_old );
\r
2066 fontBitmapSquareSize = squareSize;
\r
2070 if( hdc != NULL ) {
\r
2074 if( hdc_window != NULL ) {
\r
2075 ReleaseDC( hwndMain, hdc_window );
\r
2080 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2084 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2085 if (gameInfo.event &&
\r
2086 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2087 strcmp(name, "k80s") == 0) {
\r
2088 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2090 return LoadBitmap(hinst, name);
\r
2094 /* Insert a color into the program's logical palette
\r
2095 structure. This code assumes the given color is
\r
2096 the result of the RGB or PALETTERGB macro, and it
\r
2097 knows how those macros work (which is documented).
\r
2100 InsertInPalette(COLORREF color)
\r
2102 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2104 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2105 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2106 pLogPal->palNumEntries--;
\r
2110 pe->peFlags = (char) 0;
\r
2111 pe->peRed = (char) (0xFF & color);
\r
2112 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2113 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2119 InitDrawingColors()
\r
2121 if (pLogPal == NULL) {
\r
2122 /* Allocate enough memory for a logical palette with
\r
2123 * PALETTESIZE entries and set the size and version fields
\r
2124 * of the logical palette structure.
\r
2126 pLogPal = (NPLOGPALETTE)
\r
2127 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2128 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2129 pLogPal->palVersion = 0x300;
\r
2131 pLogPal->palNumEntries = 0;
\r
2133 InsertInPalette(lightSquareColor);
\r
2134 InsertInPalette(darkSquareColor);
\r
2135 InsertInPalette(whitePieceColor);
\r
2136 InsertInPalette(blackPieceColor);
\r
2137 InsertInPalette(highlightSquareColor);
\r
2138 InsertInPalette(premoveHighlightColor);
\r
2140 /* create a logical color palette according the information
\r
2141 * in the LOGPALETTE structure.
\r
2143 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2145 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2146 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2147 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2148 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2149 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2150 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2151 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2152 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2153 /* [AS] Force rendering of the font-based pieces */
\r
2154 if( fontBitmapSquareSize > 0 ) {
\r
2155 fontBitmapSquareSize = 0;
\r
2161 BoardWidth(int boardSize, int n)
\r
2162 { /* [HGM] argument n added to allow different width and height */
\r
2163 int lineGap = sizeInfo[boardSize].lineGap;
\r
2165 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2166 lineGap = appData.overrideLineGap;
\r
2169 return (n + 1) * lineGap +
\r
2170 n * sizeInfo[boardSize].squareSize;
\r
2173 /* Respond to board resize by dragging edge */
\r
2175 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2177 BoardSize newSize = NUM_SIZES - 1;
\r
2178 static int recurse = 0;
\r
2179 if (IsIconic(hwndMain)) return;
\r
2180 if (recurse > 0) return;
\r
2182 while (newSize > 0) {
\r
2183 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2184 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2185 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2188 boardSize = newSize;
\r
2189 InitDrawingSizes(boardSize, flags);
\r
2194 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2197 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2199 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2200 ChessSquare piece;
\r
2201 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2203 SIZE clockSize, messageSize;
\r
2205 char buf[MSG_SIZ];
\r
2207 HMENU hmenu = GetMenu(hwndMain);
\r
2208 RECT crect, wrect, oldRect;
\r
2210 LOGBRUSH logbrush;
\r
2212 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2213 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2215 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2216 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2218 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2219 oldRect.top = wpMain.y;
\r
2220 oldRect.right = wpMain.x + wpMain.width;
\r
2221 oldRect.bottom = wpMain.y + wpMain.height;
\r
2223 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2224 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2225 squareSize = sizeInfo[boardSize].squareSize;
\r
2226 lineGap = sizeInfo[boardSize].lineGap;
\r
2227 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2229 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2230 lineGap = appData.overrideLineGap;
\r
2233 if (tinyLayout != oldTinyLayout) {
\r
2234 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2236 style &= ~WS_SYSMENU;
\r
2237 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2238 "&Minimize\tCtrl+F4");
\r
2240 style |= WS_SYSMENU;
\r
2241 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2243 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2245 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2246 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2247 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2249 DrawMenuBar(hwndMain);
\r
2252 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2253 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2255 /* Get text area sizes */
\r
2256 hdc = GetDC(hwndMain);
\r
2257 if (appData.clockMode) {
\r
2258 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2260 snprintf(buf, MSG_SIZ, _("White"));
\r
2262 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2263 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2264 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2265 str = _("We only care about the height here");
\r
2266 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2267 SelectObject(hdc, oldFont);
\r
2268 ReleaseDC(hwndMain, hdc);
\r
2270 /* Compute where everything goes */
\r
2271 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2272 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2273 logoHeight = 2*clockSize.cy;
\r
2274 leftLogoRect.left = OUTER_MARGIN;
\r
2275 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2276 leftLogoRect.top = OUTER_MARGIN;
\r
2277 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2279 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2280 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2281 rightLogoRect.top = OUTER_MARGIN;
\r
2282 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2285 whiteRect.left = leftLogoRect.right;
\r
2286 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2287 whiteRect.top = OUTER_MARGIN;
\r
2288 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2290 blackRect.right = rightLogoRect.left;
\r
2291 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2292 blackRect.top = whiteRect.top;
\r
2293 blackRect.bottom = whiteRect.bottom;
\r
2295 whiteRect.left = OUTER_MARGIN;
\r
2296 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2297 whiteRect.top = OUTER_MARGIN;
\r
2298 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2300 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2301 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2302 blackRect.top = whiteRect.top;
\r
2303 blackRect.bottom = whiteRect.bottom;
\r
2305 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2308 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2309 if (appData.showButtonBar) {
\r
2310 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2311 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2313 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2315 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2316 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2318 boardRect.left = OUTER_MARGIN;
\r
2319 boardRect.right = boardRect.left + boardWidth;
\r
2320 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2321 boardRect.bottom = boardRect.top + boardHeight;
\r
2323 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2324 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2325 oldBoardSize = boardSize;
\r
2326 oldTinyLayout = tinyLayout;
\r
2327 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2328 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2329 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2330 winW *= 1 + twoBoards;
\r
2331 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2332 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2333 wpMain.height = winH; // without disturbing window attachments
\r
2334 GetWindowRect(hwndMain, &wrect);
\r
2335 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2336 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2338 // [HGM] placement: let attached windows follow size change.
\r
2339 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2340 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2341 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2342 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2343 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2345 /* compensate if menu bar wrapped */
\r
2346 GetClientRect(hwndMain, &crect);
\r
2347 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2348 wpMain.height += offby;
\r
2350 case WMSZ_TOPLEFT:
\r
2351 SetWindowPos(hwndMain, NULL,
\r
2352 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2353 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2356 case WMSZ_TOPRIGHT:
\r
2358 SetWindowPos(hwndMain, NULL,
\r
2359 wrect.left, wrect.bottom - wpMain.height,
\r
2360 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2363 case WMSZ_BOTTOMLEFT:
\r
2365 SetWindowPos(hwndMain, NULL,
\r
2366 wrect.right - wpMain.width, wrect.top,
\r
2367 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2370 case WMSZ_BOTTOMRIGHT:
\r
2374 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2375 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2380 for (i = 0; i < N_BUTTONS; i++) {
\r
2381 if (buttonDesc[i].hwnd != NULL) {
\r
2382 DestroyWindow(buttonDesc[i].hwnd);
\r
2383 buttonDesc[i].hwnd = NULL;
\r
2385 if (appData.showButtonBar) {
\r
2386 buttonDesc[i].hwnd =
\r
2387 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2388 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2389 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2390 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2391 (HMENU) buttonDesc[i].id,
\r
2392 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2394 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2395 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2396 MAKELPARAM(FALSE, 0));
\r
2398 if (buttonDesc[i].id == IDM_Pause)
\r
2399 hwndPause = buttonDesc[i].hwnd;
\r
2400 buttonDesc[i].wndproc = (WNDPROC)
\r
2401 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2404 if (gridPen != NULL) DeleteObject(gridPen);
\r
2405 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2406 if (premovePen != NULL) DeleteObject(premovePen);
\r
2407 if (lineGap != 0) {
\r
2408 logbrush.lbStyle = BS_SOLID;
\r
2409 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2411 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2412 lineGap, &logbrush, 0, NULL);
\r
2413 logbrush.lbColor = highlightSquareColor;
\r
2415 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2416 lineGap, &logbrush, 0, NULL);
\r
2418 logbrush.lbColor = premoveHighlightColor;
\r
2420 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2421 lineGap, &logbrush, 0, NULL);
\r
2423 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2424 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2425 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2426 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2427 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2428 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2429 BOARD_WIDTH * (squareSize + lineGap);
\r
2430 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2432 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2433 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2434 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2435 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2436 lineGap / 2 + (i * (squareSize + lineGap));
\r
2437 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2438 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2439 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2443 /* [HGM] Licensing requirement */
\r
2445 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2448 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2450 GothicPopUp( "", VariantNormal);
\r
2453 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2455 /* Load piece bitmaps for this board size */
\r
2456 for (i=0; i<=2; i++) {
\r
2457 for (piece = WhitePawn;
\r
2458 (int) piece < (int) BlackPawn;
\r
2459 piece = (ChessSquare) ((int) piece + 1)) {
\r
2460 if (pieceBitmap[i][piece] != NULL)
\r
2461 DeleteObject(pieceBitmap[i][piece]);
\r
2465 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2466 // Orthodox Chess pieces
\r
2467 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2468 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2469 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2470 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2471 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2472 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2473 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2474 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2475 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2476 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2477 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2478 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2479 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2480 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2481 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2482 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2483 // in Shogi, Hijack the unused Queen for Lance
\r
2484 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2485 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2486 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2488 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2489 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2490 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2493 if(squareSize <= 72 && squareSize >= 33) {
\r
2494 /* A & C are available in most sizes now */
\r
2495 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2496 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2497 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2498 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2499 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2500 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2501 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2502 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2503 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2504 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2505 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2506 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2507 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2508 } else { // Smirf-like
\r
2509 if(gameInfo.variant == VariantSChess) {
\r
2510 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2511 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2512 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2514 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2515 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2516 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2519 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2520 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2521 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2522 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2523 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2524 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2525 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2526 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2527 } else { // WinBoard standard
\r
2528 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2529 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2530 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2535 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2536 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2537 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2538 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2539 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2542 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2545 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2548 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2560 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2561 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2562 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2563 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2567 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2568 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2569 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2570 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2571 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2574 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2575 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2576 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2577 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2578 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2579 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2581 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2582 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2583 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2584 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2585 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2586 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2587 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2588 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2589 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2590 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2591 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2592 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2595 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2596 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2597 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2598 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2599 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2600 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2601 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2602 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2603 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2604 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2605 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2606 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2607 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2608 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2609 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2613 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2614 /* special Shogi support in this size */
\r
2615 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2616 for (piece = WhitePawn;
\r
2617 (int) piece < (int) BlackPawn;
\r
2618 piece = (ChessSquare) ((int) piece + 1)) {
\r
2619 if (pieceBitmap[i][piece] != NULL)
\r
2620 DeleteObject(pieceBitmap[i][piece]);
\r
2623 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2624 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2625 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2626 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2627 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2628 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2629 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2630 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2631 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2632 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2633 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2634 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2635 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2636 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2637 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2638 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2639 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2640 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2641 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2642 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2643 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2644 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2645 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2646 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2647 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2648 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2649 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2650 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2651 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2652 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2653 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2654 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2657 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2658 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2659 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2660 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2661 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2662 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2663 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2664 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2670 PieceBitmap(ChessSquare p, int kind)
\r
2672 if ((int) p >= (int) BlackPawn)
\r
2673 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2675 return pieceBitmap[kind][(int) p];
\r
2678 /***************************************************************/
\r
2680 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2681 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2683 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2684 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2688 SquareToPos(int row, int column, int * x, int * y)
\r
2691 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2692 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2694 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2695 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2700 DrawCoordsOnDC(HDC hdc)
\r
2702 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2703 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2704 char str[2] = { NULLCHAR, NULLCHAR };
\r
2705 int oldMode, oldAlign, x, y, start, i;
\r
2709 if (!appData.showCoords)
\r
2712 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2714 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2715 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2716 oldAlign = GetTextAlign(hdc);
\r
2717 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2719 y = boardRect.top + lineGap;
\r
2720 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2722 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2723 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2724 str[0] = files[start + i];
\r
2725 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2726 y += squareSize + lineGap;
\r
2729 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2731 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2732 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2733 str[0] = ranks[start + i];
\r
2734 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2735 x += squareSize + lineGap;
\r
2738 SelectObject(hdc, oldBrush);
\r
2739 SetBkMode(hdc, oldMode);
\r
2740 SetTextAlign(hdc, oldAlign);
\r
2741 SelectObject(hdc, oldFont);
\r
2745 DrawGridOnDC(HDC hdc)
\r
2749 if (lineGap != 0) {
\r
2750 oldPen = SelectObject(hdc, gridPen);
\r
2751 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2752 SelectObject(hdc, oldPen);
\r
2756 #define HIGHLIGHT_PEN 0
\r
2757 #define PREMOVE_PEN 1
\r
2760 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2763 HPEN oldPen, hPen;
\r
2764 if (lineGap == 0) return;
\r
2766 x1 = boardRect.left +
\r
2767 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2768 y1 = boardRect.top +
\r
2769 lineGap/2 + y * (squareSize + lineGap);
\r
2771 x1 = boardRect.left +
\r
2772 lineGap/2 + x * (squareSize + lineGap);
\r
2773 y1 = boardRect.top +
\r
2774 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2776 hPen = pen ? premovePen : highlightPen;
\r
2777 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2778 MoveToEx(hdc, x1, y1, NULL);
\r
2779 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2780 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2781 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2782 LineTo(hdc, x1, y1);
\r
2783 SelectObject(hdc, oldPen);
\r
2787 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2790 for (i=0; i<2; i++) {
\r
2791 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2792 DrawHighlightOnDC(hdc, TRUE,
\r
2793 h->sq[i].x, h->sq[i].y,
\r
2798 /* Note: sqcolor is used only in monoMode */
\r
2799 /* Note that this code is largely duplicated in woptions.c,
\r
2800 function DrawSampleSquare, so that needs to be updated too */
\r
2802 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2804 HBITMAP oldBitmap;
\r
2808 if (appData.blindfold) return;
\r
2810 /* [AS] Use font-based pieces if needed */
\r
2811 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2812 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2813 CreatePiecesFromFont();
\r
2815 if( fontBitmapSquareSize == squareSize ) {
\r
2816 int index = TranslatePieceToFontPiece(piece);
\r
2818 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2820 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2821 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2825 squareSize, squareSize,
\r
2830 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2832 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2833 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2837 squareSize, squareSize,
\r
2846 if (appData.monoMode) {
\r
2847 SelectObject(tmphdc, PieceBitmap(piece,
\r
2848 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2849 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2850 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2852 tmpSize = squareSize;
\r
2854 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2855 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2856 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2857 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2858 x += (squareSize - minorSize)>>1;
\r
2859 y += squareSize - minorSize - 2;
\r
2860 tmpSize = minorSize;
\r
2862 if (color || appData.allWhite ) {
\r
2863 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2865 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2866 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2867 if(appData.upsideDown && color==flipView)
\r
2868 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2870 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2871 /* Use black for outline of white pieces */
\r
2872 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2873 if(appData.upsideDown && color==flipView)
\r
2874 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2876 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2878 /* Use square color for details of black pieces */
\r
2879 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2880 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2881 if(appData.upsideDown && !flipView)
\r
2882 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2884 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2886 SelectObject(hdc, oldBrush);
\r
2887 SelectObject(tmphdc, oldBitmap);
\r
2891 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2892 int GetBackTextureMode( int algo )
\r
2894 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2898 case BACK_TEXTURE_MODE_PLAIN:
\r
2899 result = 1; /* Always use identity map */
\r
2901 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2902 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2910 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2911 to handle redraws cleanly (as random numbers would always be different).
\r
2913 VOID RebuildTextureSquareInfo()
\r
2923 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2925 if( liteBackTexture != NULL ) {
\r
2926 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2927 lite_w = bi.bmWidth;
\r
2928 lite_h = bi.bmHeight;
\r
2932 if( darkBackTexture != NULL ) {
\r
2933 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2934 dark_w = bi.bmWidth;
\r
2935 dark_h = bi.bmHeight;
\r
2939 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2940 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2941 if( (col + row) & 1 ) {
\r
2943 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2944 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2945 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2947 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2948 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2949 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2951 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2952 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2957 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2958 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2959 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2961 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2962 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2963 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2965 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2966 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2973 /* [AS] Arrow highlighting support */
\r
2975 static double A_WIDTH = 5; /* Width of arrow body */
\r
2977 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2978 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2980 static double Sqr( double x )
\r
2985 static int Round( double x )
\r
2987 return (int) (x + 0.5);
\r
2990 /* Draw an arrow between two points using current settings */
\r
2991 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2994 double dx, dy, j, k, x, y;
\r
2996 if( d_x == s_x ) {
\r
2997 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2999 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3002 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3003 arrow[1].y = d_y - h;
\r
3005 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3006 arrow[2].y = d_y - h;
\r
3011 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3012 arrow[5].y = d_y - h;
\r
3014 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3015 arrow[4].y = d_y - h;
\r
3017 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3020 else if( d_y == s_y ) {
\r
3021 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3024 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3026 arrow[1].x = d_x - w;
\r
3027 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3029 arrow[2].x = d_x - w;
\r
3030 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3035 arrow[5].x = d_x - w;
\r
3036 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3038 arrow[4].x = d_x - w;
\r
3039 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3042 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3045 /* [AS] Needed a lot of paper for this! :-) */
\r
3046 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3047 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3049 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3051 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3056 arrow[0].x = Round(x - j);
\r
3057 arrow[0].y = Round(y + j*dx);
\r
3059 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3060 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3063 x = (double) d_x - k;
\r
3064 y = (double) d_y - k*dy;
\r
3067 x = (double) d_x + k;
\r
3068 y = (double) d_y + k*dy;
\r
3071 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3073 arrow[6].x = Round(x - j);
\r
3074 arrow[6].y = Round(y + j*dx);
\r
3076 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3077 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3079 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3080 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3085 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3086 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3089 Polygon( hdc, arrow, 7 );
\r
3092 /* [AS] Draw an arrow between two squares */
\r
3093 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3095 int s_x, s_y, d_x, d_y;
\r
3102 if( s_col == d_col && s_row == d_row ) {
\r
3106 /* Get source and destination points */
\r
3107 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3108 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3111 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3113 else if( d_y < s_y ) {
\r
3114 d_y += squareSize / 2 + squareSize / 4;
\r
3117 d_y += squareSize / 2;
\r
3121 d_x += squareSize / 2 - squareSize / 4;
\r
3123 else if( d_x < s_x ) {
\r
3124 d_x += squareSize / 2 + squareSize / 4;
\r
3127 d_x += squareSize / 2;
\r
3130 s_x += squareSize / 2;
\r
3131 s_y += squareSize / 2;
\r
3133 /* Adjust width */
\r
3134 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3137 stLB.lbStyle = BS_SOLID;
\r
3138 stLB.lbColor = appData.highlightArrowColor;
\r
3141 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3142 holdpen = SelectObject( hdc, hpen );
\r
3143 hbrush = CreateBrushIndirect( &stLB );
\r
3144 holdbrush = SelectObject( hdc, hbrush );
\r
3146 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3148 SelectObject( hdc, holdpen );
\r
3149 SelectObject( hdc, holdbrush );
\r
3150 DeleteObject( hpen );
\r
3151 DeleteObject( hbrush );
\r
3154 BOOL HasHighlightInfo()
\r
3156 BOOL result = FALSE;
\r
3158 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3159 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3167 BOOL IsDrawArrowEnabled()
\r
3169 BOOL result = FALSE;
\r
3171 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3178 VOID DrawArrowHighlight( HDC hdc )
\r
3180 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3181 DrawArrowBetweenSquares( hdc,
\r
3182 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3183 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3187 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3189 HRGN result = NULL;
\r
3191 if( HasHighlightInfo() ) {
\r
3192 int x1, y1, x2, y2;
\r
3193 int sx, sy, dx, dy;
\r
3195 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3196 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3198 sx = MIN( x1, x2 );
\r